组合,Mixin,类、类对象、实例对象

时间:2022-01-30 16:34:49

  1、组合,将几个横向关系的组合在一起。所谓的组合就是将类的实例化放到新类里面,那么就将旧类组合进去了。

class Turtle:
def __init__(self, x): # 注意 init 前面必须双下划线
self.num = x class Fish:
def __init__(self, x):
self.num = x class Pool:
def __init__(self, x, y):
self.turtle = Turtle(x) # Pool类中实例化Turtle对象
self.fish = Fish(y) def print_num(self):
print('水池*有乌龟 %d 只,小鱼 %d 条!'%(self.turtle.num,self.fish.num))

  调用结果:

pool = Pool(2, 7)
>>> pool.print_num()
水池*有乌龟 2 只,小鱼 7 条!

  **:在新类中实例化旧类,如:self.turtle = Turtle(x) ,个人理解为将 Turtle 对象传递给 self.turtle 对象(属性),然后self.turtle 对象就具有Turtle 对象的属性了(体现在实例化 Pool 对象后,可以调用self.turtle.num,num 在 Pool 类中是没有定义的)。

  2、Mixin 简介

Mixin 编程是一种开发模式,是一种将多个类中的功能单元的进行组合的利用的方式,这听起来就像是有类的继承机制就可以实现,然而这与传统的类继承有所不同。通常 Mixin 并不作为任何类的基类,也不关心与什么类一起使用,而是在运行时动态的同其他零散的类一起组合使用。

特点

使用 Mixin 机制有如下好处:

  • 可以在不修改任何源代码的情况下,对已有类进行扩展;
  • 可以保证组件的划分;
  • 可以根据需要,使用已有的功能进行组合,来实现“新”类;
  • 很好的避免了类继承的局限性,因为新的业务需要可能就需要创建新的子类。

多继承

Python支持多继承,即一个类可以继承多个子类。可以利用该特性,可以方便的实现mixin继承。如下代码,类A,B分别表示不同的功能单元,C为A,B功能的组合,这样类C就拥有了类A, B的功能。

class A:
def get_a(self):
print 'a'
class B:
def get_b(self):
print 'b'
class C(A, B):
pass
c = C()
c.get_a()
c.get_b()

__bases__

多继承的实现就会创建新类,有时,我们在运行时,希望给类A添加类B的功能时,也可以利用python的元编程特性,__bases__属性便在运行时轻松给类A添加类B的特性,如下代码:

A.__bases__ += (B,)
a.get_b()

其实__bases__也是继承的机制,因为__bases__属性存储了类的基类。因此多继承的方法也可以这样实现:

class C:
pass
C.__bases__ += (A, B, )

插件方式

以上两种方式,都是基于多继承和python的元编程特性,然而在业务需求变化时,就需要新的功能组合,那么就需要重新修改A的基类,这回带来同步的问题,因为我们改的是类的特性,而不是对象的。因此以上修改会对所有引用该类的模块都收到影响,这是相当危险的。通常我们希望修改对象的行为,而不是修改类的。同样的我们可以利用__dict__来扩展对象的方法。

class PlugIn(object):
def __init__(self):
self._exported_methods = [] def plugin(self, owner):
for f in self._exported_methods:
owner.__dict__[f.__name__] = f
def plugout(self, owner):
for f in self._exported_methods:
del owner.__dict__[f.__name__]
class AFeature(PlugIn):
def __init__(self):
super(AFeature, self).__init__()
self._exported_methods.append(self.get_a_value)
def get_a_value(self):
print 'a feature.'
class BFeature(PlugIn):
def __init__(self):
super(BFeature, self).__init__()
self._exported_methods.append(self.get_b_value)
def get_b_value(self):
print 'b feature.'
class Combine:pass
c = Combine()
AFeature().plugin(c)
BFeature().plugin(c)
c.get_a_value()
c.get_b_value()

  

  3、类、类对象、实例对象

 class C:
count = 0 >>> a = C()
>>> b = C()
>>> c = C()
>>>a.count
0
>>> c.count += 10
>>> a.count
0
>>> b.count
0
>>> C.count += 100
>>> a.count
100
>>> b.count
100
>>> c.count
10

                              组合,Mixin,类、类对象、实例对象

  1)python中无处不对象。类中定义的属性时静态属性,类属性和类对象是绑定的,不依赖于实例对象。

  2)实例化对象后,进行实例对象属性的修改不会影响类对象的属性,所以 c.count += 10不会影响 C.count 的值。

  3)修改类对象属性属性之后,实例化对象的属性也会跟着改变,注意,经过实例对象修改过的属性不随着类属性的改变而改变(见上边染色部分代码),原因:个人理解为此处类似变量间的复制,实例对象是一个标签,和类对象指向同一个类属性地址,所以:a)类属性改变,实例对象属性跟着改变;b)单独修改实例对象属性是从新开辟堆栈,覆盖了该实例对象原本的类属性,所以这个修改不影响类属性,修改类属性也不影响经过修改的实例对象属性。

  4、如果属性名称和方法名称一样,属性名称会覆盖方法名称。如下:给实例对象 c 定义 x 属性之后,再调用 x() 方法会报错。

组合,Mixin,类、类对象、实例对象

  4、绑定:Python 严格要求方法需要有实例才能够被调用,这种限制就是绑定概念。 个人理解:当实例对象调用方法时,会默认再方法里加一个 self 参数,如下面调用 bb.printBB()报错是因为 python默认的调用方式是 bb.printBB(bb),而 class BB 的printBB() 方法里面没有 self 参数,所以出现:TypeError :...but 1 was given 这个报错。

组合,Mixin,类、类对象、实例对象

  下面这段代码验证上面的个人理解。

 class BB:
def printBB(self):
print('BB') >>> a = BB()
>>> a.printBB()
BB
>>> BB.printBB()
Traceback (most recent call last):
File "<pyshell#35>", line 1, in <module>
BB.printBB()
TypeError: printBB() missing 1 required positional argument: 'self'
>>> BB.printBB('b')
BB

  5、查看对象属性:对象.__dict__

组合,Mixin,类、类对象、实例对象