Python中的對象行為與特殊方法(一)對象的創建與銷毀
Python中類調用__new__()類方法來創建實例,調用__init__()方法來初始化對象,對象的銷毀則調用__del__()方法。
__new__()方法第一個參數為類cls,通常返回cls的一個實例,然後新實例的__init__()方法將以類似於__init__(self[, ...])的方式被調用,self是創建的新的實例,其它的參數和傳遞給__new__()的參數一樣。
如果__new__()方法不返回cls 的實例,那麽__init__()將不會被調用。
對於一個類來說:
class Foo(object): def __init__(self, name): self.name = name f = Foo(‘bar‘)
當進行f = Foo(‘bar‘)的操作時,實際上執行以下步驟來創建和初始化實例:
f = Foo.__new__(Foo, ‘bar‘) if isinstance(f, Foo): Foo.__init__(f, ‘bar‘)
__new__(cls[, ...])
__new__()方法是一個靜態類方法,以實例的類為第一個參數。其余的參數是傳遞給對象構造表達式的參數,與傳遞給__init__()的相同。
一般通過使用super(currentclass, cls).__new__(cls[, ...])來調用超類的__new__()方法創建類的一個新的實例。
用戶自定義對象的時候,很少定義__new__()方法。如果定義的話,一般是在定義元類或者定義一些繼承自不可變的內置數據類型(整數、字符串、元組等)的對象的時候。
定義一些不可變數據類型的時候,因為對象是不可變的,那麽在__init__()中是無法對對象進行修改的,而__new__()方法是在實例創建之前執行的:
class Upperstr(str): def __new__(cls, value=‘‘): return str.__new__(cls, value.upper())
上述Upperstr類創造的字符串實例全是大寫的:
>>> u = Upperstr(‘hello world‘) >>> u ‘HELLO WORLD‘
也可以在單例模式中使用__new__()方法創建唯一的實例,放在類變量中,以後每次創建都返回這一個實例:
class Singleton(object): def __new__(cls, *args, **kwargs): if not hasattr(cls, ‘_inst‘): cls._inst=super(Singleton, cls).__new__(cls, *args, **kwargs) return cls._inst
__init__(self[, ...])
__init__()方法用來初始化對象的屬性,在實例創建完成時調用。參數為傳遞給類構造器表達式的那些參數。如果基類具有__init__()方法,那麽繼承的類的__init__()方法,而且必須顯式調用:BaseClass.__init__(self, [args...])。
構造器的一個特殊限制是不可以返回任何值,這樣做將導致運行時拋出一個TypeError。
__init__()方法一般含有大量的self.attr = attr的賦值語句:
class Foo(object): def __init__(self, name, color, num=1): self.name = name self.color = color self.num = num f = Foo(‘bar‘, ‘yellow‘)
如果不想重復這樣的賦值操作,有以下幾個辦法:
class Foo(object): def __init__(self, name, color, num=1): print locals() f = Foo(‘bar‘, ‘yellow‘)
__init__()方法的局部命名空間如下:
{‘color‘: ‘yellow‘, ‘self‘: <__main__.Foo object at 0x00000000055C4780>, ‘num‘: 1, ‘name‘: ‘bar‘}
self變量為創建的實例本身,其余要給實例賦值的變量name,color,num均在其中。而實例屬性是存儲在實例的__dict__變量中,我們只需要將這些要賦值的變量放入__dict__即可:
class Foo(object): def __init__(self, name, color, num=1): self.__dict__.update(locals()) del self.__dict__[‘self‘]
>>> f = Foo(‘bar‘, ‘yellow‘) >>> f.name ‘bar‘ >>> f.color ‘yellow‘ >>> f.num 1
這樣當實例屬性很多的時候,就簡潔了很多。但當使用特性或者描述符來包裝對象的屬性時,會產生問題,因為特性或者描述符包裝的屬性並不保存於__dict__之中,解決方案是使用setattr內置函數:
def attr_from_locals(locals_dict): self = locals_dict.pop(‘self‘) for k,v in locals_dict.items(): setattr(self, k, v) class Foo(object): def __init__(self, name, color, num=1): attr_from_locals(locals())
如果__init__()方法中使用了一些不用賦值給實例的本地變量的時候,再使用locals()會包含這些本地變量,我們可以再精準的傳遞__init__()中的參數。
通過一個函數f的f.__code__屬性可以得到函數的字節碼對象c,字節碼對象的屬性c.co_varnames可以得到局部變量名稱的元組(函數參數在前,函數內局部變量在後),c.co_argcount可以得到函數位置參數的數量,利用切片可以去除函數內局部變量:
def attr_from_locals(locals_dict): self = locals_dict.pop(‘self‘) code = self.__init__.__func__.__code__ args = code.co_varnames[1:code.co_argcount] for k in args: setattr(self, k, locals_dict[k]) class Foo(object): def __init__(self, name, color, num=1): x = 1 attr_from_locals(locals()) f = Foo(‘bar‘, ‘yellow‘)
可以看到x變量並未賦值給實例:
>>> f.name ‘bar‘ >>> f.color ‘yellow‘ >>> f.num 1 >>> f.x Traceback (most recent call last): File "<pyshell#87>", line 1, in <module> f.x AttributeError: ‘Foo‘ object has no attribute ‘x‘
__del__(self)
當實例要被銷毀的時候,會調用對象的__del__()方法。註意當使用del a這樣的語句時,並不會調用__del__()方法,只是減少一個引用計數,當對象的引用計數減至0時,才會調用__del__()。
__del__()方法也很少有必要定義,定義了此方法的實例,無法被python的循環垃圾收集器收集。
需要執行清除操作時(關閉文件、關閉網絡連接等)可以定義,當然更好的辦法是定義一個close()方法顯式的執行關閉操作。
Python中的對象行為與特殊方法(一)對象的創建與銷毀