像__str__这样的特殊方法,它返回一个对象的数字表示

时间:2018-02-27 17:03:26

标签: python class

假设我有一个Python类,如下所示:

class TestClass():
    value = 20

    def __str__(self):
        return str(self.value)

每次尝试将__str__的实例用作字符串时,都会自动调用TestClass方法,就像在print中一样。是否有任何相当于将其视为一个数字?例如,在

an_object = TestClass()
if an_object > 30:
    ...

其中会自动调用一些假设的__num__函数来将对象解释为数字。怎么可以轻松完成?

理想情况下,我希望避免重载每个普通的数学运算符。

3 个答案:

答案 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

Python Operators