1. 程式人生 > >Python中的對象行為與特殊方法(一)對象的創建與銷毀

Python中的對象行為與特殊方法(一)對象的創建與銷毀

連接 繼承 並不會 參數 asa rac 方式 垃圾收集 shell

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中的對象行為與特殊方法(一)對象的創建與銷毀