2012-07-24 104 views
11

我在覆蓋__new__()方法返回一個具有特定__init__()集合的類實例。蟒蛇似乎在爲什麼Python不調用實例方法__init __()創建實例,而是調用類提供的__init __()來代替?

http://docs.python.org/reference/datamodel.html

調用這個類提供的__init__()方法,而不是特定實例的方法,雖然Python的文件說:

典型實現創建由類的新實例使用super(currentclass,cls).__ new __(cls [,...]) 調用 超類的__new __()方法,然後在返回它之前修改新創建的實例爲 。

如果__new __()返回CLS的實例,那麼新實例的__init __()方法 將被調用像__init __(自我[,...]),其中self是新實例,其餘的 論據和傳遞給__new __()一樣。

這裏是我的測試代碼:

#!/usr/bin/env python 

import new 

def myinit(self, *args, **kwargs): 
    print "myinit called, args = %s, kwargs = %s" % (args, kwargs) 


class myclass(object): 
    def __new__(cls, *args, **kwargs): 
     ret = object.__new__(cls) 

     ret.__init__ = new.instancemethod(myinit, ret, cls) 
     return ret 

    def __init__(self, *args, **kwargs): 
     print "myclass.__init__ called, self.__init__ is %s" % self.__init__ 
     self.__init__(*args, **kwargs) 

a = myclass() 

其輸出

$ python --version 
Python 2.6.6 
$ ./mytest.py 
myclass.__init__ called, self.__init__ is <bound method myclass.myinit of <__main__.myclass object at 0x7fa72155c790>> 
myinit called, args =(), kwargs = {} 

,似乎只有這樣才能獲得myinit()運行,是顯式調用它self.__init__()myclass.__init__()

+1

大問題! – Marcin 2012-07-24 16:49:29

回答

8

新樣式類特殊的方法是看取決於實例的類型,而不是實例本身。這是documented behaviour

對於新樣式類,特殊的方法調用隱含只能保證,如果一個對象的類型定義,而不是在對象的實例字典才能正常工作。這種行爲就是爲什麼下面的代碼會引發異常(不像相當於例如使用老式類)的原因:

>>> class C(object): 
...  pass 
... 
>>> c = C() 
>>> c.__len__ = lambda: 5 
>>> len(c) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: object of type 'C' has no len() 
8

各種特殊方法(包括__init__,還有運營商過載如__add__等)都是always accessed via the class rather than the instance。不僅如此,它們不能通過類或元類上的__getattr____getattribute__方法訪問,它們必須直接在類上。這是出於效率的原因:

繞過以這種方式__getattribute__()機械提供顯著範圍爲解釋器內的速度最佳化,以在特殊的方法處理的一些靈活性爲代價的(特殊的方法必須設置上類對象本身爲了一致地被解釋器調用)。

這不是完全清楚你要完成什麼,但有一點你可以在這裏做的是對__new__方法中繼承myclass

class myclass(object): 
    def __new__(cls, *args, **kwargs): 
     class subcls(cls): 
      def __new__(cls, *args, **kwargs): 
       return object.__new__(cls) 
     subcls.__init__ = myinit 
     return subcls(*args, **kwargs) 
相關問題