Python基础(十)—面向对象的深入讲解(继承、Mixin编程机制等)

面向对象的三大特征

面向对象(Object Oriented),对象=属性+方法

  • 封装
    对象封装了属性、方法的函数,成为一个独立性的模块(信息隐蔽),使得对象更安全。

  • 继承
    面向对象的一个重要特性是复用性,继承是实现复用性的一个重要手段,继承就是子对象可以继承父对象的属性和行为,亦即父对象拥有的属性和行为,其子对象也就拥有了这些属性和行为。

  • 多态
    多态性是指不同对象对同一方法响应不同的行动。

self、_init_(self)、公有&私有

  • self
    相当于其他语言的this,每次实例化一个对象都保存了不同的self,当进行该对象的操作时,接受到第一个参数self,python则知道是哪一个实例的对象。
    	class Ball:
    	    def setName(self, name):
    	        self.name = name
    
    	    def kick(self):
    	        print('这是%s' % self.name)
    
    	ball = Ball()
    	ball.setName('足球')
    	ball.kick()
    
  • _init_(self)
    类似与其他语言的构造方法,是在实例化对象的时候执行的函数。
    	class Ball:
    	    def __init__(self, name):  # 可以对name进行参数的默认设置
    	        self.name = name
    
    	    def kick(self):
    	        print('这是%s' % self.name)
    
    	ball = Ball('足球')
    	ball.kick()
    
  • 公有&私有
    python的属性和方法默认都是公有的。可以通过 name manging(名字改编)也就是在属性前加上“__”两个下划线就可以变为私有,但是也不是绝对的私有。
    class Person:
        __name = 'lz'
    
    def getName(self):
        return self.__name
    
    p = Person()
    # print(p.__name)  # 报错AttributeError: 'Person' object has no attribute '__name'
    print(p.getName())  # lz
    print(p._Person__name)  # lz
    

##继承的一些特性
继承对象的时候,子类会继承父类的属性和方法,但是前提是子类没有自己的构造函数(__init(self)),若有自己的构造方法(子类会覆盖父类的同名方法)。

  • __init__继承父类
    子类在使用了自己的__init__的时候,想要继承父类的属性,可以通过以下两种方法
    • 调用未绑定的父类方法
    • 使用super函数
    	class Fish:
    	    def __init__(self, x, y):
    	        self.x = x
    	        self.y = y
    
    	class Shark(Fish):
    	    def __init__(self, x, y, z):
    	        # Fish.__init__(self, x, y)  # 方法一:调用未绑定的父类方法,此时的self是子类的实例对象,所以称为未绑定父类的方法。因此容易想到调用的时候可以使用Fish.__init__(shark)
    	        super(Shark, self).__init__(x, y)  # 方法二:使用super函数,python推荐使用的方法
    	        self.z = z
    
  • python可以实现多重继承,但是python并不推荐。
    	class Ball(Ball1, Ball2)
    
  • 组合
    如果要求定义一个类:水池类,里面有鱼、乌龟。这种情况,可以使用组合的方式。
    	class Turtle:
    	    def __init__(self, x):
    	        self.num = x
    
    	class Fish:
    	    def __init__(self, x):
    	        self.num = x
    
    	class Pool:
    	    def __init__(self, x, y):
    	        self.turtle = Turtle(x)
    	        self.fish = Fish(y)
    
    	    def print_num(self):
    	        print('水池内有乌龟%d条,鱼%d条' % (self.turtle.num, self.fish.num))
    
    	pool = Pool(1, 10)
    	pool.print_num()
    

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()
      

类、类对象和实例对象

三者是什么:定义的时候是类;在写完类之后就变成类对象;实例化一个类对象的时候,就是实例对象

  	class Ball:
  		name = '球'
  • 三者的相互影响
    类定义 -> 类对象 -> 实例对象。类定义中定义的属性都是静态属性,类属性和类对象相互绑定,不会依赖实例化对象。所以当实例对象的属性改变时,只是实例属性覆盖了类属型。反之,当实例对象的属性没改变,那就是还受类属型的影响

    	class Fish:
    	    size = 10
    
    	a = Fish()
    	b = Fish()
    	c = Fish()
    
    	print(a.size, b.size, c.size)  # 10 10 10
    
    	c.size += 10
    	print(a.size, b.size, c.size)  # 10 10 20
    
    	Fish.size += 100
    	print(a.size, b.size, c.size)  # 110 110 20
    
  • 类属性名和方法同名时,方法会被覆盖

  • 绑定
    python严格要求方法需要实例才能调用,这其实就是python的绑定概念。

    	class BB:
    	    def setXY(self, x, y):
    	        self.x = x
    	        self.y = y
    	    def printXY(self):
    	        print(self.x, self.y)
    
    	bb = BB()
    	print(bb.__dict__)
    	print(BB.__dict__)
    
    	bb.setXY(5, 10)  # 这里其实相当于bb.setXY(bb, 5, 10)
    	print(bb.__dict__)
    	print(BB.__dict__)
    
    	del BB
    	# cc = BB()  # 报错
    	bb.printXY()  # 5 10。因为实例化之后,就成为了静态属性,所以不会受影响
    

对象的内置方法

下面介绍对象的一些内置方法(本身具备有的)。

  • issubclass(class, classinfo)
    如果class是classinfo的子类就返回True,其他情况则返回False,有些特殊情况:
    • 一个类是自身的子类
    • classinfo可以是类对象组成的元组,只要与其中一个匹配,则返回True
    • issubclass(className, object) # True
  • isinstance(obecjt, classinfo)
    检查一个实例对象obecjt,是否属于classinfo类,同样classinfo可以是元组的格式。
    • 如果第一个参数object不是对象,则永远返回False
    • 如果第二个参数classinfo不是类或者类对象组成的元组,会抛出“TypeError”的异常
  • hasattr(object, name)
    查看实例对象object,是否有参数name,其中name是字符串的格式。
  • getattr(object, name[, default])
    返回对象object指定的属性值name,如果指定的属性不存在,则返回定义的default,如果没有定义默认值,则抛出异常NameError…not defined
  • setattr(object, name, value)
    设置对象object指定的属性值name,如果属性不存在,则会新建该指定属性,值为value
  • delattr(object, name)
    删除对象object指定的属性值name,如果属性不存在,则抛出异常AttributeError
class A:
    def __init__(self, x=1):
        self.x = x
class B(A):
    def __init__(self, x, y=-1):
        super(B, self).__init__(x)
        self.y = y

b1 = B(2, -2)
print(issubclass(B, B))  # True
print(issubclass(B, (A, object)))  # True

print(isinstance(b1, (A, object)))  # True

print(hasattr(b1, 'x'), b1.x)  # True 2
print(hasattr(b1, 'y'), b1.y)  # True -2

print(getattr(b1, 'x', 10))  # 2
print(getattr(b1, 'z', 'z参数不存在'))  # z参数不存在

print(setattr(b1, 'x', 10), b1.x)  # None 10
print(setattr(b1, 'z', 'z参数不存在'), b1.z)  # None z参数不存在

print(delattr(b1, 'x'))  # None delattr
  • property(fget=None, fset=None, fdel=None, doc=None)
    通过类定义的属性设置属性。
class C:
    def __init__(self, size = 10):
        self.size = size
    def getSize(self):
        return self.size
    def setSize(self, value):
        self.size = value
    def delSize(self):
        del self.size
    x = property(getSize, setSize, delSize)

c1 = C(20)
# 获取
print(c1.getSize())  # 20
print(c1.x)  # 20
# 设置
c1.x = 30
print(c1.x)  # 30
# 删除
del c1.x
# print(c1.x)  # 报错

文章参照小甲鱼

个人博客:Loak 正 - 关注人工智能及互联网的个人博客
文章地址:Python基础(十)—面向对象的深入讲解(继承、Mixin编程机制等)

上一篇:less基本用法


下一篇:computd watch methods和mixin