【问题标题】:__add__ to support addition of different types?__add__ 支持不同类型的添加?
【发布时间】:2019-09-06 23:05:54
【问题描述】:

如果 python 是一种支持重载的静态编程语言,那将很容易解决。我正在创建一个名为 Complex 的类,它表示复数(我知道 python 有它自己的,但我想自己做一个),其中 a 是实数,b 是虚数(Complex(a, b))。它应该支持将复杂实例添加在一起 ​​(Complex(2, 4) + Complex(4, 5) = Complex(6, 9)),以及添加整数 (Complex(2, 3) + 4 = Complex(6, 3))。但是,由于python的性质...

__add__(self, other):

...我必须选择要支持的类,因为它在编译时无法识别类型,也不支持函数重载。什么是最好的解决方案?我是否必须针对 other 参数的数据类型编写 if 语句?

【问题讨论】:

  • 如果你想支持添加不同类型的对象,我认为最好的选择是使用single dispatcher,尽管再想一想这可能有点矫枉过正。试试看,看看你喜欢什么:)

标签: python overloading add


【解决方案1】:

您可以做的是检查实例 Complex 的事物,如果没有,则将其变成一个,如下所示:

def __add__(self, other):
    if isinstance(other, Complex):
        # do addition
    else:
        return self + Complex(other, 0)

这当然不会消除类型检查,但它会重用您在 __init__ 中所做的任何事情(这可能是检查输入是 int 还是 float)。

如果此时您不在 init 中进行类型检查,这可能是一个好主意,并且this 看起来很合理,除了内置的复杂类型。

【讨论】:

    【解决方案2】:

    不一定有最佳解决方案。但是,在这种特殊情况下:

    def __add__(self, other):
        c = make_complex(other)
        return Complex(self.real + c.real, self.imag + real.imag)
    

    可能是要走的路(尽管我在这里对您的Complex 课程做了很多假设)。如果other 已经是Complex,则make_complex 函数会返回它。如果不是,它会尽最大努力进行转换(例如,通过构造一个虚部为零的复数,将仅实数转换为复数对)。如果失败,它会引发一些合适的异常。

    这个make_complex也适合Complex的构造函数,这样你就可以替换部分:

    e = Complex(1.718, 0) # e (well, not very exactly)
    i = Complex(0, 1) # sqrt(-1)
    pi = Complex(3.14, 0) # pi
    # you know what to do next
    

    与:

    e = Complex(1.718)
    pi = make_complex(3.14)
    

    例如。 (您可以只使用Complex 构造函数来完成所有工作,使用isinstance() 来检查参数类型是否适当。)

    请注意,由于复数加法是可交换的,您可能还希望实现 __radd__

    【讨论】:

    • 感谢您对__add____iadd__ 之间区别的说明
    【解决方案3】:

    如果不是任何类型的数字,则使用 isinstance 检查它是否是相同的类型:

      def __add__(self, other):
           # it's the same class
           if isinstance(other, Complex):
                # and you should return the same class 
                # if anyone extend your class SomeClass(Complex): you should return SomeClass not Complex Object
                return self.__class__(self.a + other.a, self.b + other.b)
    
           # assuming is any type of number
           try:
             return self.__class__(int(self.a + other), self.b)
           except TypeError:
             # change the error message
             raise TypeError("unsupported operand type(s) for +: '%s' and '%s'" % (self.__class__, other.__class__)
    

    【讨论】:

    • 那是__iadd__,就地添加。根据复数是否应该在适当的位置进行修改,您可能想要也可能不想要那个。
    • 是的,我忘记了 add 应该返回同一个类的对象^^
    • 返回时请确保返回同一类的对象而不是复杂类
    • 如果您希望 Complex 能够被子类化,而不强制子类提供自己的功能,是的,使用 self.__class__ 构造返回值。
    • 总是推荐使用这种技术更安全。
    【解决方案4】:

    混合类型操作

    Python 中的numbers 模块可用于实现您自己的数字类。其中allows to correctly implement mixed-types operations 使用__add____radd__

    示例

    import numbers
    
    class Complex:
        def __add__(self, other):
            if isinstance(self, Complex):
                ...
            elif isinstance(other, numbers.Real):
                ...
            else:
                raise TypeError
    
        def __radd__(self, other):
            return self + other
    

    实现新的数字类型

    如果你想实现一个与 Python 内置数字类型一起工作的数字类,你可以通过继承抽象基类 numbers.Complex 来实现你自己的 Complex 类。

    这个抽象基类将强制实现所需方法__abs____add____complex____eq____mul____neg____neg____pos____pow____radd____rmul____rpow____rtruediv____truediv__conjugateimagreal

    【讨论】:

      【解决方案5】:

      这里有什么问题?

      您可以随时检查 python 对象的类型:

      if type(other) != type(self):
          # raise some error
      # do addition
      return 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-08-08
        • 2022-01-22
        • 2023-03-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多