7
class A(object): 
    def __init__(self, a, b, c): 
     #super(A, self).__init__() 
     super(self.__class__, self).__init__() 


class B(A): 
    def __init__(self, b, c): 
     print super(B, self) 
     print super(self.__class__, self) 
     #super(B, self).__init__(1, b, c) 
     super(self.__class__, self).__init__(1, b, c) 

class C(B): 
    def __init__(self, c): 
     #super(C, self).__init__(2, c) 
     super(self.__class__, self).__init__(2, c) 
C(3) 

在上面的代碼中,註釋掉的調用看起來是普遍接受的「智能」方式來執行超類初始化。但是,如果類層次結構可能發生變化,我一直在使用未註釋的表單,直到最近。隱式調用父類初始化器

看來,在上述層次結構中的調用超級構造函數B,即B.__init__再次調用,self.__class__實際上是C,不B因爲我一直以爲。

是否有Python的2.x的某種方式,我可以保持適當的MRO(相對於初始化以正確的順序所有父類)調用超構造函數時,而不是在super(B, self).__init__(1, b, c)命名當前類(B在)?

回答

3

簡短的回答:沒有,沒有辦法隱含調用正確__init__與正確的父類的在Python 2.x的正確參數

順便說一句,這裏顯示的代碼是不正確的:如果你使用super()。 __init__,那麼層次結構中的所有類必須在它們的__init__方法中具有相同的簽名。否則,如果您引入使用多重繼承的新子類,則代碼可能會停止工作。

請參閱http://fuhm.net/super-harmful/瞭解更多問題描述(附圖)。

+0

我開始懷疑初始簽名和多重繼承 – 2010-05-04 03:14:34

1

也許你在找什麼是元類?

class metawrap(type): 
    def __new__(mcs,name, bases, dict): 
     dict['bases'] = bases 
     return type.__new__(mcs,name,bases,dict) 

class A(object): 
    def __init__(self): 
     pass 
    def test(self): 
     print "I am class A" 

class B(A): 
    __metaclass__ = metawrap 
    def __init__(self): 
     pass 
    def test(self): 
     par = super(self.bases[0],self) 
     par.__thisclass__.test(self) 
foo = B() 
foo.test() 

版畫「我是A級」

什麼元類確實是壓倒一切的B級(不是對象)的初始創建並確保內置的字典每個B對象現在包含一個鹼基陣列,您可以在其中找到B的所有鹼基類別

+0

我剛剛意識到我正在用大炮(丹麥諺語)射擊麻雀,爲此我表示歉意,但我會讓這一點成爲現實,除非有人決定刪除它。 – 2010-03-01 10:35:50

+0

我寧願不訴諸元類的東西,應該是微不足道的,但謝謝 – 2010-04-30 20:27:34

1

您的代碼與方法解析順序無關。方法解決來自多重繼承,這不是你的例子。您的代碼是完全錯誤的,因爲你認爲self.__class__實際上是同一類的一個地方被定義的方法,這是錯誤的:

>>> class A(object): 
...  def __init__(self): 
...   print self.__class__ 
... 
>>> 
>>> class B(A): 
...  def __init__(self): 
...   A.__init__(self) 
... 
>>> B() 
<class '__main__.B'> 
<__main__.B object at 0x1bcfed0> 
>>> A() 
<class '__main__.A'> 
<__main__.A object at 0x1bcff90> 
>>> 

所以當你要撥打:

super(B, self).__init__(1, b, c) 

你的確呼籲:

# super(self.__class__, self).__init__(1, b, c) 
super(C, self).__init__(1, b, c) 

編輯:試圖更好地回答這個問題。

class A(object): 
    def __init__(self, a): 
     for cls in self.__class__.mro(): 
      if cls is not object: 
       cls._init(self, a) 
    def _init(self, a): 
     print 'A._init' 
     self.a = a 

class B(A): 
    def _init(self, a): 
     print 'B._init' 

class C(A): 
    def _init(self, a): 
     print 'C._init' 

class D(B, C): 
    def _init(self, a): 
     print 'D._init' 


d = D(3) 
print d.a 

打印:

D._init 
B._init 
C._init 
A._init 
3 

(的template pattern修改版本)。

現在,父母的方法實際上被隱含地稱爲,但我必須同意python禪,其中顯式優於隱式,因爲代碼是較少可讀性和增益較差。但要注意,所有_init方法都有相同的參數,您不能完全忘記父母,我不建議這樣做。

對於單繼承,更好的方法是顯式調用父類的方法,而不調用super。這樣做你不必命名當前類,但你仍然必須關心誰是父母的類。

好讀是:how-does-pythons-super-do-the-right-thing在這個問題建議的鏈接和特殊性Python's Super is nifty, but you can't use it

如果層次結構可能會發生改變是糟糕的設計的症狀,並在所有的部件後果誰正在使用的代碼和不應該受到鼓勵。

EDIT 2

另一個例子是我的想法,但它使用元類。 Urwid庫uses metaclass在類中存儲屬性__super,以便您只需訪問該屬性即可。

例:

>>> class MetaSuper(type): 
...  """adding .__super""" 
...  def __init__(cls, name, bases, d): 
...   super(MetaSuper, cls).__init__(name, bases, d) 
...   if hasattr(cls, "_%s__super" % name): 
...    raise AttributeError, "Class has same name as one of its super classes" 
...   setattr(cls, "_%s__super" % name, super(cls)) 
... 
>>> class A: 
... __metaclass__ = MetaSuper 
... def __init__(self, a): 
... self.a = a 
... print 'A.__init__' 
... 
>>> class B(A): 
... def __init__(self, a): 
... print 'B.__init__' 
... self.__super.__init__(a) 
... 
>>> b = B(42) 
B.__init__ 
A.__init__ 
>>> b.a 
42 
>>> 
0

據我所知,以下不常用。但它似乎工作。

給定類定義中的方法總是破壞雙下劃線屬性以包含它們所定義的類的名稱。因此,如果您以名稱改變的形式存儲對實例可以看到的類的引用,您可以在撥打super時使用該功能。

一個例子積攢對象本身的引用,通過在基類實現__new__

def mangle(cls, name): 
    if not name.startswith('__'): 
     raise ValueError('name must start with double underscore') 
    return '_%s%s' % (cls.__name__, name) 

class ClassStasher(object): 
    def __new__(cls, *args, **kwargs): 
     obj = object.__new__(cls) 
     for c in cls.mro(): 
      setattr(obj, mangle(c, '__class'), c) 
     return obj 

class A(ClassStasher): 
    def __init__(self): 
     print 'init in A', self.__class 
     super(self.__class, self).__init__() 

class B(A): 
    def __init__(self): 
     print 'init in B', self.__class 
     super(self.__class, self).__init__() 

class C(A): 
    def __init__(self): 
     print 'init in C', self.__class 
     super(self.__class, self).__init__() 

class D(B, C): 
    def __init__(self): 
     print 'init in D', self.__class 
     super(self.__class, self).__init__() 


d = D()  
print d 

而且,在做類似的事情,但使用一元類和積攢的類對象的__class引用自己:

運行所有這些組合起來,作爲一個源文件:

% python /tmp/junk.py 
init in D <class '__main__.D'> 
init in B <class '__main__.B'> 
init in C <class '__main__.C'> 
init in A <class '__main__.A'> 
<__main__.D object at 0x1004a4a50> 
init in D_meta <class '__main__.D_meta'> 
init in B_meta <class '__main__.B_meta'> 
init in C_meta <class '__main__.C_meta'> 
init in A_meta <class '__main__.A_meta'> 
<__main__.D_meta object at 0x1004a4bd0>