我有一个名为MyData
的类,其中定义了__mul__
和__rmul__
(以及所有其他算术运算符)。每当使用这些方法时,它应始终返回类型MyData
的值。但是,我发现a * myDataObj
与myDataObj * a
不同,具体取决于a
的类型。具体来说,如果a
是int
,它工作正常,但如果a
是float
,那么第一个配置返回一个数组(我的对象有一个numpy数组作为成员,MyData.__getitem__
返回该数组的切片),第二个配置返回类型MyData
的正确值。
有没有办法在这样的表达式中确定运算符的调用顺序?
答案 0 :(得分:5)
有没有办法在这样的表达式中确定运算符的调用顺序?
首先,语言参考的Data model部分中描述了确切的规则,特别是“模拟数字类型”小节。
__rfoo__
方法描述如下:
调用这些方法来实现二进制算术运算(
+
,-
,*
,/
,%
,divmod()
,带有反射(交换)操作数的pow()
,**
,<<
,>>
,&
,^
,|
)。仅当左操作数不支持相应操作且操作数具有不同类型时才调用这些函数。 [2]例如,要评估表达式x - y
,其中y
是具有__rsub__()
方法的类的实例,如果y.__rsub__(x)
则调用x.__sub__(y)
}返回NotImplemented
。请注意,三元
pow()
不会尝试调用__rpow__()
(强制规则会变得太复杂)。注意如果右操作数的类型是左操作数类型的子类,并且该子类提供了操作的反射方法,则此方法将在左操作数的非反射方法之前调用。此行为允许子类覆盖其祖先的操作。
将其置于Pythonesque伪代码中,x * y
的评估如下:
if type(y) is type(x):
return x.__mul__(y)
elif type(y) is a subclass of type(x):
try y.__rmul__(x)
otherwise x.__mul__(y)
else:
try x.__mul__(y)
otherwise y.__rmul__(x)
当然,您还可以通过创建单独的类型来动态确定调用顺序,这些类型的方法只是打印其名称并对其进行测试:
class Base(object):
def __mul__(self, lhs): print('Base.mul')
def __rmul__(self, rhs): print('Base.rmul')
class Derived(Base):
def __mul__(self, lhs): print('Derived.mul')
def __rmul__(self, rhs): print('Derived.rmul')
class Unrelated(object):
def __mul__(self, lhs): print('Unrelated.mul')
def __rmul__(self, rhs): print('Unrelated.rmul')
print('Base * Base: ', end='')
Base() * Base()
for x, y in itertools.permutations((Base, Derived, Unrelated), 2):
print('{} * {}: '.format(x.__name__, y.__name__), end='')
x() * y()
内置类型怎么样?
完全相同的规则。由于Base
不是int
或float
的子类,int
和float
都不知道如何乘以它,它们都会调用{ {1}}。你抛出的任何其他不相关的类型也是如此:
Base.__rmul__
我的问题是我从1.5 * myObj和myObj * 1.5获得了不同的结果
有很多原因:
>>> Base() * 2
Base.mul
>>> 2 * Base()
Base.rmul
>>> Base() * 2.5
Base.mul
>>> 2.5 * Base()
Base.rmul
>>> 'sdfsdfsdfds' * Base()
Base.rmul
>>> (lambda: 23) * Base()
Base.rmul
和__mul__
代码执行的操作不同。__rmul__
。