如何使我的 class 对 operator/function 重载更稳健?
How can I make my class more robust to operator/function overloading?
我正在写一个class,其中对象用两个参数(a
、b
)初始化。目的是将此 class 的实例分配给变量,以便我可以用 Python 代码符号化地编写一个等式,但让运算符重载对 a
和 [=16= 执行独特的操作].
import numpy as np
class my_class(object):
def __init__(self, a, b):
self.value1 = a
self.value2 = b
# Example of an overloaded operator that works just fine
def __mul__(self, other):
new_a = self.value1 * other
new_b = self.value2 * np.absolute(other)
return my_class(new_a, new_b)
if __name__ == "__main__":
my_object = my_class(100, 1)
print(np.exp(my_object)) # This doesn't work!
在运行上面的示例代码中,我遇到了如下输出:
TypeError: loop of ufunc does not support argument 0 of type my_class which has no callable exp method
通过猜测,我能够看到关于 no callable exp method
的投诉可能意味着我需要在我的 class:
中定义一个新方法
def exp(self):
new_val1 = np.exp(self.value1)
new_val2 = np.absolute(new_val1) * self.value2
return my_class(new_val1, new_val2)
最终运行良好。但是现在我将不得不根据需要为 np.expm1()
等编写另一个方法。谢天谢地,我只需要 np.exp()
和 np.log()
就可以工作,但我也在我的对象上尝试了 math.exp()
,但我开始出现类型错误。
所以现在我的问题是:
class 中的自定义 exp
方法似乎适用于 重载 NumPy 函数,但我应该如何处理 math.exp()
不工作?这一定是可能的,因为在 NumPy 数组上调用 math.exp()
时,NumPy 知道可以将 1 元素数组转换为标量,然后毫无问题地传递给 math.exp()
。
我的意思是我猜这在技术上是关于重载一个函数,但在我意识到定义一个新的 exp
是解决我的第一个问题之前,我不知道为什么像 __rpow__
这样的方法没有被调用.
np.exp(my_object)
实现为 np.exp(np.array(my_object))
.
np.array(my_object)
是一个对象数据类型数组。 np.exp
对数组的每个元素尝试 elmt.exp()
。这对大多数 classes 不起作用,因为它们没有实现这样的方法。
同样适用于运算符和其他 ufunc
。
math.exp
是一个不相关的实现。它显然适用于提供单个数值的东西,但我还没有探索那么多。 numpy
如果不能这样做,将引发错误。
用 class __mul__
实现 *
由解释器完成。
在 math.exp
和 __float__()
中使用数组时出现相同的错误消息
In [52]: math.exp(np.array([1,2,3]))
Traceback (most recent call last):
File "<ipython-input-52-40503a52084a>", line 1, in <module>
math.exp(np.array([1,2,3]))
TypeError: only size-1 arrays can be converted to Python scalars
In [53]: np.array([1,2,3]).__float__()
Traceback (most recent call last):
File "<ipython-input-53-0bacdf9df4e7>", line 1, in <module>
np.array([1,2,3]).__float__()
TypeError: only size-1 arrays can be converted to Python scalars
类似地,当在布尔上下文中使用数组时(例如 if
),我们可以通过
生成错误
In [55]: np.array([1,2,3]).__bool__()
Traceback (most recent call last):
File "<ipython-input-55-04aca1612817>", line 1, in <module>
np.array([1,2,3]).__bool__()
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
类似地,在 if
中使用 sympy
关系会导致
产生错误
In [110]: (x>0).__bool__()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-110-d88b76ce6b22> in <module>
----> 1 (x>0).__bool__()
/usr/local/lib/python3.8/dist-packages/sympy/core/relational.py in __bool__(self)
396
397 def __bool__(self):
--> 398 raise TypeError("cannot determine truth value of Relational")
399
400 def _eval_as_set(self):
TypeError: cannot determine truth value of Relational
pandas
系列产生类似的错误。
首先,为什么 math.exp
使用 1 元素 Numpy 数组:numpy.ndarray
class has a __float__
method; this method is part of the Python data model for numeric types, and is used when float(x)
is called. I couldn't spot anything in the math
docs 表示 math.exp
将其参数转换为float
,但这并不是不合理的行为。
至于 Numpy 的 ufunc 的自定义行为:实现覆盖 ufunc 行为的“类数组”对象的推荐方法是 somewhat complicated。我找不到有关提供 exp
、log
等自定义 ufunc 方法的文档。像这样提供方法并非在所有情况下都有效,例如 np.heaviside
;这个例子
import numpy as np
class foo:
def exp(self):
return foo()
def heaviside(self, other):
return foo()
print(f'{np.exp(foo()) = }')
print(f'{np.heaviside(foo(), foo()) = }')
给出此输出:
np.exp(foo()) = <__main__.foo object at 0x7efcdbba1ac0>
Traceback (most recent call last):
File "/home/rory/hack/Whosebug/q70312146/heaviside.py", line 14, in <module>
print(f'{np.heaviside(foo(), foo()) = }')
TypeError: ufunc 'heaviside' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
我正在写一个class,其中对象用两个参数(a
、b
)初始化。目的是将此 class 的实例分配给变量,以便我可以用 Python 代码符号化地编写一个等式,但让运算符重载对 a
和 [=16= 执行独特的操作].
import numpy as np
class my_class(object):
def __init__(self, a, b):
self.value1 = a
self.value2 = b
# Example of an overloaded operator that works just fine
def __mul__(self, other):
new_a = self.value1 * other
new_b = self.value2 * np.absolute(other)
return my_class(new_a, new_b)
if __name__ == "__main__":
my_object = my_class(100, 1)
print(np.exp(my_object)) # This doesn't work!
在运行上面的示例代码中,我遇到了如下输出:
TypeError: loop of ufunc does not support argument 0 of type my_class which has no callable exp method
通过猜测,我能够看到关于 no callable exp method
的投诉可能意味着我需要在我的 class:
def exp(self):
new_val1 = np.exp(self.value1)
new_val2 = np.absolute(new_val1) * self.value2
return my_class(new_val1, new_val2)
最终运行良好。但是现在我将不得不根据需要为 np.expm1()
等编写另一个方法。谢天谢地,我只需要 np.exp()
和 np.log()
就可以工作,但我也在我的对象上尝试了 math.exp()
,但我开始出现类型错误。
所以现在我的问题是:
class 中的自定义 exp
方法似乎适用于 重载 NumPy 函数,但我应该如何处理 math.exp()
不工作?这一定是可能的,因为在 NumPy 数组上调用 math.exp()
时,NumPy 知道可以将 1 元素数组转换为标量,然后毫无问题地传递给 math.exp()
。
我的意思是我猜这在技术上是关于重载一个函数,但在我意识到定义一个新的 exp
是解决我的第一个问题之前,我不知道为什么像 __rpow__
这样的方法没有被调用.
np.exp(my_object)
实现为 np.exp(np.array(my_object))
.
np.array(my_object)
是一个对象数据类型数组。 np.exp
对数组的每个元素尝试 elmt.exp()
。这对大多数 classes 不起作用,因为它们没有实现这样的方法。
同样适用于运算符和其他 ufunc
。
math.exp
是一个不相关的实现。它显然适用于提供单个数值的东西,但我还没有探索那么多。 numpy
如果不能这样做,将引发错误。
用 class __mul__
实现 *
由解释器完成。
在 math.exp
和 __float__()
In [52]: math.exp(np.array([1,2,3]))
Traceback (most recent call last):
File "<ipython-input-52-40503a52084a>", line 1, in <module>
math.exp(np.array([1,2,3]))
TypeError: only size-1 arrays can be converted to Python scalars
In [53]: np.array([1,2,3]).__float__()
Traceback (most recent call last):
File "<ipython-input-53-0bacdf9df4e7>", line 1, in <module>
np.array([1,2,3]).__float__()
TypeError: only size-1 arrays can be converted to Python scalars
类似地,当在布尔上下文中使用数组时(例如 if
),我们可以通过
In [55]: np.array([1,2,3]).__bool__()
Traceback (most recent call last):
File "<ipython-input-55-04aca1612817>", line 1, in <module>
np.array([1,2,3]).__bool__()
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
类似地,在 if
中使用 sympy
关系会导致
In [110]: (x>0).__bool__()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-110-d88b76ce6b22> in <module>
----> 1 (x>0).__bool__()
/usr/local/lib/python3.8/dist-packages/sympy/core/relational.py in __bool__(self)
396
397 def __bool__(self):
--> 398 raise TypeError("cannot determine truth value of Relational")
399
400 def _eval_as_set(self):
TypeError: cannot determine truth value of Relational
pandas
系列产生类似的错误。
首先,为什么 math.exp
使用 1 元素 Numpy 数组:numpy.ndarray
class has a __float__
method; this method is part of the Python data model for numeric types, and is used when float(x)
is called. I couldn't spot anything in the math
docs 表示 math.exp
将其参数转换为float
,但这并不是不合理的行为。
至于 Numpy 的 ufunc 的自定义行为:实现覆盖 ufunc 行为的“类数组”对象的推荐方法是 somewhat complicated。我找不到有关提供 exp
、log
等自定义 ufunc 方法的文档。像这样提供方法并非在所有情况下都有效,例如 np.heaviside
;这个例子
import numpy as np
class foo:
def exp(self):
return foo()
def heaviside(self, other):
return foo()
print(f'{np.exp(foo()) = }')
print(f'{np.heaviside(foo(), foo()) = }')
给出此输出:
np.exp(foo()) = <__main__.foo object at 0x7efcdbba1ac0>
Traceback (most recent call last):
File "/home/rory/hack/Whosebug/q70312146/heaviside.py", line 14, in <module>
print(f'{np.heaviside(foo(), foo()) = }')
TypeError: ufunc 'heaviside' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''