【Python】解析Python中类的使用
目錄結構:
contents structure [-]
- 類的基本使用
- 專有方法
- 繼承
- 單重繼承
- 多重繼承
- 磚石繼承
1.類的基本使用
下面是類使用的一個簡單案例,
class person:"person 類的描述信息"def __init__(self,name="",age=0):self.name = nameself.age = agedef setName(self,name):'''設置名稱'''self.name = namedef getName(self):'''獲取名稱'''return self.namedef setAge(self,age):self.age = agedef getAge(self):return self.agedef __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參數,可以直接定義到類中。類屬性可以直接通過類名調用。
class Person:type = "哺乳類"def __init__(self,name,age):self.name = nameself.age = agep = Person("luyis",13) #通過類名調用類屬性 print(Person.type) #通過對象調用類屬性 print(p.type)
?
靜態方法
靜態方法是類中的函數,不需要實例。靜態方法主要是用來存放邏輯性的代碼,主要是一些邏輯屬于類,但是和類本身沒有交互,即在靜態方法中,不會涉及到類中的方法和屬性的操作。用 @staticmethod 裝飾的不帶 self 參數的方法,類的靜態方法可以沒有參數,可以直接使用類名調用。
import time class TimeTest:def __init__(self):pass#靜態方法 @staticmethoddef show_time():print(time.strftime("%H:%M:%S", time.localtime()))TimeTest.show_time()
?
?
類方法
類方法是將類本身作為對象進行操作的方法。默認有個 cls 參數,可以被類和對象調用,需要加上 @classmethod 裝飾器。
class ClassTest:num = 0def __init__(self,num):ClassTest.num = num#類方法 @classmethoddef showNum(cls):print(cls.num)c = ClassTest(20) #通過類直接調用 ClassTest.showNum() #通過實例對象調用 c.showNum()
?
?
私有成員
Python中的私有成員,不能在類外面直接訪問。私有成員只需要在成員前面加上兩條下劃線(__)就表明該成員是私有的了。
class Person:#__type是私有成員__type = "哺乳類"def __init__(self,name="",age=0,country="中國"):#__name,__age是私有成員self.__name = nameself.__age = ageself.__setcountry(country)def get_name(self):return self.__namedef get_age(self):return self.__agedef set_name(self,name):self.__name = namedef set_age(self,age):self.__age = age#__setcountry是私有函數def __setcountry(self,cty):self.country = cty@classmethoddef get_type(cls):return cls.__type@classmethoddef set_type(cls,type):cls.__type = typep = 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中不存在嚴格意義上的私有成員。
class Person:#__type是私有成員__type = "哺乳類"def __init__(self,name="",age=0):#__name,__age是私有成員self.__name = nameself.__age = agep = 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 ?
?
2.專有方法
Python除了自定義私有變量和方法外,還可以定義專有方法。專有方法是在特殊情況下或使用特殊語法時由python調用的,而不是像普通方法一樣在代碼中直接調用??吹叫稳鏮_XXX__的變量或函數名時就需要注意下,這在python中是有特殊用途的,下面是Python中的部分專用方法:
__init__ : 構造函數,在生成對象時調用
__del__ : 析構函數,釋放對象時使用
__str__: 打印,轉換,適合于展示給用戶看的
__repr__ : 打印,轉換,適合開發者調試
__setitem__ : 按照索引賦值
__getitem__: 按照索引獲取值
__len__: 獲得長度
__call__: 函數調用
__add__: 加運算
__sub__: 減運算
__mul__: 乘運算
__div__: 除運算
__mod__: 求余運算
__pow__: 乘方
__doc__: 說明文檔信息
上面筆者已經講解過__init__和__del__函數的使用了,應此筆者不再這里重復綴述了。筆者接下來講解其它的專有方法的用法:
?
__str__,__repr__
都是用于將對象轉化為字符串的內置方法。
但是 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 1a string^ SyntaxError: unexpected EOF while parsing
?
repr返回的字符串應該是編譯器能夠再次解析的,str返回的字符串應該可以是易閱讀的
在知道了repr和str的區別后,我們就知道該如何自定義__repr__和__str__函數了。
class person:def __init__(self,name="",age=0):self.__name = nameself.__age = agedef __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)
?
?
__setitem__,__getitem__
用于通過下標來設置和獲取值,可以直接使用[]符來操作。
class DicDecorate:def __init__(self,dic):self.__dic = dicdef __getitem__(self,key):return self.__dic[key]def __setitem__(self,key,val):self.__dic[key] = valdicdec = DicDecorate({})dicdec[0] = "hello" dicdec[1] = "word"print(dicdec[0]) print(dicdec[1])
__len__():
當調用len函數時會調用該方法。
lass DicDecorate:def __init__(self,dic):self.__dic = dicdef __len__(self):return len(self.__dic);dicdec = DicDecorate({}) print(len(dicdec))
?
__call__()
關于 __call__ 方法,不得不先提到一個概念,就是可調用對象(callable),我們平時自定義的函數、內置函數和類都屬于可調用對象,但凡是可以把一對括號()應用到某個對象身上都可稱之為可調用對象,判斷對象是否為可調用對象可以用函數 callable。
如果在類中實現了 __call__ 方法,那么實例對象也將成為一個可調用對象。
class Entity:def __init__(self, x, y):self.x, self.y = x, ydef __call__(self, x, y):self.x, self.y = x, ydef __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)
?
__add__: 加運算 __sub__: 減運算 __mul__: 乘運算 __div__: 除運算 __mod__: 求余運算 __pow__: 冪運算
class Vector:def __init__(self, a, b):self.a = aself.b = bdef __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)
?
?
3. 繼承
3.1 單重繼承
Python繼承的語法格式:
class ClassName1(ClassName2):statement
其中
ClassName1:派生類
ClassName2:基類
class Shape:def __init__(self,type,area):self.type = typeself.area = areadef 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())
和其它編程語言一樣,可以重寫父類中的方法,可以繼承父類中開放的屬性和方法(不能繼承父類中的私有屬性和方法)。
?
3.2 多重繼承
除了單重繼承,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 通過上面的結果可以看出,子類在父類中查找方法的順序是從左到右的。
?
3.3 磚石繼承
磚石繼承問題是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
上面的邏輯變成了如圖所示:
轉載于:https://www.cnblogs.com/HDK2016/p/10964792.html
總結
以上是生活随笔為你收集整理的【Python】解析Python中类的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 找原头,最好是高清的
- 下一篇: 求一个好听的家人微信群名字。