大師兄的Python學習筆記(一): 基礎篇
大師兄的Python學習筆記(三): 模塊和包
三、面向過程和面向對象的思維方式
1.面向過程(Proceduce Oriented)
如果想吃西紅柿炒雞蛋:
step1: 打雞蛋
step2: 切西紅柿
step3: 熱鍋
step4: 炒雞蛋
step5: 炒西紅柿
step6: 放鹽
step7: 出鍋這種把任務切分成一系列子工作,自頂向下一步一步實現的方式叫做面向過程。
2. 面向對象(Object Oriented)
- 如果想吃西紅柿炒雞蛋:
step1: 設計一個飯館的藍圖(類)
飯館包含食材:西紅柿、雞蛋
飯館包含調料:鹽
飯館包含炊具:鍋
飯館包含功能:炒菜
step2: 根據藍圖建造一個飯館(對象)
step3: 點西紅柿炒雞蛋這道菜- 這里我們實際上是將制作西紅柿炒雞蛋的過程封裝到對象飯館中。
- 吃飯的人只需要對廚房下達命令,至于功能的實現方式則不用關心。
- 這種將功能抽象封裝的方式叫做面向對象。
3.面向對象的優缺點
1)易復用
建立廚房后,如果為一萬人提供西紅柿炒雞蛋,我們只需要下達一萬次命令即可,不用重復造輪子。
2)易維護
如果想吃黃瓜炒雞蛋,只需將食材中的西紅柿替換成黃瓜,不用重新設計整個流程。
3)易擴展
如果想吃西紅柿雞蛋蓋飯,只需要增加食材大米和蒸飯的功能,而并不需要重建一個廚房。
4)缺點:性能低于面向過程
如果只是一個人吃飯,蓋飯館就很浪費。
4.總結
1)面向過程適合簡單、少量的任務。
2)面向對象適合復雜、大量的任務。
3)面向對象并不能取代面向過程,面向對象的最底層是面向過程。
四、類和對象
1.類
- 類就是具備某些共同特征的實體的集合,它是一種抽象的數據類型,它是對所具有相同特征實體的抽象。
- 比如:西紅柿和雞蛋都屬于食材,食材是個抽象的概念,是一個類。
2.對象
- 對象是具體的事物,單個的個體。
- 比如:西紅柿和雞蛋可以是對象、鹽和鍋可以是對象、西紅柿炒雞蛋可以是對象,廚房也可以是對象。
3.類和對象的關系
類是對象的概括,對象是類的具體體現
五、在Python中實現類和對象
1.類的聲明
- 用class關鍵字。
- 類的成員只可以是屬性或方法。
- 屬性可以看理解為類中的變量。
- 方法可以看理解為類中的函數。
class <類名>(): <類的成員> # 這里必須有縮進
>>>class FoodMaterial(): >>> price = 10 # 屬性 >>> def cut(): # 方法 >>> print('我被切成片了!')
2.類的實例化
- 也就是將類實例化成為對象
<變量> = <類名>()
>>>class FoodMaterial(): >>> price = 10 # 屬性 >>> def cut(): # 方法 >>> print('我被切成片了!') >>>tomato = FoodMaterial() # 這里將類實例化后存儲在變量中
3. 訪問類和對象的成員
1)訪問類的屬性和調用方法
方法一: <類名>.<屬性名> # 訪問屬性 <類名>.<方法名>() # 調用方法 方法二: getattr(<類名>,"<屬性名>") getattr(<類名>,"<方法名>")()
>>>class FoodMaterial(): >>> price = 10 >>> def cut(): >>> print('我被切成片了!') >>>print(FoodMaterial.price) # 訪問屬性 >>>10 >>>FoodMaterial.cut() # 調用方法 >>>我被切成片了!
2) 訪問對象的屬性和方法
方法一: <對象名>.<屬性名> # 訪問屬性 <對象名>.<方法名>() # 調用方法 方法二: getattr(<對象名>,"<屬性名>") getattr(<對象名>,"<方法名>")()
>>>class FoodMaterial(): >>> price = 10 >>> def cut(self): # self我們后面說 >>> print('我被切成片了!') >>>tomato = FoodMaterial() >>>print(tomato.price) # 訪問屬性 >>>10 >>>tomato.cut() # 調用方法 >>>我被切成片了!
4. 修改類和對象的屬性
1)修改類的屬性
方法一:<類名>.<屬性名> = <值> # 修改類屬性 方法二:setattr(<類名>,"<屬性名>",<值>)
>>>class FoodMaterial(): >>> price = 10 >>>tomato = FoodMaterial() >>>FoodMaterial.price = 20 >>>print(FoodMaterial.price) 20 >>>print(tomato.price) # 這時對象的屬性也跟著改變了 20
2)修改對象的屬性
- 如果對象的屬性被修改過,就不會跟著類的屬性同步了。
方法一:<對象名>.<屬性名> = <值> # 修改對象屬性 方法二:setattr(<對象名>,"<屬性名>",<值>)
>>>class FoodMaterial(): >>> price = 10 >>>tomato = FoodMaterial() >>>tomato.price = 20 >>>print(tomato.price) 20 >>>print(FoodMaterial.price) # 這時類的屬性并沒有改變 10 >>>FoodMaterial.price = 30 # 如果這時我們再修改類的屬性 >>>print(tomato.price) # 對象的屬性不會再跟著改變 20
5. 增加類和對象的屬性
1)增加類的屬性
方法一:<類名>.<屬性名> = <值> 方法二:setattr(<類名>,"<屬性名>",<值>)
>>>class FoodMaterial(): >>> price = 10 >>>tomato = FoodMaterial() >>>FoodMaterial.colour = 'red' >>>print(FoodMaterial.colour) red >>>print(tomato.colour) # 這時對象的屬性也跟著增加了 red
2)增加對象的屬性
方法一:<對象名>.<屬性名> = <值> # 增加對象屬性 方法二:setattr(<對象名>,"<屬性名>",<值>)
>>>class FoodMaterial(): >>> price = 10 >>>tomato = FoodMaterial() >>>tomato.colour = 20 >>>print(tomato.colour) red >>>print(FoodMaterial.colour) # 這時類的屬性并沒有增加 AttributeError: type object 'FoodMaterial' has no attribute 'colour' >>>FoodMaterial.colour = 'yellow' # 如果這時我們再修改類的屬性 >>>print(tomato.colour) # 對象的屬性不會改變 red
6. 刪除類和對象的屬性
1)刪除類的屬性
方法一:del <類名>.<屬性名> # 刪除類屬性 方法二:delattr(<類名>,"<屬性名>")
>>>class FoodMaterial(): >>> price = 10 >>> colour = 'red' >>>tomato = FoodMaterial() >>>del FoodMaterial.price >>>print(FoodMaterial.price) # 刪除屬性 AttributeError: type object 'FoodMaterial' has no attribute 'price' >>>print(tomato.colour) # 這時對象的屬性也被刪除了 AttributeError: type object 'FoodMaterial' has no attribute 'price'
2)刪除對象的屬性
- 從下面的例子就可以看出,對象的屬性在沒有單獨定義的情況下,實際上是調用了類的屬性
- 如果單獨為對象創建屬性,在屬性名相同的情況下,優先調用對象的屬性
方法一:del <對象名>.<屬性名> # 刪除對象屬性 方法二:delattr(<對象名>,"<屬性名>")
>>>class FoodMaterial(): >>> price = 10 >>> colour = 'red' >>>tomato = FoodMaterial() >>>del tomato.price # 這里會提示tomato并沒有price屬性,實際上是調用的FoodMaterial得price屬性 AttributeError: price >>>tomato.price = 20 # 為對象增加屬性 >>>print(tomato.price) # 覆蓋了類的屬性 20 >>>del tomato.price # 刪除了對象的屬性 >>>print(tomato.price) # 這里又調用了類的屬性 10 >>>del FoodMaterial.price # 如果這時我們將類的屬性也刪除掉 >>>print(tomato.price) # 無法調用屬性,所以報錯了 AttributeError: 'FoodMaterial' object has no attribute 'price'
7.關于self
- self是類和對象中的特一個特殊的參數,代表類本身。
- self只在類和對象中存在。
- self不需要賦值。
- self是類中默認的第一個參數。
- 其實self并不是系統指定的關鍵字,他可以叫任何名字,只要他是類中的第一個參數就可以。
1)在類中使用self參數- 很少直接使用類中的self參數
>>>class FoodMaterial(): >>> def cut(self) : >>> print('我被切成片了!') >>>FoodMaterial.cut(FoodMaterial) # 這時需要將類自身作為參數返回,不然會報錯。 我被切成片了!
- 因為對象也屬于類,所以可以用對象作為self參數。但是這種用法比較奇葩,不推薦使用。
>>>class FoodMaterial(): >>> def cut(self) : >>> print('我被切成片了!') >>>tomato = FoodMaterial() >>>FoodMaterial.cut(tomato) # 這里將方法當做參數代表類本身,也可以正常運行 我被切成片了!
- 如果不使用self名稱,也沒有任何區別,但不推薦這樣使用。
>>>class FoodMaterial(): >>> def cut(somethingbutnotself) : # 證明self并不是關鍵字 >>> print('我被切成片了!') >>>FoodMaterial.cut(FoodMaterial) 我被切成片了!
2)在對象中使用self參數
- 在調用對象方法時,會默認將自身作為第一個參數返回。所以如例(3.2)中,如果在沒有為方法添加self參數,而直接用對象中的該方法會報錯。
>>>class FoodMaterial(): >>> def cut() : >>> print('我被切成片了!') >>>tomato = FoodMaterial() >>>tomato.cut() # 這里會提示傳入了一個參數,但是cut()不需要傳入參數 TypeError: cut() takes 0 positional arguments but 1 was given >>># 正確的調用方式 >>>class FoodMaterial(): >>> def cut(self) : >>> print('我被切成片了!') >>>tomato = FoodMaterial() >>>tomato.cut() # tomato會默認將自身作為第一個參數傳入方法 我被切成片了!
- 在對象中self改變了變量和函數的作用域,成為對象內的全局變量。
>>>class FoodMaterial(): >>> def __init__(self): # __init__是魔法函數,這里可以先理解為一個在對象創建時自動執行的方法。 >>> self.price = 10 # 這里self.price成為對象內的全局變量 >>> def getPrice(self) : >>> print('我的價格是:{price}'.format(price=self.price)) # 調用了方法內的變量self.price,證明其作用域是全局 >>>tomato = FoodMaterial() >>>tomato.getPrice() 我的價格是:10
8.Python中的構造函數
- 構造函數是一種特殊的方法,用來在創建對象時為對象賦值。
- Python中構造函數的方法是:
def __init__(self,arg1,arg2)
- 構造函數只能調用一次,就是在創建類的時候調用
- __init__ 其實是一個魔法函數,后面會介紹。
>>>class FoodMaterial(): >>> def __init__(self,price,colour): # 構造函數,參數在創建類時需要提供 >>> self.price = price >>> self.colour = colour >>> self.show() # 在創建類時就要執行的方法 >>> def show(self): >>> print(self.price) >>> print(self.colour) >>>tomato = FoodMaterial(price=10,colour='red') # 這里并沒有再專門調用show(),在創建時就自動執行了。 10 red >>>print(FoodMaterial.price) # 直接用類訪問不到構造函數中的成員,因為構造函數只在創建對象時執行。 AttributeError: type object 'FoodMaterial' has no attribute 'price'
六、面向對象的三大特征
- 封裝
- 繼承
- 多態
1. 封裝
- 封裝就是對對象的成員進行訪問限制,是為了保護私隱,明確區分內外。
- 封裝的級別分為:公開(public)、受保護的(protected)、私有的(private)
- 與Java不同,Python中沒有public,private,protected的關鍵字
- Python中的封裝并不是嚴格的語法,更像是一種規范。
1)公開的(public)
公共的封裝實際對成員沒有任何操作,任何地方都可以訪問。
2)受保護的(protected)
- 受保護的成員名稱前增加一個下劃線_。
- 受保護的封裝在類中或者子類中都可以訪問,但在外部不允許訪問。
- 我測試受保護的成員實際上是可以直接訪問的,應該只是一種書寫規范,希望有人可以指正我。
_<受保護的成員名>
>>>class FoodMaterial(): >>> _price = 10 # 受保護的屬性 >>> def _getPrice(self) : # 受保護的方法 >>> print('我的價格是:{price}'.format(price=self.price))
3)私有的(private)
- 私有員名稱前增加兩個劃線__。
- 私有成員只能在當前類或對象中訪問。
- Python中的私有實現方式是一種被稱為name mangling的障眼法。
__<私有的成員名>
>>>class FoodMaterial(): >>> __price = 10 # 私有的屬性 >>> def __getPrice(self) : # 私有的方法 >>> print('我的價格是:{price}'.format(price=self.__price)) >>>tomato = FoodMaterial() # 實例化 >>>print(tomato.__price) # 注意,這里的報錯是:不存在這個值!。 AttributeError: 'FoodMaterial' object has no attribute '__price' >>>tomato.__getPrice() AttributeError: 'FoodMaterial' object has no attribute '__getPrice'
- 用魔法函數
__dict__
查看類的全部成員,可以看出私有成員實際是被改名為:_<類名>__<成員名>
>>>print(FoodMaterial.__dict__) >>>mappingproxy({'__module__': '__main__', >>> '_FoodMaterial__price': 10, # 這里可以看到被改名了 >>> '_FoodMaterial__getPrice': <function __main__.FoodMaterial.__getPrice(self)>, >>> '__dict__': <attribute '__dict__' of 'FoodMaterial' objects>, >>> '__weakref__': <attribute '__weakref__' of 'FoodMaterial' objects>, >>> '__doc__': None}) >>>print(tomato._FoodMaterial__price) # 試著訪問改名后的成員 10 >>>print(tomato._FoodMaterial__getPrice()) 我的價格是:10
2. 繼承
1)繼承的作用:
- 繼承就是一個類可以獲得另外一個類中的成員屬性和成員方法。
- 繼承可以減少代碼,增加代碼的復用功。
- 繼承可以建立類與類直接的關系。
2)繼承與被繼承的概念:
- 被繼承的類叫父類,也叫基類,也叫超類。
- 用于繼承的類,叫子類,也叫派生類。
- 所有的類全都是object的子類,繼承時如果不特別指定父類,將默認繼承object類。
3)繼承的語法
class <子類名>(<父類名>): <類的內容>
>>># 父類 >>>class Dog(object): # 這里不寫object也會默認繼承object >>> type = 'dog' >>> legs = 4 >>> def bark(self): >>> print('汪汪!') >>># 子類 >>>class Schnauzer(Dog): # schnauzer繼承了dog類 >>> sub_type = 'schnauzer'
4)繼承的特性
- 子類一旦繼承父類,則可以使用父類中除私有成員外的所有內容。
- 子類繼承父類后并沒有將父類成員完全賦值到子類中,而是通過引用關系訪問調用。
- 子類中可以定義獨有的成員屬性和方法。
- 子類中定義的成員和父類成員如果相同,則優先使用子類成員。
>>># 父類 >>>class Dog(object): # 這里不寫object也會默認繼承object >>> type = 'dog' >>> def bark(self): >>> print('汪汪!') >>> def __showLegs(self): # 私有成員不會被繼承 >>> print(4) >>># 子類 >>>class Schnauzer(Dog): # schnauzer繼承了dog類 >>> sub_type = 'schnauzer' >>> def bark(self): # 這里覆蓋了父類的方法 >>> print('嗷嗷1') >>>qq = Schnauzer() >>>print(qq.type) # 繼承了父類的屬性 'dog' >>>print(qq.bark()) # 同名時首先引用了自己的方法 嗷嗷! >>>print(qq.sub_type) # 有了自己的屬性 'schnauzer' >>>print(qq.__showLegs()) # 私有變量不會被繼承 AttributeError: 'schnauzer' object has no attribute '__showLegs'
5)super函數
- super()函數用于調用父類的一個方法。
super().<父類方法名>() = <父類名>.<父類方法名>()
self(<父類名>,self).<方法名>(參數) # 意思是將父類的方法轉換為自己的方法。 self().<方法名>(參數) # python3.0中可以省略self()中的內容,但多重繼承中還是需要。
>>># 父類 >>>class Dog(): >>> def bark(self): >>> print('汪汪!') >>># 子類 >>>class Schnauzer(Dog): # schnauzer繼承了dog類 >>> def barkTwice(self): >>> super().bark() # 這里調用了父類的方法 >>> print('嗷嗷!') >>>qq = Schnauzer() >>>qq.barkTwice() 汪汪! 嗷嗷!
6)繼承的順序
- 優先查找自己的函數
- 沒有則查找父類
- 如果父類沒有則查找父類的父類
>>># 父類 >>>class Dog(): >>> type = 'dog' >>># 子類 >>>class Schnauzer(Dog): >>> pass >>># 孫類 >>>class BabySchnauzer(Schnauzer): >>> pass >>>qq = BabySchnauzer() >>>print(qq.type) # 繼承了祖父類的屬性s 'dog'
7)繼承構造函數
- 如果定義了構造函數,則實例化時使用構造函數。
- 如果沒定義,則自動查找父類構造函數。
- 如果重寫了構造函數,還要繼承父類的構造方法,可以使用 super()方法。
>>># 父類 >>>class Dog(): >>> def __init__(self,legs): >>> self.type = 'dog' >>> def fatherType(self): >>> print('father type is {type}'.format(type=self.type)) >>># 子類 >>>class Schnauzer(Dog): >>> def __init__(self,legs): >>> super().__init__(self,legs) # 這里調用了父類的構造函數,將參數傳入 >>> def sonType(self): >>> print('son type is {type}'.format(type=self.type)) >>>qq = Schnauzer(4) >>>qq.sonType() son type is dog >>>qq.fatherType() # 將參數傳給父類的方法,并調用方法 father type is dog
8)多繼承
- 多繼承就是子類同時繼承多個類,方法是:
def <子類名>(<父類一>,<父類二>):
- 如果兩個父類的屬性相同,則繼承寫在前面的父類。
- 可以通過inspect.getmro(<類名>)查看類的MRO列表(繼承順序)
>>># 父類一· >>>class Human(): >>> type = 'human' >>> def introHuman(self): >>> print('i am human!') >>>class Fish(): >>> type = 'fish' >>> def introFish(self): >>> print('i am fish!') >>>class Fishman(human,fish): # 多個父類 >>> pass >>>f = Fishman() >>>print(f.introHuman()) # 同時繼承了兩個父類的方法 i am human! >>>print(f.introFish()) i am fish! >>>print(f.type) # 屬性名相同則繼承前面的父類 human >>>import inspect # 導入inspect包 >>>inspect.getmro(fishman) # 查看fishman類的mro (__main__.fishman, __main__.human, __main__.fish, object)
- 單繼承和多繼承的對比:
/ 單繼承 多繼承 優點 傳承有序邏輯清晰語法簡單隱患少 類的功能擴展方便 缺點 功能擴展性受父類限制 繼承關系混亂
- 經典鉆石繼承問題:當一個孫類同時繼承多個父類,而這些父類又繼承同一個祖父類時,如果孫類同時繼承多個父類的構造函數,祖父類構造函數會被初始化多次。
# 祖父類 >>>class Grandpa(object): >>> def __init__(self): >>> print('from grandpa') # 父類1 >>>class Dad1(Grandpa): >>> def __init__(self): >>> grandpa.__init__(self) >>> print('from dad1') # 父類2 >>>class Dad2(Grandpa): >>> def __init__(self): >>> grandpa.__init__(self) >>> print('from dad2') # 子類 >>>class Son(Dad1, Dad2): >>> def __init__(self): >>> dad1.__init__(self) >>> dad2.__init__(self) # 同時調用兩個父類的構造函數 >>> print('from son') >>>s = Son() from grandpa from dad1 from grandpa from dad2 from son
- 鉆石繼承問題解決方案:使用super()函數
# 祖父類 >>>class Grandpa(object): >>> def __init__(self): >>> print('from grandpa') # 父類1 >>>class Dad1(Grandpa): >>> def __init__(self): >>> super().__init__() >>> print('from dad1') # 父類2 >>>class Dad2(Grandpa): >>> def __init__(self): >>> super().__init__() >>> print('from dad2') # 子類 >>>class Son(dad1, dad2): >>> def __init__(self): >>> super().__init__() #這里同時繼承了兩個父類的構造函數 >>> print('from son') >>>s = Son() # 這里祖父類只調用了一次 from grandpa from dad1 from dad2 from son
3. 多態
1)理解多態
- 首先要理解創建一個類就是創建了一種數據類型,和list,dict是一樣的。
>>>a = list() >>>type(a) list >>>isinstance(a,list) # a 屬于list類型 True >>>class Animal(): >>> pass >>>dog = Animal() >>>type(dog) __main__.Animal >>>isinstance(dog,Animal) # dog是animal數據類型 True
- 多態是指同一個對象在不同情況下有不同的狀態出現。
- 多態是一種思想,而不是一種語法。
- 多態增加了程序的靈活性和擴展性。
>>>class Animal(): # 創造動物的數據類型 >>> pass >>>class Dog(Animal): # 狗屬于動物 >>> pass >>>class Cat(Animal): # 貓也屬于動物 >>> pass >>>qq = Dog() >>>isinstance(qq,Dog) True >>>isinstance(qq,Animal) # qq即屬于狗,也屬于動物 True >>>mimi = Cat() >>>isinstance(mimi,Cat) True >>>isinstance(mimi,Animal) # mimi即屬于貓,也屬于動物 True
2)多態性
- 指具有不同功能的函數可以使用相同的函數名,這樣就可以用一個函數名調用不同內容的函數。
- 多態性是一鐘調用方式,
>>>class Animal(): >>> def talk(self): >>> pass >>>class Human(): >>> def talk(self): >>> print('你好!') >>>class Dog(): >>> def talk(self): >>> print('汪汪!') >>>class Cat(): >>> def talk(self): >>> print('喵喵!') >>> ppl = Human() >>> pp = Dog() >>> mimi = Cat() >>> Human.talk() 你好! >>> Dog.talk() 汪汪! >>> Cat.talk() 喵喵!
3)mixin模式
- mixin模式通過多繼承來實現,子類通過繼承Mixin類獲得一種功能。
- Mixin類用
<類名 + Mixin>
的方式起名,但這不是語法,而是告訴后來讀代碼的人,這是一個Mixin類。- 一個Mixin類可以理解為一種功能,且只能是一種功能,如果有多個功能則創建多個Mixin類。
- Mixin不能依賴于子類的實現
- 子類及時沒有繼承這個Mixin類, 也能照樣工作,只是缺少了某個功能
>>>class Animal(): >>> pass >>>class FlyMixin(): # Mixin類 >>> def fly(self): >>> print('I can fly!') >>>class Bird(Animal,FlyMixin): # 鳥既是動物,也能飛 >>> pass >>>kiki = Bird() # kiki作為bird類,獲得了fly能力 >>>kiki.fly() I can fly!
4)mixin模式的優點
- 使用Mixin可以在不對類進行任何修改的情況下,擴充功能
- 可以方便的組織和維護不同功能組件的劃分
- 可以根據需要任意調整功能類的組合
- 可以避免創建很多新的類,導致類的繼承混亂
七.類的成員描述符
(這里如果暫時看不懂請先看下一章魔法函數)
- 類的成員描述符是為了在類中,對類的成員屬性進行相關操作而創建的一種方式,大部分屬于數據清洗。
- 屬性有三種操作:get獲取屬性值、set修改或添加屬性、delete刪除屬性。
- 實現成員描述符包括三種方法:描述器、屬性修飾符、property函數。
1. 描述器
- 一個類只要實現了__get__,__set__,__delete__中任意一個方法,我們就可以叫它描述器
- 如果僅僅實現了__get__.就是非數據描述器 non-data descriptor
- 同時實現了__get__,__set__,就是數據描述器 data descriptor
1)__get__
不管是類還是實例去訪問,默認都獲得的是__get__的返回值,但是,如果中間有任何一次重新賦值,那么,這個實例獲得的是新的值(對象),已經和原來的描述器完全脫離了關系。>>>class Tomato(): >>> def __get__(self,obj,cls): >>> print('here inside __get__ !') >>>class MyClass(): >>> t = Tomato() #類或實例默認獲得__get__的返回值 >>>m = MyClass() >>>m.t # 調用了__get__ here inside __get__ !
2)__set__
后期通過實例對描述器進行賦值,那么訪問的是__set__,并且永遠關聯起來。但是如果通過修改類屬性的方式復制,那么也會被重新獲取新的值(對象)。>>>class Tomato(): >>> def __set__(self,obj,cls): >>> print('here inside __set__ !') >>>class MyClass(): >>> t = Tomato() #類或實例默認獲得__get__的返回值 >>>m = MyClass() >>>m.t = 'hello' # 賦值時調用__set__ here inside __set__ !
3)__delete__
采用del刪除屬性時,觸發__delete__。>>>class Tomato(): >>> def __delete__(self,obj): >>> print('here inside __delete__ !') >>>class MyClass(): >>> t = Tomato() >>>m = MyClass() >>>del m.t # 用del刪除屬性時,觸發__delete__ here inside __delete__ !
2. 屬性修飾符
- 屬性修飾符包括:__getattribute__()、__getattr__()、__setattr__()、__delattr__()
1)__getattribute__()
調用屬性或方法時,會先強制調用 getattribute 方法,然后再傳回屬性的值或者是 函數(方法)的引用。>>>class Tomato(): >>> def __init__(self): >>> name = 'tomato' >>> def __getattribute__(self, item): >>> print('inside __getattribute__!') >>>t = Tomato() >>>t.name # 調用屬性時觸發__getattribute__ inside __getattribute__!
2)__getattr__()
在實例以及對應的__dict__中查找屬性失敗,那么會調用__getattr__函數>>>class Tomato(): >>> def __getattr__(self, item): >>> print('inside __getattr__!') >>>t = Tomato() >>>t.name # 這里name并不存在,所以觸發__getattr__ inside __getattr__!
3)__setattr__()
當為類屬性賦值時,調用__setattr__()語句>>>class Tomato(): >>> def __setattr__(self, item,value): >>> print('inside __setattr__!') >>>t = Tomato() >>>t.name = 'tomato' # 雖然和上個例子看起來相似,但其實這里時因為賦值觸發__setattr__ inside __setattr__!
4)__delattr__()
只要使用del語句刪除屬性,就會調用這個方法>>>class Tomato(): >>> def __init__(self): >>> self.name = 'tomato' >>> def __delattr__(self, item): >>> print('inside __delattr__!') >>>t = Tomato() >>>del t.name # 用del刪除元素時觸發__delattr__ inside __delattr__!
3. property函數
- property() 函數的作用是在新式類中返回屬性值。
- 使用語法
property(<get方法>,<set方法>,<del方法>,<屬性描述>)
。- get、set和del方法對應描述器的三個方法。
- 三個方法的名字可以任意取,但是在property()中要按順序一一對應。
>>>class Tomato(): >>> def __init__(self): >>> self._name = 'tomato' # 創建protected屬性 >>> def get_name(self): # 創建屬性的get方法 >>> print('here inside get_name') >>> return self._name >>> def set_name(self, value): # 創建屬性的set方法 >>> print('here inside set_name') >>> self._name = value >>> def del_name(self): # 創建屬性的del方法 >>> print('here inside del_name') >>> del self._name >>> name = property(get_name, set_name, del_name, "I'm the 'name' property.") # 為name屬性附加property >>>t = Tomato() >>>print(t.name) # 觸發get_name() here inside get_name 'tomato' >>>t.name = 'Tomato' # 觸發set_name() here inside set_name >>>del t.name # 觸發del_name() here inside del_name >>>help(t.name) # 觸發描述 here inside get_name
- 用裝飾器實現property(如果對裝飾器不熟悉可以先跳過):
>>>class Tomato(): >>> def __init__(self): >>> self._name = 'tomato' # 創建protected屬性 >>> @property # 相當于get >>> def name(self): # 創建屬性的get方法 >>> print('here inside get_name') >>> return self._name >>> @name.setter # 相當于set >>> def set_name(self, value): # 創建屬性的set方法 >>> print('here inside set_name') >>> self._name = value >>> @name.deleter # 相當于del >>> def del_name(self): # 創建屬性的del方法 >>> print('here inside del_name') >>> del self._name >>>t = Tomato() >>>print(t.name) # 觸發get_name() here inside get_name 'tomato' >>>t.name = 'Tomato' # 觸發set_name() here inside set_name >>>del t.name # 觸發del_name() here inside del_name >>>help(t.name) # 觸發描述 here inside get_name
八.類的內置屬性
類包含以下默認內置屬性:
1)__dict__:以字典的方式顯示類的成員組成>>>class FoodMaterial(): >>> def __init__(self): >>> self._price = 10 # 受保護的屬性 >>> def getPrice(self) : # 受保護的方法 >>> print('我的價格是:{price}'.format(price=self.price)) >>>print(FoodMaterial.__dict__) {'__module__': '__main__', '__init__': <function FoodMaterial.__init__ at 0x000001A9534477B8>, 'getPrice': <function FoodMaterial.getPrice at 0x000001A953447840>, '__dict__': <attribute '__dict__' of 'FoodMaterial' objects>, '__weakref__': <attribute '__weakref__' of 'FoodMaterial' objects>, '__doc__': None} >>>tomato = FoodMaterial() >>>print(tomato.__dict__) {'_price': 10}
2)__doc__: 獲取類的文檔信息
>>>class FoodMaterial(): >>> '''This is class FoodMaterial''' >>> pass >>>print(FoodMaterial.__doc__) This is class FoodMaterial >>>tomato = FoodMaterial() >>>print(tomato.__doc__) This is class FoodMaterial
3)__name__:獲取類的名稱,如果在模塊中使用,獲取模塊的名稱
>>>class FoodMaterial(): >>> pass >>>print(FoodMaterial.__name__) 'FoodMaterial'
4)__bases__: 獲取某個類的所有父類,以元組的方式顯示
>>>class Animal(): >>> pass >>>class Baby(): >>> pass >>>class Dog(Animal): >>> pass >>>class puppy(Dog,Baby): >>> pass >>>print(puppy.__bases___) (__main__.Dog, __main__.Baby)
九.魔法函數
- 魔術方法就是不需要人為調用的方法,基本是在特定的時刻自動觸發。
- 魔法函數大全(沒找到很好的中文教程,點擊查看英文介紹)。
- 以下是一些常用的魔法函數:
1)__init__
見:構造函數 - 5.8
2)__str__
當類被打印時,將調用str方法的值。>>>class Dog(object): # 如果不處理__str__方法 >>> pass >>>dog = Dog() >>>print(dog) <__main__.Dog object at 0x000001A953477240> >>>class Dog(object): >>> def __str__(self): >>> return 'It is a dog!' >>>dog = Dog() >>>print(dog) # 返回了__str__方法的值 It is a dog!
3)__new__
- 初始化對象,在__init__前執行
- __new__方法包含一個參數,用于傳入要實例化的對象
- __new__方法必須返回一個值,是實例化后的對象
>>>class Dog(object): >>> def __new__(cls): >>> print(cls) >>> return 1 >>>dog = Dog() # 調用了print(cls) <class '__main__.Dog'> >>>print(dog) # 由于返回值是1,所以Dog變成了1 1 >>>type(dog) # 類型也變成了整數 int
4)__call__
- 定義后可以將對象當作函數使用。
>>>class Dog(object): >>> def __call__(self,name): >>> print('{name} is running!'.format(name=name)) >>>dog = Dog() >>>dog('qq') # 調用__call__ qq is running!
5)__len__
- 在用len()方法時,會調用__len__方法
>>>class Dog(object): >>> def __len__(self): >>> print('very very long!') >>>dog = Dog() >>>len(dog) very very long! --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-100-b3792c81c856> in <module> ----> 1 len(dog) TypeError: 'NoneType' object cannot be interpreted as an integer
6)__repr__
函數str() 用于將值轉化為適于人閱讀的形式,而repr() 轉化為供解釋器讀取的形式,某對象沒有適于人閱讀的解釋形式的話,str() 會返回與repr(),所以print展示的都是str的格式。>>>class Dog(object): >>> def __repr__(self): >>> return 'It is a dog!' >>>dog = Dog() >>>print(dog) # 返回了__str__方法的值 It is a dog!
7)__setattr__
見:類的成員描述符 - 7
8)__getattr__
見:類的成員描述符 - 7
9)__delattr__
見:類的成員描述符 - 7
10)__getattribute__
見:類的成員描述符 - 7
11)__del__
見:類的成員描述符 - 7
12)__getitem__
定義__getitem__方法,就可以用對象像字典一樣取值:key[value]
>>>class Dog(object): >>> def __init__(self): >>> self.name = 'dog' >>> def __getitem__(self,key): >>> print('here in __getitem__') >>> return self.name >>>dog = Dog() >>>print(dog['name']) # 調用了__getitem__方法 here in __getitem__ 'dog'
13)__delitem__
定義__delitem__方法,就可以用對象像字典一樣刪除值:del key[value]
>>>class Dog(object): >>> def __init__(self): >>> self.name = 'dog' >>> def __delitem__(self,key): >>> print('here in __delitem__') >>> del self.name >>>dog = Dog() >>>del dog['name'] # 調用了__delitem__方法 here in __delitem__ >>>print(dog.name) AttributeError: 'Dog' object has no attribute 'name'
十.抽象類
- 抽象類就是沒有具體實現內容的方法成為抽象類
- 抽象類的主要意義是規范了子類的行為和接口
- 抽象類的使用需要借助abc模塊
- 抽象類不允許直接實例化
- 必須繼承才可以使用,且繼承的子類必須實現所有繼承來的抽象方法
- 假定子類沒有是現實所有繼承的抽象方法,則子類也不能實例化
>>>import abc # 導入abc模塊 >>>class Animal(metaclass=abc.ABCMeta): >>> type = animal >>> @abc.abstractmethod # 定義抽象方法,無需實現功能 >>> def bart(self): >>> pass >>>dog = Animal() # 抽象類不能被實例化 TypeError: Can't instantiate abstract class animal with abstract methods bart >>>class Dog(animal): # 通過繼承實現抽象類 >>> def bart(self): >>> print('嗷嗷!') >>>dog = Dog() >>>dog.bart() 嗷嗷!
參考資料
- https://www.runoob.com 菜鳥教程
- http://www.tulingxueyuan.com/ 北京圖靈學院
- http://www.imooc.com/article/19184?block_id=tuijian_wz#child_5_1 兩點水
- https://blog.csdn.net/weixin_44213550/article/details/91346411 python老菜鳥
- https://realpython.com/python-string-formatting/ Dan Bader
- https://www.liaoxuefeng.com/ 廖雪峰
- https://blog.csdn.net/Gnewocean/article/details/85319590 新海說
- 《Python學習手冊》Mark Lutz
- 《Python編程 從入門到實踐》Eric Matthes
本文作者:大師兄(superkmi)