一、參考
二、構造和初始化
2.1 __new__
在對象實例化過程中最先調用的方法是__new__, 該方法接收參數為類,然后將其他參數,傳遞給__init__, 該魔法函數比較少見,可以使用其,創建單例類; __new__方法是一個類方法,需要攜帶的第一個參數是類
class T1(object):
_instances = {}
def __new__(class_, *args, **kwargs):
if class_ not in class_._instances:
class_._instances[class_] = super(T1, class_).__new__(class_, *args, **kwargs)
return class_._instances[class_]
def __init__(self, *args, **kwargs):
pass
2.2 __init__
__init__是一個實例方法,用于將構造的實例初始化,在類定義中十分常見
2.3 __del__
類比于C++, __new__和__init__可以當作類的構造函數,__del__充當類的析構函數,該函數在垃圾回收時候調用,而不是在del object時候觸發,可以用于添加套接字或者文件的close()邏輯,但是使用需要小心,實際幾乎不使用該方法
from os.path import join
class FileObject:
'''Wrapper for file objects to make sure the file gets closed on deletion.'''
def __init__(self, filepath='~', filename='sample.txt'):
# open a file filename in filepath in read and write mode
self.file = open(join(filepath, filename), 'r+')
def __del__(self):
self.file.close()
del self.file
三、重定義運算符
四、獲取類表示信息
4.1 __str__
__str__(self)
自定義對類調用str()方法時候的行為
4.2 __repr__
__repr__(self)
定義對類的實例調用repr()時候的行為,str()和repr()的不同之處是:目標受眾的不同,repr()的目的是生成主要是機器可讀的輸出,許多情況下,可能輸出為python代碼, 而str()一般輸出為人類可讀的信息
4.3 __unicode__
__unicode__(self)
定義對類實例執行unicode()方法時候的行為,其返回unicode字符串,如果只定義了unicode(),使用str()會報錯,所以需要同事定義兩個函數
4.4 __format__
__format__(self, formatstr)
定義在新樣式字符串格式中使用類實例時候的行為
4.5 __hash__
__hash__(self)
當調用hash()函數時候定義行為,通常用于字典中的key的快速比較是否相同,通常也需要實現__eq__, 遵循下面的規則,
a == b 實際為 hash(a) == hash(b)
4.6 __nonzero__
__nonzero__(self)
定義對類的實例調用bool()時候的行為,返回值為True或者False,取決于具體的類
4.7 __dir__
__dir__(self)
定義對類的實例調用dir()函數時候的行為,返回值是用戶的屬性列表,通常,實現dir()函數是不必要的,但是,如果重新定義了__getattr__或者__getattribute__或者其他動態生成屬性,則其對于交互式使用類非常重要
4.8 __sizeof__
__sizeof__(self)
定義對類實例調用sys.getsizeof()函數時候的行為,返回值為對象的大小(字節為單位),通常對于C擴展實現的Python類有作用
五、控制屬性訪問
如果與其他語言比較(例如: Java),Python中好像沒有真正的封裝,例如,沒有辦法通過公共函數getter和setter定義私有屬性,這是事實。
Python通過下列的魔法函數實現屬性的封裝,而不是使用顯式的修飾符
5.1 __getattr__
__getattr__(self, name)
可以定義當用戶試圖訪問一個不存在的屬性時候的行為,這對于捕獲或者重定向常見的拼寫錯誤、引發警告等非常有用,只有當訪問不存在的屬性時候才會調用該方法,因此它不是真正的封裝解決方案
5.2 __setattr__
__setattr__(self, name, value)
與__getattr__不相同,__setattr__是一種封裝解決方案,允許定義分配屬性值的行為,如果該屬性值已經存在,則會覆蓋,由此可以自定義屬性值的賦值規則
def __setattr__(self, name, value):
self.name = value
# since every time an attribute is assigned, __setattr__() is called, this
# is recursion.
# so this really means self.__setattr__('name', value). Since the method
# keeps calling itself, the recursion goes on forever causing a crash
由上,代碼self.name = value會調用__setattr__內置函數,所以會導致循環無限遞歸,正確的定義方式為
def __setattr__(self, name, value):
self.__dict__[name] = value # assigning to the dict of names in the class
# define custom behavior here
5.3 __delattr__
__delattr__(self, name)
與__setattr__相同,但是作用是刪除屬性而不是設置屬性,為了防止無限遞歸,還需要采取與__setattr__相同的預防措施(在__delattr__的實現中調用del self.name將導致無限遞歸)
5.4 __getattribute__
__getattribute__(self, name)
不建議使用該函數,因為極少情況下可以不產生bug正確使用。
每次獲取屬性時候,都會調用該函數
5.5 總結
Python的魔法方法非常重要且強大,如果任意使用可能會帶來破壞,因此,在已經充分了解了自定義屬性訪問之前,不要隨意使用該魔法方法,事實上,魔法方法往往過于強大和反直覺,它存在的原因是:通過它,可以自由的做任何想做的事情,但是如果不充分熟悉,使用將會非常困難。
class AccessCounter(object):
'''A class that contains a value and implements an access counter.
The counter increments each time the value is changed.'''
def __init__(self, val):
super(AccessCounter, self).__setattr__('counter', 0)
super(AccessCounter, self).__setattr__('value', val)
def __setattr__(self, name, value):
if name == 'value':
super(AccessCounter, self).__setattr__('counter', self.counter + 1)
# Make this unconditional.
# If you want to prevent other attributes to be set, raise AttributeError(name)
super(AccessCounter, self).__setattr__(name, value)
def __delattr__(self, name):
if name == 'value':
super(AccessCounter, self).__setattr__('counter', self.counter + 1)
super(AccessCounter, self).__delattr__(name)
六、自定義序列
七、反射
八、可調用對象
在python中,函數是第一類對象,這意味著它們可以被傳遞給函數和方法,好像其他第一類對象一樣(數值、字符串等),而這是一個非常強大的功能。
通過魔法函數__call__,可以使得實例也表現的像函數一樣,以便可以調用實例,可以將實例作為參數在函數調用中傳遞。
8.1 __call__
__call__(self, [args...])
允許將實例進行函數調用,如下,實際作用為e(5,6)實際為調用e.__call__(5,6)
在經常需要修改實例的狀態的場景下,使用__call__調用實例,修改實例狀態是一種十分直觀且優雅的方式
class Entity(object):
'''Class to represent an entity. Callable to update the entity's position.'''
def __init__(self, size, x, y):
self.x, self.y = x, y
self.size = size
def __call__(self, x, y):
'''Change the position of the entity.'''
self.x, self.y = x, y
if __name__ == '__main__':
e = Entity(size=10, x=1, y=2)
print(e)
e(5, 6)
print(e)
九、上下文管理
十、抽象基類
十一、描述符對象
十二、復制
十三、序列化對象
十四、結論