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

本文深入探讨了Python中的类、对象、组合与Mixin的概念,解释了如何通过组合和多继承来扩展类的功能,同时详细解析了类属性与实例属性的区别及绑定方法的工作原理。

  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

                              

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

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

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

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

                

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

      

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

 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__  

       

   

转载于:https://www.cnblogs.com/Chris-01/p/10310739.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值