面向對象的三大特征
面向對象(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程式設計機制等)