【问题标题】:Why are dunder methods inconsistant?为什么dunder方法不一致?
【发布时间】:2021-03-10 13:23:21
【问题描述】:

我很困惑。假设我有一个类(我这样做),其中每个运算符和比较器(+-<>=== 等)都返回自身。如果你不明白,这里是代码:

class _:
    def __init__(self, *args, **kwargs):
        pass
    def __add__(self, other):
        return self
    def __sub__(self, other):
        return self
    def __mul__(self, other):
        return self
    def __truediv__(self, other):
        return self
    def __floordiv__(self, other):
        return self
    def __call__(self, *args, **kwargs):
        return self
    def __eq__(self, other):
        return self
    def __lt__(self, other):
        return self
    def __gt__(self, other):
        return self
    def __ge__(self, other):
        return self
    def __le__(self, other):
        return self

我注意到不一致之处。以下工作:

_()+_
_()-_
_()*_
_()/_
_()//_
_()>_
_()<_
_()==_
_()>=_
_()<=_
_<_()
_>_()
_==_()
_<=_()
_>=_()

但以下不是:

_+_()
_-_()
_*_()
_/_()
_//_()

他们给出以下错误:

Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: unsupported operand type(s) for *: 'type' and '_'

总而言之,比较器可以同时使用类型和实例两种方式,但运算符仅在实例位于运算符左侧时才起作用。这是为什么呢?

【问题讨论】:

    标签: python python-3.x operators comparator python-3.9


    【解决方案1】:

    您在这些比较中使用类型的事实无关紧要且令人困惑。对于任何未实现运算符的任意对象,您都会看到相同的行为。因此,只需创建另一个类class Foo: pass,如果您使用Foo() 实例,您将看到相同的行为。或者只是一个object() 实例。

    无论如何,算术 dunder 方法都有一个交换的参数版本,例如对于__add__,它是__radd__(我认为它是“正确添加”)。如果你有x + y,而x.__add__没有实现,它会尝试使用y.__radd__

    现在,对于比较运算符,没有 __req____rgt__ 运算符。相反,其他运营商自己会这样做。来自docs

    这些方法没有交换参数版本(待使用 当左参数不支持操作但右 论据确实);相反,__lt__()__gt__() 是彼此的 反射,__le__()__ge__() 是彼此的反射,并且 __eq__()__ne__() 是他们自己的反映。

    所以,如果你有左边的类型,例如

    _<_()
    

    然后type.__lt__不存在,所以它尝试_.__gt__,它确实存在。

    演示:

    >>> class Foo:
    ...     def __lt__(self, other):
    ...         print("in Foo.__lt__")
    ...         return self
    ...     def __gt__(self, other):
    ...         print("in Foo.__gt__")
    ...         return self
    ...
    >>> Foo() < Foo
    in Foo.__lt__
    <__main__.Foo object at 0x7fb056f696d0>
    >>> Foo < Foo()
    in Foo.__gt__
    <__main__.Foo object at 0x7fb0580013d0>
    

    同样,您使用实例类型这一事实也无关紧要。任何其他不实现这些运算符的对象都会得到相同的模式:

    >>> Foo() < object()
    in Foo.__lt__
    <__main__.Foo object at 0x7fb056f696d0>
    >>> object() < Foo()
    in Foo.__gt__
    <__main__.Foo object at 0x7fb0580013d0>
    

    【讨论】:

      【解决方案2】:

      这是因为python会这样翻译源代码:

      a + b
      

      进入:

      a.__add__(b)
      

      但是:

      _ + _()
      

      翻译为:

      _.__add__(_())
      

      类_没有__add__(),而实例有。

      【讨论】:

      • 嗯,没错,但这并不能解释为什么比较运算符 do 起作用。
      猜你喜欢
      • 2022-12-11
      • 1970-01-01
      • 1970-01-01
      • 2021-01-20
      • 2022-01-06
      • 2016-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多