類的聲明:
一、類的屬性
(私有屬性和公有屬性)
(類屬性)
二、類的方法
(構造方法、析構方法、自定義方法、特殊成員方法)
(靜態方法、類方法、類屬性)
三、類的繼承
(方法和屬性的繼承,方法的重構)
(抽象類,多重繼承)
四、類的多態
(實現接口的重用)
五、類的特殊裝飾
(@staticmethod、@classmethod、@property)
六、類的來源和原類(metaclass)
七、反射
類的聲明
使用class聲明類,建議類名單詞首字母大寫。
“新式類”和“經典類”的區分在Python 3之後就已經不存在,在Python 3.x之後的版本,因爲所有的類都派生自內置類型object(即使沒有顯示的繼承object類型),即所有的類都是“新式類”。
新式類:
class Management(object): def add(): pass
經典類:
class Management: pass
類的屬性
類的屬性就是類定義的變量值。
公有屬性:在類裏直接定義的屬性,它在類名下面直接定義。
調用:1、類中調用:類名.屬性名 ,更改原公有屬性值
2、實例調用:實例.屬性名
class Management(object): num = 10 def add(self): Management.num +=10 # 類中調用公有屬性並更改值,num=11 pass s1 = Management() s2 = Management() # 第一種情況:s1實例中調用公有屬性,s2實例沒有調用公有屬性 s1.num +=1 Management.num += 2 """ <結果> s1不變,s2和Management都改變了 s1_num:11 s2_num:12 Manage_num:12 """ # 第二種情況:先調用s1實例公有屬性,再通過S1調用add更改,然後再使用類更改 s1.num +=1 s1.add() Management.num += 2 """ <結果> 先調用的s1實例num依然不變,s2和Management都被修改了 s1_num:11 s2_num:22 Manage_num:22 """
問題:爲什麼修改num的值以後,實例s1和實例s2會有不同的結果呢 ?
因爲公有屬性查找的順序是:先找實例的公有屬性,沒找到再找類裏的公有屬性
可以這樣理解:Management相當於一個微信羣,num是羣裏發的一張照片,S1和S2是羣裏面的兩個人。
情況1:S1把照片存到本地,P了個雙眼皮,S2說太難看了,我也不會P圖,不保存。這個時候發照片的Management說我也覺得難看,把圖撤回,重新發了一個P的圖。S2就只能保存最新的圖片。
情況2:S1找到Management說你的圖太醜了,重新改一下吧,Management說好!撤回圖片修改了~
私有屬性:加兩個下劃線,__membername,編譯的時候自動加上類名,變成_classname__membername,這種技術叫變量名壓縮(mangling),以達到外部不能調用的目的。實際使用_classname__membername是可以調用的,但是不符合規定。標準方法是通過定義函數來獲取。
class Classname(object): '''this is a demo!''' def __init__(self): self.__membername = 88 def read_membername(self): # 標準的外部訪問方法,使用函數來讀取私有屬性。 return self.__membername s= Classname() print(s._Classname__membername) print(s.read_membername()) ''' <結果> 88 88 ------像下面的調用,會出現AttributeError錯誤------ print(s.__membername) '''
類屬性: 類自帶的屬性,需要注意的是實例化的類屬性,和原類的類屬性不同。用上面的例子作演示。
屬性 | 作用 | 示例 | 結果 |
__doc__ | 類的文檔字符串 | print(s.__doc__) print(Classname.__doc__) | this is a demo! this is a demo! |
__dict__ | 類的屬性組成的字典 | print(s.__dict__) print(Classname.__dict__) | {'_Classname__membername': 88} {'__init__':, '__module__': '__main__', '__doc__': '\nthis is a demo!\n', 'read_membername':} |
__name__ | 類的名字(字符串) | ##不能用於實例print(s.__name__ ) print(Classname.__name__ ) | Classname |
__bases__ | 類的所有父類組成的元組 | #不能用於實例print(s.__bases__) print(Classname.__bases__) | (,)爲什麼沒有值?可能是編譯器問題 |
__module__ | 類所屬的模塊 | print(s.__module__) print(Classname.__module__) | __main__ __main__ |
__class__ | 類對象的類型 | print(s.__class__) print(Classname.__class__) | 待測 |
__slots__ | 限定類屬性,在類屬性位置定義 未在slots定義的屬性都是非法屬性 | __slots__.('name','age','sexy') | 使用'name','age','sexy'的以外屬性會報錯 |
類的方法
類的方法就是類裏面定義的函數。類的構造方法、析構方法、自定義類方法、靜態方法、類方法、屬性方法、特殊成員方法。
構造方法:__init__
實例化類的時候就會運行的函數。希望初始化的參數放置在init下面。(個人覺得,這個初始化參數可以是一切對象!)
class A(object): def instense(self): print("init obj A") class B(object): def __init__(self, para): self.init_para = para self.obj_A = A() self.num = 1 def show(self): print(self.init_para) self.obj_A.instense() print(self.num) haha = B("this is para") haha.show() ---------- this is para init obj A 1
析構方法:
__del__:銷燬實例時,方法纔會執行。
class Hello(object): def __del__(self): print("你刪除了實例") # 在python上測試 instance = Hello() del instance # 當然也可以使用實例調用,但沒有這麼用的~~ instance.__del__()
自定義方法:
除去類中自帶的以_下劃線開頭的函數,在類中定義一個函數,實現相應的功能。
class Person(object): def __init__(self,person_name, person_age)
靜態方法:
@staticmethod,不需要訪問類裏的任何參數。所帶的參數都是從外部傳入的。
class Person(object): def __init__(self,person_name, person_age): self.name = person_name self.age = person_age @staticmethod def info(country): print(country)
類方法:
@classmethod,第一個參數必須是類屬性。
class Person(object): country = "china" def __init__(self,person_name, person_age): self.name = person_name self.age = person_age @classmethod def info(country): print(country)
屬性方法:
@property把一個函數變成一個靜態屬性
直接調用函數名字,不需要加括號,就能獲取到函數返回值。一般用在不注重過程,只要結果的情況!
class Person(object): country = "china" def __init__(self,person_name, person_age): self.name = person_name self.age = person_age @property def health_point(self): print("HP:【{}】".format(self.age*2)) return self.age*2 P = Person("laowang",23) P.health_point # 不需要括號,看起來完全是一個屬性,這就是屬性方法 '''上面的類屬性只是只讀的,即然是叫屬性,那麼只讀就顯得太LOW了'''
類屬性裝飾器@property,裝飾以後,函數就有:賦值setter\銷燬deleter兩個方法。
class Person(object): country = "china" def __init__(self,person_name, person_age): self.name = person_name self.age = person_age @property def health_point(self): self.age = self.age*2 print("HP:【{}】".format(self.age)) return self.age @health_point.setter # 增加了一個賦值方法 def health_point(self, add): self.age = add @health_point.deleter # 增加了一個銷燬屬性的方法 def health_point(self): del self.age P = Person("laowang", 33) print(P.health_point) P.health_point = 22 # 給health.point賦值 print(P.health_point) del P.health_point # 銷燬屬性
特殊成員方法:
方法 | 作用 | 示例 | 結果 |
__call__ | 默認未定義 類實例化後,調用實例運行的方法 | p = Person() p() | Person是類名 實例p沒有調用函數,加()運行call方法 |
__str__ | 默認未定義,定義時必須有返回值 定義時,打印實例,輸入str返回值 | p = Person() print (p) | Person是類名 打印實例p,運行str方法,打印返回值 |
__getitem__ | 用於索引操作,如字典。獲取數據 | p = Person() p['name'] | 自動運行getitem |
__setitem__ | 用於索引操作,如字典。賦值 | p = Person() p['name'] = 'David' | 自動運行setitem |
__delitem__ | 用於索引操作,如字典。刪除數據 | p = Person() del p['name'] | 自動運行delitem |
__new__ | 類實例化時,執行__new__,並且會阻止 init運行,可以在new裏調用init | p = Person() | 參照例子二 |
__len__ | 待續 | ||
__cmp__ | 待續 |
'''例子一call\str\getitem\setitem\delitem方法''' class Person(object): def __call__(self): print("print call") def __str__(self): print("print str:",end='') return "1" def __getitem__(self,key): print("getitem:",key) def __setitem__(self,key,value): print('setitem:',key,value) def __delitem__(self,key): print('delitem:',key) p = Person() p() print(p) print('-----------') get = p['name'] p['name'] = 'David' del p['name']
'''例子二:__new__/__init__''' class Person(object): def __init__(self): print('this is init!') def __new__(self): # __new__會阻斷__init__執行,要想執行,需要寫init方法 print("this is new!") self.__init__(self) # 如果去掉這一行,init不能執行 p = Person()
類的來源和元類:
http://blog.jobbole.com/21351/中文版詳細解答。下面寫一個自己理解的簡版的。
首先,類也是對象,可以:
1) 你可以將它賦值給一個變量
2) 你可以拷貝它
3) 你可以爲它增加屬性
4) 你可以將它作爲函數參數進行傳遞
類也是是由type()這個函數創建的,type是類的類,類的爹。學名叫元類!
也許有人會問那爲啥type()能查看到數據類型呢?
可能你會注意到,type的結果前是class。。。因爲數據類型在Python中都是類定義的,這也說明了,爲什麼數字,字符等等全是對象。
type格式:
type(類名,(父類元組),{屬性和方法的字典}) , 父類元組可以沒有;後面兩個典省略時,默認值爲None。
'''一言不合就上例子''' Person = type('Person',(),{'country':'china'}) print(Person.country) # 帶繼承Person類的用法 def run(): # 定義函數,把run傳給Action的leg方法。 print('running...') Action = type('Action',(Person,),{'leg':run}) print(Action.country) Action.leg()
metaclass:
創建類的時候,使用metaclass,python就會使用指定的元類創建類。找不到則使用type創建。
class MyType(type): def __init__(self,*args,**kwargs): print("Mytype __init__",*args,**kwargs) def __call__(self, *args, **kwargs): print("Mytype __call__", *args, **kwargs) obj = self.__new__(self) print("obj ",obj,*args, **kwargs) print(self) self.__init__(obj,*args, **kwargs) return obj def __new__(cls, *args, **kwargs): print("Mytype __new__",*args,**kwargs) return type.__new__(cls, *args, **kwargs) print('here...') class Foo(object,metaclass=MyType): def __init__(self,name): self.name = name print("Foo __init__") def __new__(cls, *args, **kwargs): print("Foo __new__",cls, *args, **kwargs) return object.__new__(cls) f = Foo("Alex") print("f",f) print("fname",f.name)
類的繼承
代碼重用。
Python3中類有單繼承和多繼承,其中多繼承與Python2的繼承順序不同。
子類方法與父類重名,子類方法會覆蓋父類
單繼承:
'''一言不合就上例子''' class Person(object): def __init__(self,person_name): self.name = person_name def info(self): print("this is personal info:\nname:{}".format(self.name)) class Action(Person): def __init__(self,person_name): '''父類析構函數有兩種繼承方法''' super(Action, self).__init__(person_name) # 方法一 # Person.__init__(self, person_name) # 方法二 def run(self): print("{} is running...".format(self.name)) a = Action('Alex') a.run() # 繼承了Person的屬性。 a.info() # 繼承了Person的方法。
多重繼承:
不能同時繼承兩父親類和爺爺類比如B繼承A,C繼承時就不能同時寫A和B,只寫B就可以了。
class ROOT(object): def __init__(self): print("this is root") def action(self): print("ROOT is action...") class A(ROOT): def __init__(self): super(A, self).__init__() print ('init A...') def action(self): print("A is action...") class B(ROOT): def __init__(self): super(B, self).__init__() print ('init B...') def action(self): print("B is action...") class C(A): def __init__(self): super(C, self).__init__() print ('init C...') def action(self): print("C is action...") class D(B,C): def __init__(self): super(D, self).__init__() print ('init D...') def action(self): print("D is action...") d = D()
要理解繼承的順序,因爲當方法重名時,順序靠後的方法覆蓋前面的方法。
類的繼承關係: D(A,B)實例化時候的繼承先後順序:先找B(及其父類),再找C(及其父類)
類的多態
多態就是同一個父類的方法,不同子類繼承可以進行不同的改寫,實現多種不同的功能。
任何依賴父類作爲參數的函數或者方法都可以不加修改地正常運行,原因就在於多態。
'''接着上面的例子''' # 定義一個函數,把ROOT類傳進裏面 def who_action(class_name): class_name.action() who_action(ROOT()) who_action(D()) who_action(A())