頗有一段時間沒使用python了,前兩天研究微信公衆號使用了下python的django服務,感受好多知識都遺忘了,畢竟以前沒有深刻的實踐,長期不使用就忘得快。本博的主要目的就是對Python中我認爲重要的面對對象知識進行總結,一是加深記憶,而是方便往後遺忘了好迅速的撿起來。html
目錄索引:python
(1)隱式的基類object程序員
(2)python中類和對象的區別算法
(3)實現python風格類所用特殊方法django
(a)__all__和__slots__編程
(b)__format__()、__repr__()和__str__()設計模式
(c)__init__()方法微信
(d)__new__()和__del__()函數
(e)__call__()方法post
(f)屬性訪問、特性和修飾符
(g)@classmethod和@staticmethod
(h)關於私有屬性的說法
(4)抽象基類和繼承
每一個Python類的定義都回隱式繼承自object類,它的定義很是簡單,幾乎什麼行爲都不包括。
能夠看到類定義就是對type類的一個對象的類型聲明,基類爲object,相應的派生自object類中的對象方法也將繼承各自相應的默認實現,在某些狀況下,基類中一些特殊方法的默認行爲也正是咱們想要的,對於一些特殊狀況,就須要重寫這些方法。
Python2 沒有默認繼承object
Python3 默認所有繼承object類,都是新式類
(1)類屬性和對象屬性的區別
對象能夠經過 對象名.屬性名 調用對象屬性和類屬性
類能夠經過 類名.屬性名 調用類的屬性,可是不能調用對象的屬性
class People(object): # 類屬性是指定義在類的內部並且在方法的外部的屬性 money = 10000 def __init__(self,name,age,gender=1): # 對象屬性是指定義在方法的內部的屬性,例如本例中 # name,age和gender都是對象屬性 self.name = name self.age = age self.gender = gender student1 = People("張三",20) student2 = People("李四",25) print(student2.name) print(student2.money) print(People.money) print(People.name)
再注意看下面代碼輸出結果:
print(id(student1.money))
print(id(student2.money))
print(id(People.money))
說明:對象student一、student2和類People的屬性money的內存地址都是相同的
繼續往下看
student1.money -= 1000
print("student1.money:", student1.money)
print("student1.money id:",id(student1.money))
print("student2.money:", student2.money)
print("student2.money id:",id(student2.money))
print("People.money id:",id(People.money))
說明:student1引用的money屬性的內存地址已經和另外兩個的不同了而另外兩個的內存地址卻仍是同樣的
緣由:在通過表達式student1.money -= 1000 的過程時,會先在對象中查找是否有money這個屬性,若是有的話,則直接進行運算若是沒有,則會去類中查找是否有money屬性,若是在類中找到money屬性,那麼student1就會建立一個對象屬性money,在第二次調用的時候就會調用本身的對象屬性,而不是類People中的屬性了,而student2由於沒有通過運算,因此不會建立本身的money屬性,而是引用類People的屬性,因此student2和People引用的仍是同一個屬性
關於__all__ = []總結兩點:
A:在__init__.py文件中
表示形式: __all__=["module_a","module_b"] 在使用 from package_name import * 時 , 表示import 該package 中的 兩個module及 兩個module相關的類、方法等。
B:在普通的*.py中
表示形式: __all__=["class_name","function_name"] 在使用 from module_name import * 時,表示import 該module中的__all__中所列出的。
__slots__使用:
若是咱們想限制一個類的屬性怎麼辦?好比只容許對Student實例添加name和age屬性,爲了達到限制的目的,Python容許在定義class的時候,定義一個特殊的__slots__變量來限制class能添加的屬性
>>> class Student(object): ... __slots__ = ('name', 'age') # 用tuple定義容許綁定的屬性名稱 ... >>> s = Student() # 建立新的實例 >>> s.name = 'Michael' # 綁定屬性'name' >>> s.age = 25 # 綁定屬性'age' >>> s.score = 99 # 綁定屬性'score' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'score'
因爲score沒有被放到__slots__中,因此不能綁定score屬性,試圖綁定score將獲得AttributeError錯誤
注意:__slots__定義的屬性僅對當前類起做用,對繼承的子類是不起做用的
>>> class GraduateStudent(Student): ... pass ... >>> g = GraduateStudent() >>> g.score = 9999
除非在子類中也定義__slots__,這樣子類容許定義的屬性就是自身的__slots__加上父類的__slots__
上面的三個方法都是爲了爲Python對象提供一個很好的字符串表示
一般str()方法表示的對象對用戶更加友好,這個方法由__str__()實現
repr()方法的表示一般會被更加技術化,這個方法由__repr__()實現
{!r} 調用__repr__方法
{!s} 調用__str__方法
string.format()和內置的format()函數都使用__format()__方法,都是爲了得到給定對象的一個符合要求的字符串表示
直接上例子能更好的進行說明:
class People: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def __str__(self): #return "{__class__.__name__}(name={0},age={1},sex={2})" .format(self.name,self.age,self.sex,__class__=self.__class__) return "__str__:(name={name},age={age},sex={sex})" .format(__class__=self.__class__,**self.__dict__) def __repr__(self): return "__repr__:{__class__.__name__}(name={name},age={age},sex={sex})" .format(__class__=self.__class__,**self.__dict__) def __format__(self, format_spec): if format_spec == "": return str(self) return format_spec.replace("%n",self.name).replace("%s",self.sex) if __name__ == "__main__": people = People("john",18,"man") print("{0!s}".format(people)) print("{0!r}".format(people)) print("format:",format(people,"%n-%s")) print("__format__:",people.__format__("%n-%s")) print("format:",format(people))
__init__(self)就如同類的構造函數,儘可能在構造函數中實現屬性清單,這裏就很少作介紹
下面主要總結下Python繼承中super()方法的使用:
super實現原理:經過c3算法,生成mro(method resolution order)列表,根據列表中元素順序查詢調用新式類調用順序爲廣度優先,舊式類爲深度優先
super相似於嵌套的一種設計,當代碼執行到super實例化後,先去找同級父類,若沒有其他父類,再執行自身父類,再往下走,簡單說就是子類在父類前,全部類不重複調用,從左到右(見下面例子的D.mro()打印)
Python2 super調用 super(開始類名,self).函數名()
Python3 super().函數名()
上例子更能說明上述問題:
class A(): def go(self): print ("go A go!") def stop(self): print ("stop A stop!") def pause(self): raise Exception("Not Implemented") class B(A): def go(self): super(B, self).go() print ("go B go!") class C(A): def go(self): super(C, self).go() print ("go C go!") def stop(self): super(C, self).stop() print ("stop C stop!") class D(B,C): def go(self): super(D, self).go() print ("go D go!") def stop(self): super(D, self).stop() print ("stop D stop!") def pause(self): print ("wait D wait!") class E(B,C): pass a = A() b = B() c = C() d = D() e = E() # 說明下列代碼的輸出結果 d.go() print('--------') e.go() print('--------') d.stop() print('--------') e.stop() print(D.mro()) a.pause() b.pause() c.pause() d.pause() e.pause()
__ new__ ()在__ init__()以前被調用,用於生成實例對象.利用這個方法和類屬性的特性能夠實現設計模式中的單例模式.單例模式是指建立惟一對象嗎,單例模式設計的類只能實例化一個對象.
class Singleton(object): __instance = None # 定義實例 def __init__(self): pass def __new__(cls, *args, **kwd): # 在__init__以前調用 if Singleton.__instance is None: # 生成惟一實例 Singleton.__instance = object.__new__(cls, *args, **kwd) return Singleton.__instance
__new__()方法的使用和Python元編程相關,能夠查看我以前的博客:Python元編程
__del__()方法涉及到Python對象銷燬,Python文檔中用不穩定性來描述__del__()方法的這種行爲,而且提供了額外的關於異常處理的註釋,總之,該函數的使用要慎重之慎重。
__ del__稱做析構方法
析構方法,當對象在內存中被釋放時,自動觸發執行。
注:此方法通常無須定義,由於在Python中,程序員在使用時無需關心內存的分配和釋放,由於此工做都是交給Python解釋器來執行,因此,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。在程序執行結束以後,執行此方法
Python可調用對象:函數,方法,使用 yield 關鍵字的函數或方法,類的實例
__call__方法將類的實例變成可調用對象
>>>class Reader(): def __init__(self,name,nationality): self.name = name self.nationality = nationality def __call__(self): print('Reader: %s Nationality: %s' % (self.name, self.nationality)) >>>r = Reader('Annie','Chinese') >>>r() Reader:Annie Nationality: Chinese
以前的博客Python之屬性、特性和修飾符做了詳細說明
classmethod 修飾符對應的函數不須要實例化,不須要 self 參數,但第一個參數須要是表示自身類的 cls 參數,能夠來調用類的屬性,類的方法,實例化對象等。
#!/usr/bin/python # -*- coding: UTF-8 -*- class A(object): bar = 1 def func1(self): print ('foo') @classmethod def func2(cls): print ('func2') print (cls.bar) cls().func1() # 調用 foo 方法 A.func2() # 不須要實例化
staticmethod修飾符實現靜態方法,該方法不強制要求傳遞參數
#!/usr/bin/python # -*- coding: UTF-8 -*- class C(object): @staticmethod def f(): print('runoob'); C.f(); # 靜態方法無需實例化 cobj = C() cobj.f() # 也能夠實例化後調用
Python並無真正的私有化支持,但可用下劃線獲得僞私有
我的習慣:
(1)_XXX 單下劃表明protected
(2)__XXX 雙下劃線開始的且不以_結尾表示private(下面說明)
(3)__XXX__系統定義的屬性和方法
看下面的例子:
class People: __name="zhanglin" def __init__(self): self.__age = 16 print(People.__dict__) p = People() print(p.__dict__)
會發現__name和__age屬性名都發生了變化,都變成了(_類名+屬性名),只有在__XXX這種命名方式下才會發生變化,因此以這種方式做爲僞私有說明
該主題內容請查看以前的博客:Python之抽象基類