天天看點

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 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程式設計機制等)