猴子修补运算符重载在Python2和Python3中表现不同

时间:2016-10-01 00:40:47

标签: python python-2.7 python-3.x operator-overloading monkeypatching

请考虑以下代码:

var toObj = (ks, vs) => ks.reduce((o,k,i)=> {o[k] = vs[i]; return o;}, {});

var keys=['one', 'two', 'three'],
    values = [1, 2, 3];
var obj = toObj(keys, values);
console.log(obj);

在Python2中(带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) ),输出

from future import print

在Python3中,输出

2.5
2.5

当我尝试实现支持代数运算子集的类型时,我遇到了这种情况。例如,我需要修改laziness的乘法函数:必须推迟一些计算,直到实例与另一个变量相乘。猴子补丁在Python 2中工作,但我注意到它在3中失败了。

为什么会这样? 有没有办法在Python3中获得更灵活的运算符重载?

2 个答案:

答案 0 :(得分:2)

这不是一个monkeypatch。

这可能是一个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所做的是在__mul__个实例上创建x属性。

然后,预计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)

这将引发异常。请注意(object)

答案 1 :(得分: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