Python 和 JavaScript同樣便是面向過程語言,也是面嚮對象語言,動態語言。大多數面嚮對象語言裏,Class是必不可少的。面向對象有三大特性:封裝, 繼承,多態。在Python中Class究竟是怎樣的呢?java
先來看一個示例:python
class Person(object): id='' name = '' age = 3 # 等同於Java中的<init>,即構造器 def __init__(self, id, name, age): print("init a Person instance") self.id = id self.name = name self.age = age def show(this): print(this) # print(this.toString()) #def toString(self): # return "id:{}, name:{}, age:{}".format(self.id, self.name, self.age) # 等同於Java中的toString def __str__(self): # return self.toString() return "id:{}, name:{}, age:{}".format(self.id, self.name, self.age) # 等同於Java中的finalize方法,del 實例時調用 def __del__(self): print("finally a Person instance") self.id = None self.name = None self.age = None self = None
下面是以對比Java的方式,來講明Python中的類: 算法
1)Class中,包括屬性、方法,它們都是public的。在Python的Class中,是不存在private,protected等修飾符的。編程
2)__init__是構造函數,調用構造器時,會自動調用__init__。它至關於Java中的<init>。在建立一個Python對象時,不須要像Java那樣使用new。app
3)__del__是析構函數,當del instance時,會自動調用__del__。它至關於Java中的finalize編程語言
4)須要獲取對象的字符串表示時,會調用__str__。它就至關於Java中的toString。函數
5)類的方法的第一個參數,都是self,至關於Java中的this。其實Java中的實例方法的第一個參數也是this,只是被隱藏了而已,若是你瞭解JVM 運行時的話,或者使用過 javassist或者asm等字節碼工具的話,會知道的。上面的示例中的p1.show()運行時,能夠理解爲執行的是Person.show(p1)。 並非說必須得寫成self,你也能夠寫完this或者其餘任何的知足變量命名的形式。但一般你們都約定俗稱的寫爲self,保持編碼風格的統一,有利於方便他人理解代碼。工具
6)__init__,__del__,__str__不是必須的。學習
7)全部屬性都要有初始值測試
Java,JavaScript ES6都支持setter,getter。Python中也是支持的。
class Person(object): id='' name = '' age = 3 # 等同於Java中的<init>,即構造器 def __init__(self, id, name, age): print("init a Person instance") self.id = id self.name = name self.age = age def show(this): print(this) # print(this.toString()) #def toString(self): # return "id:{}, name:{}, age:{}".format(self.id, self.name, self.age) # 等同於Java中的toString def __str__(self): # return self.toString() return "id:{}, name:{}, age:{}".format(self.id, self.name, self.age) # 等同於Java中的finalize方法,del 實例時調用 def __del__(self): print("finally a Person instance") self.id = None self.name = None self.age = None self = None def __get__(self, name): print("invoke in __get__") print(self.__dict__) return 1111 def __getattr__(self, name): print("invoke in __getattr__") return 1111 def __getattribute__(self, name): print("invoke in __getattribute__") print(object.__getattribute__(self, name)) print("after invoke in __getattribute__") return object.__getattribute__(self, name)
對於舊式類,訪問屬性的順序是:
1)直接訪問屬性
2)訪問__getattr__
對於新式類,訪問屬性的順序是:
1)__getattribute
2)直接訪問屬性
3)__getattr__
切記,若是寫了__getattribute__,最後一句話必須是object.__getattribute(self,name) 不然就會出現:’XxxType’ object is not callable 。它的存在,更像是做爲攔截器使用。
C++是多繼承的,Java是單繼承的,Python借鑑了C++的多繼承方式。
在繼承結構下,訪問屬性,方法時,與Java中同樣的,先本身的,本身沒有時,纔去從父類找。
因爲支持多繼承,因此當一個屬性、方法,在多個父類中都存在時,會訪問到哪一個呢?
要解答這個問題,就得先知道搜索屬性、方法的順序。採用的搜索算法是深度優先搜索算法。
也就是一個class A(S1,S2,S3):pass; 要調用一個屬性時,會先從A裏找,找不到再從S1,若是還找不到再從S2,依次類推。
那麼在多繼承狀況下,若是A,S1,S2,S3都重寫了__getattr__方法,那會有什麼區別呢?查找順序大致不變的:
1) A的直接屬性,找不到而後是A.__getattr__
2) S1的直接屬性,找不到而後是S1.__getattr__
3) S2的直接屬性,找不到而後是S2.__getattr__
4) S3的直接屬性,找不到而後是S3.__getattr__
對於構造函數__init__,在構造實例時,不會像Java那樣,先去調用父類的__init__。
class Student(Person): def __init__(self, id, name, age,email): print("invoke in Student __init__") super(Student, self).__init__(id, name, age) self.email = email
Java中有super(),Python中是否有呢?可否利用super呢?若是不能自動調用父類的構造器,那麼在重寫__init__又得給全部的屬性都分配一下,這個好麻煩的,該怎麼辦呢?只能以編程的方式本身調用了。
super(類,instance),這個函數的做用是找到指定的類的下一個父類的指定方法,將該方法在指定的實例上調用。
例如上面的例子中,繼承關係是這樣的:Student > Person > object。self是Student對象,super(Student, self).__init__(id, name, age)該語句的意思就是:找到Student的下一個父類(即Person)的__init__方法,而後在self上調用該__init__方法。
須要注意的是:
1)super函數的兩個參數,不能有誤。須要知足兩個條件,第二個instance應該是第一個參數所表明的類的實例(或者是其子類的實例)。
2)Super只能在新式類中使用。
在.Net和Scala編程語言中,都是支持運算符重寫的。若是沒有接觸過這些東西,可能會費解的。其實只要把運算符看着是方法就能夠了。從這個角度來理解的話,Java也是支持運算符重寫的,只不過呢,該特性並無暴露給用戶罷了。例如字符串拼接 + ,它實際上是調用的StringBuilder.append方法,遍歷集合的foreach,它其實調用的是iterator。既然提到了運算符重寫,那麼Python中也必然是支持的,而且它把這個特性暴露出來了。
Python String 就重寫了幾個運算符:
+ 字符串拼接 * copy多份 == 比較字符串內容 > 字符串比較 < 字符串比較
那麼怎樣實現運算符重寫呢?
上面已經說明,將運算符看做是一個方法。那麼重寫運算符,就是就是重寫方法了。
下面列出了常見的運算符重載:
重載方法 |
說明 |
調用 |
__init__ |
構造器 |
對象創建,X=Class() |
__del__ |
析構方法 |
對象回收,或者del X |
__add__ |
運算符 + |
X+Y, X+=Y |
__iadd__ |
加強的 + |
X+Y, X+=Y |
__radd__ |
左側+ |
Noninstance + Y |
__or__ |
運算符 | (位 OR) |
X|Y, X|=Y |
__repr__,__str__ |
打印,轉換 |
print(X), repr(X),str(X) |
__getattr__ |
點號運算符 |
獲取屬性 |
__setattr__ |
賦值運算符 |
設置屬性 |
__call__ |
函數調用 |
Y() |
__len__ |
長度 |
len(X) |
__cmp__, __lt__, __eq__ |
比較 |
X < Y X==Y |
__getitem__ |
索引運算符 |
X[name] |
__setitem__ |
索引賦值 |
X[Y]=Z |
__iter__ |
迭代 |
循環,迭代等 |
使用__getitem__可使得對象具有索引的方式來訪問?
測試以下:對上面的Student以下:
class Student(Person): def __init__(self, id, name, age,email): print("invoke in Student __init__") super(Student, self).__init__(id, name, age) self.email = email def __getitem__(self, name): return self.__dict__[name] + "_suffix"
執行測試:
import model Student = model.Student s = Student('0001', 'fjn', 20, 'fs1194361820@163.com') s.show() print(s["email"])
發現結果是:fs1194361820@163.com_suffix
經過上面的學習,瞭解到Python的屬性、方法都是public, 這點和JavaScript太 同樣了。寫Java時間久了,私有屬性的好處,怎麼能夠浪費了呢。那麼如何模擬出私有的?
有兩種方式能夠模擬的,都與JavaScript裏的理念是相似的。
方式一:使用對象時,直接加屬性。
方式二:利用對象的__dict__
在java中,會將實例方法與類方法區分,具體作法是在 class 方法上加上static修飾符。在python中是沒有static的,那麼如何實現static方法。
前面也講了,實例方法聲明時,第一個參數是self。在python中,實例方法本質就是調用類方法的。
p1=Person(‘a’,’b’,23)
Person.show(p1)
在方法前加上註解:@staticmethod