【问题标题】:Python: `__eq__()` doesn't work as expected when subclassesPython:`__eq__()` 在子类时不能按预期工作
【发布时间】:2022-10-12 21:24:13
【问题描述】:

首先,我正在使用类似于PEP-0478 的插件系统工作的项目使用PriortyQueue 来确保插件以正确的顺序运行。我将省略它的工作原理:

  1. 在 PEP-487 中有详细记录
  2. 理解此问题并不直接需要。

    首先,我有以下基本课程:

    
    # abstract.py
    import abc
    
    from dataclasses import data, field
    
    @dataclass(order=True)
    class PluginBase(abc.ABC):
        value: int
    
        def __eq__(self, __o: object) -> bool:
            print("PluginBase __eq__ called")
            return self.value == __o.value
    
    # plugin_a.py
    from abstract import PluginBase
    
    class PluginA(PluginBase):
        pass
    
    # plugin_b.py
    from abstract import PluginBase
    
    class PluginB(PluginBase):
        pass
    
    

    首先,在插件被发现并加载后,它们被包裹在dataclass 中,这是为了确保每个插件的代码量最少。

    问题:__eq__() 永远不会传递给子类。

    我如何设置课程甚至使用funtools.total_ordering 都没有关系。

    我注意到的一件事是,如果你这样做 assert PluginA(1) == PluginB(1) 总是返回 false 并且 assert PluginA(1) < PluginB(2) 总是返回 TypeError: '<' not supported between instances 与当前类。

    这一直是故意的?

    要解决第一个问题,== 不起作用,您需要添加:

        def __eq__(self, __o: object) -> bool:
            print("Plugin __eq__ called")
            return super().__eq__(__o)
    

    向一个或两个子类添加更多我不想要的插件系统的样板代码。

    要修复 '<' 问题,您需要将 PluginBase 更改为:

    @functools.total_ordering
    @dataclass
    class PluginBase(abc.ABC):
        value: int
    
        def __eq__(self, __o: object) -> bool:
            print("PluginBase __eq__ called")
            return self.value == __o.value
    
        def __lt__(self, __o: object) -> bool:
            print("PluginBase __lt__ called")
            return self.value < __o.value
    

    这允许您执行PluginA(1) &lt; PluginB(2),这将是true。但是,== 在这里仍然不起作用。

    我认为这是由于类实例的差异,Python 强制__eq__() 先检查实例类型。反正有没有让这个工作?

    一种解决方案是将每个插件包装到一个可比较的对象中并将其用于队列。

【问题讨论】:

  • 它对我有用,还有三行来定义对象并进行比较。也许你可以发布一个复制品。 (这里指的是==)
  • 1)您的抽象基类没有抽象方法。 2)您的数据类覆盖了您的显式 __eq__ 方法,因为您没有将 eq=False 传递给装饰器。 3) 您的__eq__ 方法错误地假定object 类型的值将具有value 属性。如果没有,您可能应该返回NotImplemented。
  • 您的问题非常令人困惑,因为您在描述问题的代码中包含了 __eq__ 覆盖,因此该代码实际上可以工作,因此很难理解您希望它工作没有它,仅使用数据类自动生成的比较器,这是我在下面理解和回答的

标签: python python-dataclasses


【解决方案1】:

在 python 中,继承 __eq__ 工作得很好

例子:

class C():
  value = 3
  def __eq__(self, other):
    print('A __eq__ called')
    return self.value == other.value

class A(C):
  value=3
class B(C):
  pass
a, b = A(), B()
print(a == b)

但是,如果您想使用注释 @dataclass 中的 __eq__ ,则不能,因为它会比较类类型。您可以在此处的文档中看到这一点 (https://docs.python.org/3/library/dataclasses.html):

eq:如果为 true(默认),将生成一个 __eq__() 方法。这个 方法比较类,就好像它是其字段的元组一样,在 命令。比较中的两个实例必须是相同的类型。

简而言之:如果要比较不同的类,则必须手动定义 eq 方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-26
    • 2013-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-17
    相关资源
    最近更新 更多