假设我有一个Python类,如下所示:
class TestClass():
value = 20
def __str__(self):
return str(self.value)
每次尝试将__str__
的实例用作字符串时,都会自动调用TestClass
方法,就像在print
中一样。是否有任何相当于将其视为一个数字?例如,在
an_object = TestClass()
if an_object > 30:
...
其中会自动调用一些假设的__num__
函数来将对象解释为数字。怎么可以轻松完成?
理想情况下,我希望避免重载每个普通的数学运算符。
答案 0 :(得分:3)
您可以提供__float__()
,__int__()
和/或__complex__()
方法将对象转换为数字。您还可以为自定义舍入提供__round__()
方法。 Documentation here。 __bool__()
方法在技术上也适用于此,因为布尔值是Python中整数的子类。
虽然Python会隐含地将对象转换为字符串,例如print()
,它永远不会在没有你说的情况下将对象转换为数字。因此,Foo() + 42
无效只是因为Foo
具有__int__
方法。您必须明确使用int()
或float()
或complex()
。至少就是这样,通过阅读代码,你就知道自己得到了什么。
要使类实际上像数字一样,您必须为数字参与的操作实现所有特殊方法,包括算术和比较。如你所知,这很烦人。但是,您可以编写一个mixin类,这样至少您只需要编写一次。如:
class NumberMixin(object):
def __eq__(self, other): return self.__num__() == self.__getval__(other)
# other comparison methods
def __add__(self, other): return self.__num__() + self.__getval__(other)
def __radd__(self, other): return self.__getval__(other) + self.__num__()
# etc., I'm not going to write them all out, are you crazy?
这个类需要在与它混合的类中使用两种特殊方法。
__num__()
- 将self
转换为数字。通常,这将是对象支持的最精确类型的转换方法的别名。例如,您的类可能有__int__()
和__float__()
方法,但__int__()
会截断数字,因此您在类定义中指定__num__ = __float__
。另一方面,如果您的类具有自然的整数值,您可能希望提供__float__
,因此它也可以转换为浮点数,但您使用__num__ = __int__
,因为它应该表现得像整数。__getval__()
- 从另一个对象获取数值的静态方法。当您希望能够支持除数字类型以外的对象的操作时,这非常有用。例如,在比较时,您可能希望能够与您自己类型的对象以及传统的数字类型进行比较。您可以编写__getval__()
来删除正确的属性或调用其他对象的正确方法。当然,对于您自己的实例,您可以依靠float()
做正确的事情,但__getval__()
可以让您在接受的内容中保持灵活。使用此mixin的简单示例类:
class FauxFloat(NumberMixin):
def __init__(self, value): self.value = float(value)
def __int__(self): return int(self.value)
def __float__(self): return float(self.value)
def __round__(self, digits=0): return round(self.value, digits)
def __str__(self): return str(self.value)
__repr__ = __str__
__num__ = __float__
@staticmethod
def __getval__(obj):
if isinstance(obj, FauxFloat):
return float(obj)
if hasattr(type(obj), "__num__") and callable(type(obj).__num__):
return type(obj).__num__(obj) # don't call dunder method on instance
try:
return float(obj)
except TypeError:
return int(obj)
ff = FauxFloat(42)
print(ff + 13) # 55.0
要获得额外的功劳,您可以注册您的课程,以便它被视为适当的抽象基类的子类:
import numbers
numbers.Real.register(FauxFloat)
issubclass(FauxFloat, numbers.Real) # True
要获得额外的额外功劳,您还可以创建一个全局num()
函数,在具有它的对象上调用__num__()
,否则将回退到旧方法。
答案 1 :(得分:0)
看起来您需要__gt__
方法。
class A:
val = 0
def __gt__(self, other):
return self.val > other
a = A()
a.val = 12
a > 10
如果您只想将对象转换为int - 您应该定义__int__
方法(或__float__
)。
答案 2 :(得分:0)
在数字的情况下,它有点复杂。但它可能!您必须覆盖您的类操作符以满足您的需求。
operator.__lt__(a, b) # lower than
operator.__le__(a, b) # lower equal
operator.__eq__(a, b) # equal
operator.__ne__(a, b) # not equal
operator.__ge__(a, b) # greater equial
operator.__gt__(a, b) # greater than