整理自小甲鱼鱼C论坛
组合 所谓”组合”,可以理解为相对于继承来讲的一个概念。有些时候,利用多重继承本身除了存在风险的同时,在有些时候,如果类本身并不存在明显的相互继承关系,强行继承会很别扭。比如定义了两个类,小鸟和老鹰后,我们想定义一个新的类,天空,天空中包含小鸟和老鹰,奈何天空和小鸟和老鹰本身并不存在继承关系,这时就适合用组合来解决这个问题。
当执行组合时,我们将需要的类放进目的类中进行实例化 ,就可以实现组合,例如代码:
class Bird (): def __init__ (self, x ): self .num = x class Eagle (): def __init__ (self, x ): self .num = x class Sky (): def __init__ (self, x, y ): self .bird = Bird(x) self .eagle = Eagle(y) def print_num (self ): print ("天空中共有小鸟 %d只,老鹰 %d只" % (self .bird.num, self .eagle.num))
将几个不是很有继承关系的(横向关系)的几个类放在一起,我们称之为组合 。如果几个类之间的纵向关系,我们就可以使用继承。
除了组合以外,还有另外一种很流行的编程模式,叫做Mixin。
Mixin Mixin编程是一种开发模式,是一种将多个类中的功能单元的进行组合的利用的方式。通常Mixin不作为任何类的基类,也不关心与什么类一起使用,而是在运行时动态的同其他零散的类一起组合使用 。
其具有以下有点:
可以在不修改任何源代码的情况下,对已有类进行扩展;
可以保证组件的划分;
可以根据需要,使用已有的功能进行组合,来实现“新”类;
很好的避免了类继承的局限性,因为新的业务需要,可能就意味着需要创建新的子类
代码示例:
class A : def GetA (self ): print ('a' ) class B : def GetB (self ): print ('b' ) class C (A, B): pass a = A() b = B() c = C() c.GetA() >>>a c.GetB() >>>b class A : def GetA (self ): print ('a' ) class B : def GetB (self ): print ('b' ) class C (A, B): pass C.__bases__ = (A, B) a = A() b = B() c = C() c.GetA() c.GetB() >>> a b C.__base__ = (A, object ) a = A() b = B() c = C() c.GetA() c.GetB() AttributeError: 'C' object has no attribute 'GetB'
我们也可以通过__base__ += calss来对目标类进行拓展。如下代码:
class A : def GetA (self ): print ('a' ) class B : def GetB (self ): print ('b' ) class C (A ): pass C.__base__ += (B, object ) a = A() b = B() c = C() c.GetA() c.GetB() >>> a b
类、类对象和实例对象 我们在类中定义的属性是静态的变量,相当于C语言的static关键字,类的属性是与类对象进行绑定,并不会依赖与任何实例对象 。
那么下面就会衍生出一个问题,如果实例对象中属性的名字和类对象的属性名重复了会怎么样呢?
>>> class C : count = 0 >>>a = C() >>>b = C() >>>c = C() >>>print (a.count, b.count, c.count) 0 0 0 >>>c.count += 10 >>>print (a.count, b.count, c.count) 0 0 10 >>> C.count += 100 >>> print (a.count, b.count, c.count)100 100 10
这个例子说明,在实例对象c中的count属性进行了赋值后,就会覆盖掉类对象C的count属性。
由此我们有以下结论:
一般不要再类里面定义出所有属性和方法,应该多利用继承和组合机制来进行扩展。
使用骆驼命名法,CamelCase。
绑定 Python严格要求方法需要有实例才能被调用 ,这种限制就是绑定。
如下代码:
>>>class A : def printA (): print ('A' ) >>>A.printA() A
但是一旦将类对象实例化以后,根据类实例化后的实例对象无法调用里面的函数。
>>>B = A() >>>B.printA() TypeError: printA() takes 0 positional arguments but 1 was given
实际上当我们调用类的实例对象的方法时,B.printA()的完整代码为B.printA(B),由于Pyhton的绑定机制,**这里自动把B对象作为第一个参数传入,所以会出现TypeError,print()不需要参数,但是实际上传入了一个参数B。
下面我们利用__dict__来查看对象所有用的属性,来更好地理解。
>>>class C : def setXY (self, x, y ): self .x = x self .y = y def printXY (self ): print (self .x, self .y) >>>d = C() >>>d.__dict__ {} >>>C.__dict__ mappingproxy({'__module__' : '__main__' , 'setXY' : <function C.setXY at 0x00000166C7E2A378 >, 'printXY' : <function C.printXY at 0x00000166C7E2A400 >, '__dict__' : <attribute '__dict__' of 'C' objects>, '__weakreg__' : <attribue '__weakref__' of 'C' objects>, '__doc__' : None })
__dict__属性由一个字典组成,字典中仅有实例对象的属性,不显示属性和特殊属性(魔法方法),键表示的是属性名,值代表属性相应的数据值。
当我们给d加入新属性,而且这两个属性仅属于实例对象自身。
>>>d.setXY(4 , 5 ) >>>d.__dict__ {'x' : 4 , 'y' : 5 }
因为self参数,当实例对象d调用setXY方法时,传入参数d,使得self.x = 4, self.y = 5等同于d.x = 4, d.y = 5。
再次强调这个属性只属于实例对象本身,即使我们删除掉之前定义的C类,同样可以获得执行:
>>>del C >>>d.printXY() 4 5
注意,一般只要用实例属性,而不要用类属性,因为类属性通常只用来跟踪与类相关的值。
类中的属性与方法是静态的,只有在程序退出后,才会被释放。
问题
类对象在什么时候产生。
当你这个类定义完时,类定义就编程类对象,可以直接通过“类名.属性”或者“类名.方法名()”引用或使用相关的属性或方法。
类中的self.x = x是实例属性
定义一个栈类,用于模拟一种后进先出的(LIFO)特性的数据结构。至少需要有以下方法:
方法名
含义
isEmpty()
判断当前栈是否为空(返回True或者False)
push()
往栈的顶部压如一个数据项
pop()
往栈顶弹出一个数据项(并在栈中删除
top()
显示当前栈顶的一个数据
bottom()
显示当前栈底的一个数据
自己的代码:
class Stack (): def __init__ (self ): self .LIST = [] def isEmpty (self ): if self .LIST == []: return 'TRUE' else : return 'FALSE' def push (self, data ): self .LIST.append(data) def pop (self ): self .LIST.pop() def top (self ): return self .LIST[-1 ] def bottom (self ): return self .LIST[0 ] data1 = 1 data2 = 2 data3 = 3 a = Stack() print (a.isEmpty())a.push(data1) a.push(data2) print (a.isEmpty())print (a.top())print (a.bottom())a.push(data3) print (a.top())a.pop() print (a.top())
答案代码:
class Stack : def __init__ (self, start=[] ): self .stack = [] for x in start: self .push(x) def isEmpty (self ): return not self .stack def push (self, obj ): self .stack.append(obj) def pop (self ): if not self .stack: print ('警告,栈为空!' ) else : return self .stack.pop() def top (self ): if not self .stack: print ('警告,栈为空!' ) else : return self .stack[-1 ] def bottom (self ): if not self .stack: print ('警告:栈为空!' ) else : return self .stack[0 ]