Python中的魔法屬性
魔法屬性
在Python中,所有以
__雙下劃線包起來(lái)的方法,都統(tǒng)稱為Magic Method,例如類的初始化方法__init__(),實(shí)例對(duì)象創(chuàng)造方法__new__()等。魔法屬性和方法是Python內(nèi)置的一些屬性和方法,有著特殊的含義。命名時(shí)前后加上兩個(gè)下劃線,在執(zhí)行系統(tǒng)特定操作時(shí),會(huì)自動(dòng)調(diào)用。
常見(jiàn)的魔法屬性
__doc__
表示類的描述信息
# __doc__
class Foo:
""" 描述類信息,這是用于測(cè)試的類 """
def func(self):
pass
# ipython 測(cè)驗(yàn)
In [2]: Foo.__doc__
Out[2]: ' 描述類信息,這是用于測(cè)試的類 '
__module__ 和 __class__
__module__表示當(dāng)前操作的對(duì)象在那個(gè)模塊__class__表示當(dāng)前操作的對(duì)象的類是什么
# __module__、__class__
# oop.py
class Student(object):
def __init__(self, name):
self.name = name
# main.py
from oop import Student
s = Student()
print(s.__module__) # 輸出 oop 即:輸出模塊
print(s.__class__) # 輸出 <class 'oop.Student'> 即:輸出類
__init__ 、__new__
__init__()初始化方法 和__new__(),通過(guò)類創(chuàng)建對(duì)象時(shí),自動(dòng)觸發(fā)執(zhí)行。__new__是用來(lái)創(chuàng)建類并返回這個(gè)類的實(shí)例,而__init__只是將傳入的參數(shù)來(lái)初始化該實(shí)例。
__new__()創(chuàng)建對(duì)象時(shí)調(diào)用,會(huì)返回當(dāng)前對(duì)象的一個(gè)實(shí)例__init__()創(chuàng)建完對(duì)象后調(diào)用,對(duì)當(dāng)前對(duì)象的一些實(shí)例初始化,無(wú)返回值
# __init__ 、 __new__
class Student(object):
def __init__(self, name, age):
print('__init__() called')
self.name = name
self.age = age
def __new__(cls, *args, **kwargs):
print('__new__() called')
print(cls, args, kwargs)
return super().__new__(cls)
# ipython 測(cè)驗(yàn)
In [26]: s1 = Student('hui', age=21)
__new__() called
<class '__main__.Student'> ('hui',) {'age': 21}
__init__() called
In [27]: s2 = Student('jack', age=20)
__new__() called
<class '__main__.Student'> ('jack',) {'age': 20}
__init__() called
__del__
當(dāng)對(duì)象在內(nèi)存中被釋放時(shí),自動(dòng)觸發(fā)執(zhí)行。
注:此方法一般無(wú)須定義,因?yàn)镻ython是一門高級(jí)語(yǔ)言,有 內(nèi)存管理、垃圾回收機(jī)制,程序員在使用時(shí)無(wú)需關(guān)心內(nèi)存的分配和釋放,因?yàn)榇斯ぷ鞫际墙唤oPython解釋器來(lái)執(zhí)行,所以,__del__ 的調(diào)用是由解釋器在進(jìn)行垃圾回收時(shí)自動(dòng)觸發(fā)執(zhí)行的。
# __del__
class Foo:
def __del__(self):
print('__del__() called')
# ipython 測(cè)驗(yàn)
In [29]: f = Foo()
In [30]: del f
__del__() called
__call__
讓類的實(shí)例的行為表現(xiàn)的像函數(shù)一樣,你可以調(diào)用它們,將一個(gè)函數(shù)當(dāng)做一個(gè)參數(shù)傳到另外一個(gè)函數(shù)中等等。這是一個(gè)非常強(qiáng)大的特性,其讓Python編程更加舒適甜美。對(duì)象后面加括號(hào),觸發(fā)執(zhí)行。
注:__init__ 方法的執(zhí)行是由創(chuàng)建對(duì)象觸發(fā)的,即:對(duì)象 = 類名() ;而對(duì)于 __call__ 方法的執(zhí)行是由對(duì)象后加括號(hào)觸發(fā)的,即:對(duì)象() 或者 類()()
__call__ 在那些 類的實(shí)例經(jīng)常改變狀態(tài)的時(shí)候會(huì)非常有效。調(diào)用這個(gè)實(shí)例是一種改變這個(gè)對(duì)象狀態(tài)的直接和優(yōu)雅的做法。用一個(gè)實(shí)例來(lái)表達(dá)最好不過(guò)了:
# __call__
class Rect(object)
"""
調(diào)用實(shí)例對(duì)象來(lái)改變矩形的位置
"""
def __init__(self, x, y):
# x, y代表矩形坐標(biāo)
self.x, self.y = x, y
def __call__(self, x, y):
# 改變實(shí)體的位置
self.x, self.y = x, y
# ipython 測(cè)驗(yàn)
In [33]: r = Rect(10, 10)
In [34]: r.x, r.y
Out[34]: (10, 10)
In [35]: r(0, 0)
In [36]: r.x, r.y
Out[36]: (0, 0)
In [37]: r(100, 100)
In [38]: r.x, r.y
Out[38]: (100, 100)
__dict__
類或?qū)ο笾械乃袑傩?/p>
類的實(shí)例屬性屬于對(duì)象;類中的類屬性和方法等屬于類,即:
# __dict__
class Student(object):
def __init__(self, name, age):
self.name = name
self._age = age
@property
def age(self):
return self._age
# ipython 測(cè)驗(yàn)
In [47]: # 獲取類屬性
In [48]: Student.__dict__
Out[48]:
mappingproxy({'__module__': '__main__',
'__init__': <function __main__.Student.__init__(self, name, age)>,
'age': <property at 0x210e2a005e8>,
'__dict__': <attribute '__dict__' of 'Student' objects>,
'__weakref__': <attribute '__weakref__' of 'Student' objects>,
'__doc__': None})
In [49]: # 獲取實(shí)例對(duì)象的屬性
In [50]: s = Student('hui', 21)
In [51]: s.__dict__
Out[51]: {'name': 'hui', '_age': 21}
In [52]: s2 = Student('jack', 20)
In [53]: s2.__dict__
Out[53]: {'name': 'jack', '_age': 20}
__str__
如果一個(gè)類中定義了__str__方法,那么在打印 對(duì)象 時(shí),默認(rèn)輸出該方法的返回值。
In [65]: # __str__
...: class Foo(object):
...: pass
...:
In [66]: f = Foo()
In [67]: print(f)
<__main__.Foo object at 0x00000210E2715608>
In [68]: class Foo(object):
...:
...: def __str__(self):
...: return '< Custom Foo object str >'
...:
In [69]: f = Foo()
In [70]: print(f)
< Custom Foo object str >
__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分別表示獲取、設(shè)置、刪除數(shù)據(jù)。
用于切片操作,如列表。
字典示例
# __getitem__、__setitem__、__delitem__
class MyDict(object):
def __init__(self):
self.my_dict = dict()
def __getitem__(self, key):
print('__getitem__() ', key)
return self.my_dict.get(key, None)
def __setitem__(self, key, value):
print('__setitem__() ', key, value)
self.my_dict.update(key=value)
def __delitem__(self, key):
print('__delitem__() ', key)
del self.my_dict[key]
# ipython 測(cè)驗(yàn)
In [33]: mdict = MyDict()
In [34]: print(mdict['name'])
__getitem__() name
None
In [35]: # 新增
In [36]: mdict['name'] = 'hui'
__setitem__() name hui
In [37]: mdict['age'] = 21
__setitem__() age 21
In [38]: mdict['name']
__getitem__() name
Out[38]: 'hui'
In [39]: mdict['age']
__getitem__() age
Out[39]: 21
In [40]: # 更新
In [41]: mdict['name'] = 'jack'
__setitem__() name jack
In [42]: mdict['name']
__getitem__() name
Out[42]: 'jack'
In [43]: # 刪除
In [44]: del mdict['age']
__delitem__() age
In [45]: print(mdict['age'])
__getitem__() age
None
列表示例
# 切片操作
class MyList(object):
def __init__(self):
self.mlist = list()
def __getitem__(self, index):
print('__getitem__() called')
print(index)
if isinstance(index, slice):
return self.mlist[index]
def __setitem__(self, index, value):
print('__getitem__() called')
print(index, value)
if isinstance(index, slice):
self.mlist[index] = value
def __delitem__(self, index):
print('__delitem__() called')
if isinstance(index, slice):
del self.mlist[index]
# ipython 測(cè)驗(yàn)
In [70]: mlist = MyList()
In [71]: mlist[0]
__getitem__() called
0
In [72]: mlist[0:-1]
__getitem__() called
slice(0, -1, None)
Out[72]: []
In [73]: mlist[:] = [1,2,3]
__getitem__() called
slice(None, None, None) [1, 2, 3]
In [74]: mlist[:]
__getitem__() called
slice(None, None, None)
Out[74]: [1, 2, 3]
In [75]: mlist[0:2]
__getitem__() called
slice(0, 2, None)
Out[75]: [1, 2]
In [76]: mlist[::-1]
__getitem__() called
slice(None, None, -1)
Out[76]: [3, 2, 1]
In [77]: mlist[0]
__getitem__() called
0
In [78]: mlist[0:1]
__getitem__() called
slice(0, 1, None)
Out[78]: [1]
In [79]: del mlist[0:1]
__delitem__() called
In [80]: mlist[:]
__getitem__() called
slice(None, None, None)
Out[80]: [2, 3]
注意: 當(dāng)進(jìn)行 mlist[0] 操作的時(shí)候傳遞并不是一個(gè) slice 對(duì)象,不是一個(gè) int 類型的數(shù)字,所以不能把索引為 0 的值取出來(lái),改成 mlist[0, 1] 或者在 __getitem__() 的方法中新增數(shù)字判斷,大家可以嘗試一下。
__enter__、__exit__
with 聲明是從 Python2.5 開(kāi)始引進(jìn)的關(guān)鍵詞。你應(yīng)該遇過(guò)這樣子的代碼:
with open('foo.txt') as bar:
# do something with bar
pass
在 with 聲明的代碼段中,我們可以做一些對(duì)象的開(kāi)始操作和退出操作,還能對(duì)異常進(jìn)行處理。這需要實(shí)現(xiàn)兩個(gè)魔術(shù)方法: __enter__ 和 __exit__。
__enter__(self):
定義了當(dāng)使用 with 語(yǔ)句的時(shí)候,會(huì)話管理器在塊被初始創(chuàng)建時(shí)要產(chǎn)生的行為。請(qǐng)注意,__enter__ 的返回值與 with 語(yǔ)句的目標(biāo)或者 as 后的名字綁定。
__exit__(self, exception_type, exception_value, traceback):
定義了當(dāng)一個(gè)代碼塊被執(zhí)行或者終止后,會(huì)話管理器應(yīng)該做什么。它可以被用來(lái)處理異常、執(zhí)行清理工作或做一些代碼塊執(zhí)行完畢之后的日常工作。如果代碼塊執(zhí)行成功,exception_type,exception_value,和traceback 將會(huì)為 None 。否則,你可以選擇處理這個(gè)異常或者是直接交給用戶處理。如果你想處理這個(gè)異常的話,請(qǐng)確保__exit__ 在所有語(yǔ)句結(jié)束之后返回 True。如果你想讓異常被會(huì)話管理器處理的話,那么就讓其產(chǎn)生該異常。
__copy__、__deepcopy__
有時(shí)候,尤其是當(dāng)你在處理可變對(duì)象時(shí),你可能想要復(fù)制一個(gè)對(duì)象,然后對(duì)其做出一些改變而不希望影響原來(lái)的對(duì)象。這就是Python的copy所發(fā)揮作用的地方。
__copy__(self):
定義了當(dāng)對(duì)你的類的實(shí)例調(diào)用 copy.copy() 時(shí)所產(chǎn)生的行為。copy.copy() 返回了你的對(duì)象的一個(gè)淺拷貝——這意味著,當(dāng)實(shí)例本身是一個(gè)新實(shí)例時(shí),它的所有數(shù)據(jù)都被引用了——例如,當(dāng)一個(gè)對(duì)象本身被復(fù)制了,它的數(shù)據(jù)仍然是被引用的(因此,對(duì)于淺拷貝中數(shù)據(jù)的更改仍然可能導(dǎo)致數(shù)據(jù)在原始對(duì)象的中的改變)。
__deepcopy__(self, memodict={}):
定義了當(dāng)對(duì)你的類的實(shí)例調(diào)用 copy.deepcopy()時(shí)所產(chǎn)生的行為。copy.deepcopy() 返回了你的對(duì)象的一個(gè)深拷貝——對(duì)象和其數(shù)據(jù)都被拷貝了。memodict 是對(duì)之前被拷貝的對(duì)象的一個(gè)緩存——這優(yōu)化了拷貝過(guò)程并且阻止了對(duì)遞歸數(shù)據(jù)結(jié)構(gòu)拷貝時(shí)的無(wú)限遞歸。當(dāng)你想要進(jìn)行對(duì)一個(gè)單獨(dú)的屬性進(jìn)行深拷貝時(shí),調(diào)用copy.deepcopy(),并以 memodict 為第一個(gè)參數(shù)。
這些魔術(shù)方法的用例看起來(lái)很小,并且確實(shí)非常實(shí)用. 它們反應(yīng)了關(guān)于面向?qū)ο蟪绦蛏弦恍┲匾臇|西在Python 上,并且總的來(lái)說(shuō) Python 總是一個(gè)簡(jiǎn)單的方法去找某些事情,即使是沒(méi)有必要的。這些魔法方法可能看起來(lái)不是很有用,但是一旦你需要它們,你會(huì)感到慶幸它們的存在。
其他魔法方法
由于魔法屬性、方法太多了在這就不一一描述和展示了,其他的就以表格形式呈現(xiàn)吧。
用于比較的魔術(shù)方法
| 方法 | 作用 |
|---|---|
__cmp__(self, other) | 比較方法里面最基本的的魔法方法 |
__eq__(self, other) | 定義相等符號(hào)的行為,== |
__ne__(self,other) | 定義不等符號(hào)的行為,!= |
__lt__(self,other) | 定義小于符號(hào)的行為,< |
__gt__(self,other) | 定義大于符號(hào)的行為,> |
__le__(self,other) | 定義小于等于符號(hào)的行為,<= |
__ge__(self,other) | 定義大于等于符號(hào)的行為,>= |
數(shù)值計(jì)算的魔術(shù)方法
單目運(yùn)算符和函數(shù)
| 方法 | 作用 |
|---|---|
__pos__(self) | 實(shí)現(xiàn)一個(gè)取正數(shù)的操作 |
__neg__(self) | 實(shí)現(xiàn)一個(gè)取負(fù)數(shù)的操作 |
__abs__(self) | 實(shí)現(xiàn)一個(gè)內(nèi)建的 abs() 函數(shù)的行為 |
__invert__(self) | 實(shí)現(xiàn)一個(gè)取反操作符(~操作符)的行為 |
__round__(self, n) | 實(shí)現(xiàn)一個(gè)內(nèi)建的 round() 函數(shù)的行為 |
__floor__(self) | 實(shí)現(xiàn) math.floor() 的函數(shù)行為 |
__ceil__(self) | 實(shí)現(xiàn) math.ceil() 的函數(shù)行為 |
__trunc__(self) | 實(shí)現(xiàn) math.trunc() 的函數(shù)行為 |
雙目運(yùn)算符或函數(shù)
| 方法 | 作用 |
|---|---|
__add__(self, other) | 實(shí)現(xiàn)一個(gè)加法 |
__sub__(self, other) | 實(shí)現(xiàn)一個(gè)減法 |
__mul__(self, other) | 實(shí)現(xiàn)一個(gè)乘法 |
__floordiv__(self, other) | 實(shí)現(xiàn)一個(gè) // 操作符產(chǎn)生的整除操作 |
__div__(self, other) | 實(shí)現(xiàn)一個(gè) / 操作符代表的除法操作 |
__truediv__(self, other) | 實(shí)現(xiàn)真實(shí)除法 |
__mod__(self, other) | 實(shí)現(xiàn)一個(gè) % 操作符代表的取模操作 |
__divmod__(self, other) | 實(shí)現(xiàn)一個(gè)內(nèi)建函數(shù) divmod() |
__pow__(self, other) | 實(shí)現(xiàn)一個(gè)指數(shù)操作( ****** 操作符)的行為 |
__lshift__(self, other) | 實(shí)現(xiàn)一個(gè)位左移操作**(<<)**的功能 |
__rshift__(self, other) | 實(shí)現(xiàn)一個(gè)位右移操作**(>>)**的功能 |
__and__(self, other) | 實(shí)現(xiàn)一個(gè)按位進(jìn)行與操作**(&)**的行為 |
__or__(self, other) | 實(shí)現(xiàn)一個(gè)按位進(jìn)行或操作的行為 |
__xor__(self, other) | 異或運(yùn)算符相當(dāng)于 ^ |
增量運(yùn)算
| 方法 | 作用 |
|---|---|
__iadd__(self, other) | 加法賦值 |
__isub__(self, other) | 減法賦值 |
__imul__(self, other) | 乘法賦值 |
__ifloordiv__(self, other) | 整除賦值,地板除,相當(dāng)于 //= 運(yùn)算符 |
__idiv__(self, other) | 除法賦值,相當(dāng)于 /= 運(yùn)算符 |
__itruediv__(self, other) | 真除賦值 |
__imod_(self, other) | 模賦值,相當(dāng)于 %= 運(yùn)算符 |
__ipow__(self, other) | 乘方賦值,相當(dāng)于 **= 運(yùn)算符 |
__ilshift__(self, other) | 左移賦值,相當(dāng)于 <<= 運(yùn)算符 |
__irshift__(self, other) | 左移賦值,相當(dāng)于 >>= 運(yùn)算符 |
__iand__(self, other) | 與賦值,相當(dāng)于 &= 運(yùn)算符 |
__ior__(self, other) | 或賦值 |
__ixor__(self, other) | 異或運(yùn)算符,相當(dāng)于 ^= 運(yùn)算符 |
類型轉(zhuǎn)換
| 方法 | 作用 |
|---|---|
__int__(self) | 轉(zhuǎn)換成整型 |
__long__(self) | 轉(zhuǎn)換成長(zhǎng)整型 |
__float__(self) | 轉(zhuǎn)換成浮點(diǎn)型 |
__complex__(self) | 轉(zhuǎn)換成 復(fù)數(shù)型 |
__oct__(self) | 轉(zhuǎn)換成八進(jìn)制 |
__hex__(self) | 轉(zhuǎn)換成十六進(jìn)制 |
__index__(self) | 如果你定義了一個(gè)可能被用來(lái)做切片操作的數(shù)值型,你就應(yīng)該定義__index__ |
__trunc__(self) | 當(dāng) math.trunc(self) 使用時(shí)被調(diào)用 __trunc__ 返回自身類型的整型截取 |
__coerce__(self, other) | 執(zhí)行混合類型的運(yùn)算 |
大自然用數(shù)百億年創(chuàng)造出我們現(xiàn)實(shí)世界,而程序員用幾百年創(chuàng)造出一個(gè)完全不同的虛擬世界。我們用鍵盤敲出一磚一瓦,用大腦構(gòu)建一切。人們把1000視為權(quán)威,我們反其道行之,捍衛(wèi)1024的地位。我們不是鍵盤俠,我們只是平凡世界中不凡的締造者 。
