目錄結構:python
下面是類使用的一個簡單案例,編程
class person: "person 類的描述信息" def __init__(self,name="",age=0): self.name = name self.age = age def setName(self,name): '''設置名稱''' self.name = name def getName(self): '''獲取名稱''' return self.name def setAge(self,age): self.age = age def getAge(self): return self.age def __del__(self): print("person (name:"+self.name+",age:"+str(self.age)+") is deleting") p = person("jame",12); #Output:jame print(p.getName()) #效果和下面的調用方式是同樣的 #person.getName(p) #Output:12 print(p.getAge()) #效果和下面的調用方式是同樣的 #person.getAge(self) #Output: person 類的描述信息 print(p.__doc__) #輸出person類的幫助信息 print(help(person)) del p
輸出:編程語言
jame
12
person (name:jame,age:12) is deleting
person 類的描述信息
Help on class person in module __main__:
class person(builtins.object)
| person 類的描述信息
|
| Methods defined here:
|
...
__init__函數是構造函數,在構造對象的時候會自動調用該函數。
__del__函數是析構函數,在使用del刪除目標對象時會自動調用該方法。
Python中,類不能夠定義多個構造方法,只能定義一個構造方法。全部成員實例函數的第一個參數都必須是self參數(也能夠有非self參數的成員函數,下面會講解)。python會自動建立成員屬性,無需提早定義,例如self.name=name。函數
類屬性的定義無需使用self參數,能夠直接定義到類中。類屬性能夠直接經過類名調用。ui
class Person: type = "哺乳類" def __init__(self,name,age): self.name = name self.age = age p = Person("luyis",13) #經過類名調用類屬性 print(Person.type) #經過對象調用類屬性 print(p.type)
靜態方法是類中的函數,不須要實例。靜態方法主要是用來存放邏輯性的代碼,主要是一些邏輯屬於類,可是和類自己沒有交互,即在靜態方法中,不會涉及到類中的方法和屬性的操做。用 @staticmethod 裝飾的不帶 self 參數的方法,類的靜態方法能夠沒有參數,能夠直接使用類名調用。spa
import time class TimeTest: def __init__(self): pass #靜態方法 @staticmethod def show_time(): print(time.strftime("%H:%M:%S", time.localtime())) TimeTest.show_time()
類方法是將類自己做爲對象進行操做的方法。默認有個 cls 參數,能夠被類和對象調用,須要加上 @classmethod 裝飾器。調試
class ClassTest: num = 0 def __init__(self,num): ClassTest.num = num #類方法 @classmethod def showNum(cls): print(cls.num) c = ClassTest(20) #經過類直接調用 ClassTest.showNum() #經過實例對象調用 c.showNum()
Python中的私有成員,不能在類外面直接訪問。私有成員只須要在成員前面加上兩條下劃線(__)就代表該成員是私有的了。code
class Person: #__type是私有成員 __type = "哺乳類" def __init__(self,name="",age=0,country="中國"): #__name,__age是私有成員 self.__name = name self.__age = age self.__setcountry(country) def get_name(self): return self.__name def get_age(self): return self.__age def set_name(self,name): self.__name = name def set_age(self,age): self.__age = age #__setcountry是私有函數 def __setcountry(self,cty): self.country = cty @classmethod def get_type(cls): return cls.__type @classmethod def set_type(cls,type): cls.__type = type p = Person("jame",21) print(p.get_name()) print(p.get_age()) print("--------------------") p.set_name("charlse") p.set_age(31) print(p.get_name()) print(p.get_age()) print("-------------------") print(Person.get_type()) print("------------------") Person.set_type("靈長類") print(Person.get_type())
輸出:對象
jame
21
--------------------
charlse
31
-------------------
哺乳類
------------------
靈長類
定義私有成員的格式:
__xxx:私有成員,只有類對象本身能訪問,子類對象不能直接訪問到這個成員,
注意:雖然不能在類外直接訪問到類的私有成員,但在對象外部能夠經過「 對象名._類名__xxx 」這樣的特殊方式來訪問類的私有成員。應此,在Python中不存在嚴格意義上的私有成員。blog
class Person: #__type是私有成員 __type = "哺乳類" def __init__(self,name="",age=0): #__name,__age是私有成員 self.__name = name self.__age = age p = Person("Emma",32); #直接訪問私有成員的值 print(p._Person__type) print(p._Person__name) print(p._Person__age) #直接修改私有成員的值 p._Person__age = 33 p._Person__name = "Angela" p._Person__type = "靈長類" print("-------------------") print(p._Person__type) print(p._Person__name) print(p._Person__age)
輸出:
哺乳類
Emma
32
-------------------
靈長類
Angela
33
Python除了自定義私有變量和方法外,還能夠定義專有方法。專有方法是在特殊狀況下或使用特殊語法時由python調用的,而不是像普通方法同樣在代碼中直接調用。看到形如__XXX__的變量或函數名時就須要注意下,這在python中是有特殊用途的,下面是Python中的部分專用方法:
__init__ : 構造函數,在生成對象時調用
__del__ : 析構函數,釋放對象時使用
__str__: 打印,轉換,適合於展現給用戶看的
__repr__ : 打印,轉換,適合開發者調試
__setitem__ : 按照索引賦值
__getitem__: 按照索引獲取值
__len__: 得到長度
__call__: 函數調用
__add__: 加運算
__sub__: 減運算
__mul__: 乘運算
__div__: 除運算
__mod__: 求餘運算
__pow__: 乘方
__doc__: 說明文檔信息
上面筆者已經講解過__init__和__del__函數的使用了,應此筆者再也不這裏重複綴述了。筆者接下來說解其它的專有方法的用法:
都是用於將對象轉化爲字符串的內置方法。
可是 repr() 函數將對象轉化爲供解釋器讀取的形式,__str__只是覆蓋了__repr__以獲得更友好的用戶顯示。
例如:
>>> y = 'a string' >>> repr(y) "'a string'" >>> str(y) 'a string'
repr函數的返回字符串能夠再次傳遞給eval函數。可是str函數的返回值傳遞給eval顯然是不合適。
>>> y == eval(repr(y)) True >>> y == eval(str(y)) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1 a string ^ SyntaxError: unexpected EOF while parsing
repr返回的字符串應該是編譯器可以再次解析的,str返回的字符串應該能夠是易閱讀的
在知道了repr和str的區別後,咱們就知道該如何自定義__repr__和__str__函數了。
class person: def __init__(self,name="",age=0): self.__name = name self.__age = age def __str__(self): return "name:%s,age:%d"%(self.__name,self.__age) def __repr__(self): return "person(name='%s',age=%d)"%(self.__name,self.__age) p = person("jame",12) #Output: name:jame,age=12 print(str(p))#same as print(p) #Output: person(name='jame',age=12) print(repr(p)) p2 = eval(repr(p)) #Output: name:jame,age=12 print(p2)
用於經過下標來設置和獲取值,能夠直接使用[]符來操做。
class DicDecorate: def __init__(self,dic): self.__dic = dic def __getitem__(self,key): return self.__dic[key] def __setitem__(self,key,val): self.__dic[key] = val dicdec = DicDecorate({}) dicdec[0] = "hello" dicdec[1] = "word" print(dicdec[0]) print(dicdec[1])
當調用len函數時會調用該方法。
lass DicDecorate: def __init__(self,dic): self.__dic = dic def __len__(self): return len(self.__dic); dicdec = DicDecorate({}) print(len(dicdec))
關於 __call__ 方法,不得不先提到一個概念,就是可調用對象(callable),咱們平時自定義的函數、內置函數和類都屬於可調用對象,但凡是能夠把一對括號()應用到某個對象身上均可稱之爲可調用對象,判斷對象是否爲可調用對象能夠用函數 callable。
若是在類中實現了 __call__ 方法,那麼實例對象也將成爲一個可調用對象。
class Entity: def __init__(self, x, y): self.x, self.y = x, y def __call__(self, x, y): self.x, self.y = x, y def __str__(self): return "x=%s,y=%s"%(self.x,self.y) e = Entity(2, 3) # 建立實例 if(callable(e)): e(4, 5) #實例能夠象函數那樣執行,並傳入x y值,修改對象的x y #Output:x=4,y=5 print(e)
class Vector: def __init__(self, a, b): self.a = a self.b = b def __str__(self): return 'Vector (%d, %d)' % (self.a, self.b) def __add__(self,other): return Vector(self.a + other.a, self.b + other.b) v1 = Vector(2,10) v2 = Vector(5,-2) print (v1 + v2)
Python繼承的語法格式:
class ClassName1(ClassName2): statement
其中
ClassName1:派生類
ClassName2:基類
class Shape: def __init__(self,type,area): self.type = type self.area = area def describe(self): return "Share type:%s,area=%f"%(self.type,self.area) class Square(Shape): def __init__(self,area): super().__init__("square",area); square = Square(12.1) print(square.describe())
和其它編程語言同樣,能夠重寫父類中的方法,能夠繼承父類中開放的屬性和方法(不能繼承父類中的私有屬性和方法)。
除了單重繼承,Python還支持多重繼承
class ClassName1(ClassName2,ClassName3,ClassName4...): statement
Python多重繼承中順序繼承是一個很是重要的概念,若是繼承的多個父類中有相同的方法名,但在派生類中使用時未指定父類名,則Python解釋器將從左至右搜索,即調用先繼承的類中的同名方法。
class A: def test(self): print("I am in Class A"); class B: def test(self): print("I am in Class B"); class C(A,B): def __call__(self): #調用A類中的test方法 self.test() #能夠經過類名顯示指定調用B類中的test方法 B.test(self) class D(B,A): def __call__(self): #調用B類的test方法 self.test() #經過類名顯示指定要調用A類中的test方法 A.test(self) c = C() c() print("---------------"); d = D() d()
輸出:
I am in Class A
I am in Class B
-----------------
I am in Class B
I am in Class A
經過上面的結果能夠看出,子類在父類中查找方法的順序是從左到右的。
磚石繼承問題是Python多重繼承中的典型問題,下面先經過一張圖片看看什麼是磚石繼承。
經過這張圖片看出,D繼承了B和C,B和C又派生於A,在調用D類的test()方法時,會再去調用B和C的test()方法,B和C又會去調用A的test()方法,因此A類的test()方法在理論應該會被調用兩次,例如:
class A: def test(self): print("I am in Class A"); class B(A): def test(self): A.test(self); print("I am in Class B") class C(A): def test(self): A.test(self); print("I am in Class C"); class D(B,C): def test(self): B.test(self) C.test(self) print("I am in Class D") d = D() d.test()
輸出:
I am in Class A
I am in Class B
I am in Class A
I am in Class C
I am in Class D
從上面的輸出結果能夠看出,A類的test()被調用了兩次。在有些狀況這種邏輯將會形成極大的Bug,好比一筆銀行轉帳記錄轉了兩次。要避免這種狀況,可使用super()方法,當使用super()調用父類的test方法時,會轉而去調用下一個重寫了該super類的test方法,若沒有的話纔會去調用父類的test方法。
案例:
class A: def test(self): print("I am in Class A") class B(A): def test(self): super().test() #不能替換爲A.test(self) print("I am in Class B") class C(A): def test(self): super().test() print("I am in Class C") class D(B,C): def test(self): super().test() #能夠替換爲B.test(self) print("I am in Class D") d = D() d.test()
輸出:
I am in Class A
I am in Class C
I am in Class B
I am in Class D
經過上面的結果咱們能夠看出,super.test()和父類.test(self)是不同的,在B類中使用super()調用父類的test()時,會去尋找B實例後的下一個類是否重寫A類的test()方法,因爲類D繼承了類B和類C,因此類C在類B後,並且類C又重寫了test()方法,應此會直接調用類C的test()方法。在類C的test()方法中,又使用了super().test()調用父類的test(),因此又會去尋找C實例後的下一個類有重寫A類的test方法,由於類D只繼承了B和C,而且類C是最後一個,因此C後沒有實例直接重寫A類的test()方法,所以直接去調用A類的test()方法。最終完整的方法壓棧順序是D->B->C->A。
下面的邏輯和上面案例中使用super()的邏輯是等同的:
class A: def test(self): print("I am in Class A") class B(A): def test(self): C.test(self)#調用C class C(A): def test(self): A.test(self)#調用A class D(B,C): def test(self): B.test(self)#調用B
上面的邏輯變成了如圖所示: