Python 特殊成员和魔法方法
Python中有大量類似__doc__這種以雙下劃線開頭和結(jié)尾的特殊成員及“魔法方法”,它們有著非常重要的地位和作用,也是Python語言獨具特色的語法之一!
比如:
- __init__ : 構(gòu)造函數(shù),在生成對象時調(diào)用
- __del__ : 析構(gòu)函數(shù),釋放對象時使用
- __repr__ : 打印,轉(zhuǎn)換
- __setitem__ : 按照索引賦值
- __getitem__: 按照索引獲取值
- __len__: 獲得長度
- __cmp__: 比較運算
- __call__: 調(diào)用
- __add__: 加運算
- __sub__: 減運算
- __mul__: 乘運算
- __div__: 除運算
- __mod__: 求余運算
- __pow__: 冪
需要注意的是,這些成員里面有些是方法,調(diào)用時要加括號,有些是屬性,調(diào)用時不需要加括號(廢話!)。下面將一些常用的介紹一下,:
1. __doc__
說明性文檔和信息。Python自建,無需自定義。
class Foo:""" 描述類信息,可被自動收集 """def func(self):pass# 打印類的說明文檔 print(Foo.__doc__)2. __init__()
實例化方法,通過類創(chuàng)建實例時,自動觸發(fā)執(zhí)行。
''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class Foo:def __init__(self, name):self.name = nameself.age = 18obj = Foo(jack') # 自動執(zhí)行類中的 __init__ 方法3.__module__和__class__
__module__ 表示當前操作的對象在屬于哪個模塊。
__class__表示當前操作的對象屬于哪個類。
這兩者也是Python內(nèi)建,無需自定義。
class Foo:passobj = Foo() print(obj.__module__) print(obj.__class__)------------ 運行結(jié)果: __main__ <class '__main__.Foo'>4. __del__()
析構(gòu)方法,當對象在內(nèi)存中被釋放時,自動觸發(fā)此方法。
注:此方法一般無須自定義,因為Python自帶內(nèi)存分配和釋放機制,除非你需要在釋放的時候指定做一些動作。析構(gòu)函數(shù)的調(diào)用是由解釋器在進行垃圾回收時自動觸發(fā)執(zhí)行的。
''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class Foo:def __del__(self):print("我被回收了!")obj = Foo()del obj5.__call__()
如果為一個類編寫了該方法,那么在該類的實例后面加括號,可會調(diào)用這個方法。
注:構(gòu)造方法的執(zhí)行是由類加括號執(zhí)行的,即:對象 = 類名(),而對于__call__() 方法,是由對象后加括號觸發(fā)的,即:對象() 或者 類()()
class Foo:def __init__(self):passdef __call__(self, *args, **kwargs):print('__call__')obj = Foo() # 執(zhí)行 __init__ obj() # 執(zhí)行 __call__那么,怎么判斷一個對象是否可以被執(zhí)行呢?能被執(zhí)行的對象就是一個Callable對象,可以用Python內(nèi)建的callable()函數(shù)進行測試,我們在前面的章節(jié)已經(jīng)介紹過這個函數(shù)了。
>>> callable(Student()) True >>> callable(max) True >>> callable([1, 2, 3]) False >>> callable(None) False >>> callable('str') False >>> callable(int) True >>> callable(str) True6. __dict__
列出類或?qū)ο笾械乃谐蓡T!非常重要和有用的一個屬性,Python自建,無需用戶自己定義。
''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class Province:country = 'China'def __init__(self, name, count):self.name = nameself.count = countdef func(self, *args, **kwargs):print('func')# 獲取類的成員 print(Province.__dict__)# 獲取 對象obj1 的成員 obj1 = Province('HeBei',10000) print(obj1.__dict__)# 獲取 對象obj2 的成員 obj2 = Province('HeNan', 3888) print(obj2.__dict__)7. __str__()
如果一個類中定義了__str__()方法,那么在打印對象時,默認輸出該方法的返回值。這也是一個非常重要的方法,需要用戶自己定義。
下面的類,沒有定義__str__()方法,打印結(jié)果是:<__main__.Foo object at 0x000000000210A358>
class Foo:passobj = Foo() print(obj)定義了__str__()方法后,打印結(jié)果是:‘jack’。
''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class Foo:def __str__(self):return 'jack'obj = Foo() print(obj)8、__getitem__()、__setitem__()、__delitem__()
取值、賦值、刪除這“三劍客”的套路,在Python中,我們已經(jīng)見過很多次了,比如前面的@property裝飾器。
Python中,標識符后面加圓括號,通常代表執(zhí)行或調(diào)用方法的意思。而在標識符后面加中括號[],通常代表取值的意思。Python設計了__getitem__()、__setitem__()、__delitem__()這三個特殊成員,用于執(zhí)行與中括號有關(guān)的動作。它們分別表示取值、賦值、刪除數(shù)據(jù)。
也就是如下的操作:
a = 標識符[] : 執(zhí)行__getitem__方法 標識符[] = a : 執(zhí)行__setitem__方法 del 標識符[] : 執(zhí)行__delitem__方法如果有一個類同時定義了這三個魔法方法,那么這個類的實例的行為看起來就像一個字典一樣,如下例所示:
class Foo:def __getitem__(self, key):print('__getitem__',key)def __setitem__(self, key, value):print('__setitem__',key,value)def __delitem__(self, key):print('__delitem__',key)obj = Foo()result = obj['k1'] # 自動觸發(fā)執(zhí)行 __getitem__ obj['k2'] = 'jack' # 自動觸發(fā)執(zhí)行 __setitem__ del obj['k1'] # 自動觸發(fā)執(zhí)行 __delitem__9. __iter__()
這是迭代器方法!列表、字典、元組之所以可以進行for循環(huán),是因為其內(nèi)部定義了 iter()這個方法。如果用戶想讓自定義的類的對象可以被迭代,那么就需要在類中定義這個方法,并且讓該方法的返回值是一個可迭代的對象。當在代碼中利用for循環(huán)遍歷對象時,就會調(diào)用類的這個__iter__()方法。
普通的類:
''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class Foo:passobj = Foo()for i in obj:print(i)# 報錯:TypeError: 'Foo' object is not iterable<br># 原因是Foo對象不可迭代添加一個__iter__(),但什么都不返回:
class Foo:def __iter__(self):passobj = Foo()for i in obj:print(i)# 報錯:TypeError: iter() returned non-iterator of type 'NoneType'#原因是 __iter__方法沒有返回一個可迭代的對象返回一個個迭代對象:
class Foo:def __init__(self, sq):self.sq = sqdef __iter__(self):return iter(self.sq)obj = Foo([11,22,33,44])for i in obj:print(i)# 這下沒問題了!最好的方法是使用生成器:
''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class Foo:def __init__(self):passdef __iter__(self):yield 1yield 2yield 3obj = Foo() for i in obj:print(i)10、__len__()
在Python中,如果你調(diào)用內(nèi)置的len()函數(shù)試圖獲取一個對象的長度,在后臺,其實是去調(diào)用該對象的__len__()方法,所以,下面的代碼是等價的:
>>> len('ABC') 3 >>> 'ABC'.__len__() 3Python的list、dict、str等內(nèi)置數(shù)據(jù)類型都實現(xiàn)了該方法,但是你自定義的類要實現(xiàn)len方法需要好好設計。
11. __repr__()
這個方法的作用和__str__()很像,兩者的區(qū)別是__str__()返回用戶看到的字符串,而__repr__()返回程序開發(fā)者看到的字符串,也就是說,__repr__()是為調(diào)試服務的。通常兩者代碼一樣。
class Foo:def __init__(self, name):self.name = namedef __str__(self):return "this is %s" % self.name__repr__ = __str__12. __add__: 加運算 __sub__: 減運算 __mul__: 乘運算 __div__: 除運算__mod__: 求余運算 __pow__: 冪運算
這些都是算術(shù)運算方法,需要你自己為類設計具體運算代碼。有些Python內(nèi)置數(shù)據(jù)類型,比如int就帶有這些方法。Python支持運算符的重載,也就是重寫。
''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' 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)13. __author__
__author__代表作者信息!類似的特殊成員還有很多,就不羅列了。
#!/usr/bin/env python # -*- coding:utf-8 -*-""" a test module """ __author__ = "Jack"def show():print(__author__)show()14. __slots__
Python作為一種動態(tài)語言,可以在類定義完成和實例化后,給類或者對象繼續(xù)添加隨意個數(shù)或者任意類型的變量或方法,這是動態(tài)語言的特性。例如:
def print_doc(self):print("haha")class Foo:passobj1 = Foo() obj2 = Foo() # 動態(tài)添加實例變量 obj1.name = "jack" obj2.age = 18 # 動態(tài)的給類添加實例方法 Foo.show = print_doc obj1.show() obj2.show()但是!如果我想限制實例可以添加的變量怎么辦?可以使__slots__限制實例的變量,比如,只允許Foo的實例添加name和age屬性。
''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' def print_doc(self):print("haha")class Foo:__slots__ = ("name", "age")passobj1 = Foo() obj2 = Foo() # 動態(tài)添加實例變量 obj1.name = "jack" obj2.age = 18 obj1.sex = "male" # 這一句會彈出錯誤 # 但是無法限制給類添加方法 Foo.show = print_doc obj1.show() obj2.show()由于’sex’不在__slots__的列表中,所以不能綁定sex屬性,試圖綁定sex將得到AttributeError的錯誤。
Traceback (most recent call last):File "F:/Python/pycharm/201705/1.py", line 14, in <module>obj1.sex = "male" AttributeError: 'Foo' object has no attribute 'sex'需要提醒的是,__slots__定義的屬性僅對當前類的實例起作用,對繼承了它的子類是不起作用的。想想也是這個道理,如果你繼承一個父類,卻莫名其妙發(fā)現(xiàn)有些變量無法定義,那不是大問題么?如果非要子類也被限制,除非在子類中也定義__slots__,這樣,子類實例允許定義的屬性就是自身的__slots__加上父類的__slots__。
Python的特殊成員和“魔法方法”還有很多,需要大家在平時使用和學習的過程中不斷積累和總結(jié)使用經(jīng)驗。
總結(jié)
以上是生活随笔為你收集整理的Python 特殊成员和魔法方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python提升“技术逼格”的6个方法
- 下一篇: python教程:循环(while和fo