2012-07-27 120 views
2

我想弄明白爲什麼下面的例子不起作用。重寫猴子修補的方法

class BaseClass(object): 
    def __init__(self): 
     self.count = 1 

    def __iter__(self): 
     return self 

    def next(self): 
     if self.count: 
      self.count -= 1 
      return self 
     else: 
      raise StopIteration 


class DerivedNO(BaseClass): 
    pass 


class DerivedO(BaseClass): 
    def __init__(self): 
     self.new_count = 2 
     self.next = self.new_next 

    def new_next(self): 
     if self.new_count: 
      self.new_count -= 1 
      return None 
     else: 
      raise StopIteration 


x = DerivedNO() 
y = DerivedO() 

print x 
print list(x) 
print y 
print list(y) 

這裏是輸出:

<__main__.DerivedNO object at 0x7fb2af7d1c90> 
[<__main__.DerivedNO object at 0x7fb2af7d1c90>] 
<__main__.DerivedO object at 0x7fb2af7d1d10> 
Traceback (most recent call last): 
    File "playground.py", line 41, in <module> 
    print list(y) 
    File "playground.py", line 11, in next 
    if self.count: 
AttributeError: 'DerivedO' object has no attribute 'count' 

正如你可以看到新的方法不會在DerivedO被覆蓋,當我嘗試在__init__分配next()方法。這是爲什麼?對下一個簡單的調用會很好,但在使用迭代技術時根本不會。

編輯:我意識到我的問題並不完全清楚。 AttributeError不是我想要解決的問題。但它確實顯示next()BaseClass上被調用,而不是在我想象的DerivedO上。

+1

我想你只需要在派生類的構造函數中調用基類「__init__」。 – sje397 2012-07-27 12:23:45

+1

@ sje397:這就是要點; 'new_next'方法不會引用'count'。 – 2012-07-27 12:29:22

+0

有趣的是,當從'__iter__'返回'self'時,使用類定義的'next',不管在實例上是否設置了next。 '__iter__'方法同樣適用,使用'yield'方法修補它也被忽略。 – 2012-07-27 12:34:20

回答

4

你,因爲這些方法爲類方法,而不是作爲一個CPython的內部優化(見Special method lookup for new-style classes一個深入的理由,爲什麼這是)治療不能實例猴補丁或者__iter__(self),或者推而廣之,next(self)

如果需要猴補丁這些方法,你需要設置它們直接的類,而不是:

class DerivedO(BaseClass): 
    def __init__(self): 
     self.new_count = 2 
     self.__class__.next = self.__class__.new_next 

    def new_next(self): 
     if self.new_count: 
      self.new_count -= 1 
      return None 
     else: 
      raise StopIteration 

以上將工作;請注意,我將__class__.next設置爲未綁定函數new_next,而不是綁定方法。

+0

啊我明白了,謝謝。每當我想我知道Python時,總會發現它還有更多的東西! – Fulkerson 2012-07-27 12:52:51

+0

這會起作用,但值得一提的是,在這一點上,您可以使用標準語法重寫該方法(即在'new_next'的定義之後放置'next = new_next')。你在這裏使用的方法相當模糊! – senderle 2012-07-27 12:55:57

+0

@senderle:我假設OP已經構建了一個例子來說明他的問題。monkeypatching也可能發生在他或她的控制之外的課堂上,在這一點上我的答案背後的理論仍然適用。 – 2012-07-27 12:57:39

-1

由於DerivedO從不初始化count屬性,因此執行next方法時會發生AttributeError。

您可以通過安排BaseClass.__init__避免這個錯誤被稱爲(顯式或使用super):

class DerivedO(BaseClass): 
    def __init__(self): 
     super(DerivedO, self).__init__() 
     self.new_count = 2 

    def next(self): 
     if self.new_count: 
      self.new_count -= 1 
      return None 
     else: 
      raise StopIteration 

此外,而不是定義new_next,你可以簡單地覆蓋(重新)next

+2

雖然如此, OP有一個有趣的問題。爲什麼你不能*接下來的monkeypatch?迭代器使用類定義的next方法,而不是實例重載。你在這裏沒有解決真正的問題。 – 2012-07-27 12:32:48