2

考慮下面的代碼:猴修補操作符重載表現不同在Python2 VS Python3

class Foo: 
    def __mul__(self,other): 
     return other/0 
x = Foo() 
x.__mul__ = lambda other:other*0.5 
print(x.__mul__(5)) 
print(x*5) 

在Python2(帶from future import print),此輸出

2.5 
2.5 

在Python3,這個輸出

2.5 

--------------------------------------------------------------------------- 
ZeroDivisionError       Traceback (most recent call last) 
<ipython-input-1-36322c94fe3a> in <module>() 
     5 x.__mul__ = lambda other:other*0.5 
     6 print(x.__mul__(5)) 
----> 7 print(x*5) 

<ipython-input-1-36322c94fe3a> in __mul__(self, other) 
     1 class Foo: 
     2  def __mul__(self,other): 
----> 3   return other/0 
     4 x = Foo() 
     5 x.__mul__ = lambda other:other*0.5 

ZeroDivisionError: division by zero 

我遇到這種情況時,當我試圖實現一種類型sup移植了一部分代數運算。例如,我需要修改懶惰的乘法函數:一些計算必須推遲到實例與另一個變量相乘。猴子補丁在Python 2中工作,但我注意到它在3中失敗。

爲什麼會發生這種情況? 有什麼辦法可以在Python3中獲得更靈活的運算符重載?

回答

2

這不是一個monkeypatch。

這本來是一個猴補丁:

class Foo: 
    def __mul__(self, other): 
     return other/0 

Foo.__mul__ = lambda self,other: other * 0.5 

x = Foo() 
x*9 # prints 4.5 

x.__mul__ = lambda other:other*0.5是創造對x實例__mul__屬性做了什麼。

然後,預計x*5將撥打x.__mul__(5)。它在Python 2中做了。

在Python 3中,它叫做Foo.__mul__(x, 5),所以這個屬性沒有被使用。

Python 2會和Python 3一樣,但它並不是因爲Foo被創建爲舊式類。

這個代碼就相當於爲Python 2和Python 3:

class Foo(object): 
    def __mul__(self,other): 
     return other/0 
x = Foo() 
x.__mul__ = lambda other:other*0.5 
print(x.__mul__(5)) 
print(x*5) 

這將引發異常。請注意0​​。

1

您不能覆蓋實例級別的特殊方法。基於Python的documentation

對於定製類,特殊的方法調用隱含只能保證,如果一個對象的類型定義,而不是在對象的實例字典才能正常工作。

此行爲背後的基本原理在於許多特殊方法,如__hash__()__repr__(),這些方法都由所有對象(包括類型對象)實現。如果這些方法隱含查找使用的傳統的查找過程,類型對象本身的調用時會失敗:

>>> 1 .__hash__() == hash(1) 
True 
>>> int.__hash__() == hash(int) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: descriptor '__hash__' of 'int' object needs an argument 

所以一個簡單的方法是定義你的猴子打補丁的目標之間的定時功能,和你的新方法分配給它:

In [45]: class Foo: 
      def __init__(self, arg): 
       self.arg = arg 
      def __mul__(self,other): 
       return other * self.arg 
      def _mul(self, other): 
       return other/0 

演示:

In [47]: x = Foo(10) 

In [48]: x * 3 
Out[48]: 30 

In [49]: my_func = lambda x: x * 0.5 

In [50]: x._mul = my 
my_func mypub/ 

In [50]: x._mul = my_func 

In [51]: x._mul(4) 
Out[51]: 2.0