代码之家  ›  专栏  ›  技术社区  ›  Matina G

类实例的Python相等性[重复]

  •  1
  • Matina G  · 技术社区  · 7 年前

    在编写自定义类时,通过 == != 接线员。在Python中,通过实现 __eq__ __ne__

    class Foo:
        def __init__(self, item):
            self.item = item
    
        def __eq__(self, other):
            if isinstance(other, self.__class__):
                return self.__dict__ == other.__dict__
            else:
                return False
    
        def __ne__(self, other):
            return not self.__eq__(other)
    

    你知道更优雅的方法吗?你知道使用上述比较方法有什么特别的缺点吗 __dict__ s

    笔记 :澄清一下——什么时候 __情商__ __东北__ 如果未定义,您将发现以下行为:

    >>> a = Foo(1)
    >>> b = Foo(1)
    >>> a is b
    False
    >>> a == b
    False
    

    就是, a == b 评估为 False 因为它真的在运行 a is b ,身份测试(即“是 a b ?").

    什么时候 __情商__ __东北__ 定义后,您将发现以下行为(即我们所关注的行为):

    >>> a = Foo(1)
    >>> b = Foo(1)
    >>> a is b
    False
    >>> a == b
    True
    
    0 回复  |  直到 17 年前
        1
  •  396
  •   Tal Weiss    6 年前

    考虑这个简单的问题:

    class Number:
    
        def __init__(self, number):
            self.number = number
    
    
    n1 = Number(1)
    n2 = Number(1)
    
    n1 == n2 # False -- oops
    

    因此,Python默认使用对象标识符进行比较操作:

    id(n1) # 140400634555856
    id(n2) # 140400634555920
    

    凌驾 __eq__

    def __eq__(self, other):
        """Overrides the default implementation"""
        if isinstance(other, Number):
            return self.number == other.number
        return False
    
    
    n1 == n2 # True
    n1 != n2 # True in Python 2 -- oops, False in Python 3
    

    在里面 Python 2 ,始终记得覆盖 __ne__ 功能以及 documentation 国家:

    比较运算符之间没有隐含的关系。这个 真理 x==y 这并不意味着 x!=y 这是错误的。因此,当 决定性的 __eq__() ,我们还应该定义 __ne__() 所以 操作员将按预期操作。

    def __ne__(self, other):
        """Overrides the default implementation (unnecessary in Python 3)"""
        return not self.__eq__(other)
    
    
    n1 == n2 # True
    n1 != n2 # False
    

    Python 3 ,这不再是必需的,因为 documentation 国家:

    默认情况下, __ne_uuuuu() 代表 __等式 并反转结果 NotImplemented . 没有其他暗示 比较运算符之间的关系,例如,真值 (x<y or x==y) x<=y

    但这并不能解决我们所有的问题。让我们添加一个子类:

    class SubNumber(Number):
        pass
    
    
    n3 = SubNumber(1)
    
    n1 == n3 # False for classic-style classes -- oops, True for new-style classes
    n3 == n1 # True
    n1 != n3 # True for classic-style classes -- oops, False for new-style classes
    n3 != n1 # False
    

    注: Python 2有两种类型的类:

    • classic-style (或 旧式 )班级,那就行了 继承自 object 并声明为 class A: , class A(): class A(B): 哪里 B 是一个经典风格的班级;

    • new-style 类,这些类从 并声明为 class A(object) 哪里 B 是一个新型的班级。Python3只有声明为 A类: , class A(object): .

    regardless of the order of the operands

    那么这里,如果 Number 是一个经典风格的课程:

    • n1 == n3 电话 n1.__eq__ ;
    • n3 == n1 电话 n3.__eq__ ;
    • n1 != n3 电话 n1.__ne__ ;
    • n3 != n1 电话 n3.__ne__ .

    是一个新型类:

    • 二者都 n1==n3 n3==n1 ;
    • 二者都 n1!=n3 n3!=n1 呼叫 第3条__

    要解决 == != __情商__ __东北__ 不支持操作数类型时的值。这个 documentation 定义 价值为:

    如果出现以下情况,数值方法和富比较方法可能会返回此值: 它们不会对提供的操作数执行操作。(修订) 回退,取决于运算符。)其真值为真。

    在这种情况下,操作员将比较操作委托给 反射法 另外 documentation 将反射方法定义为:

    这些方法(要使用)没有交换的参数版本 当左参数不支持该操作,但支持右参数时 论据确实如此);相当地 __lt__() __gt__() 彼此都是 __le__() __ge__() 是彼此的反映,和 __等式 __ne_uuuuu() 都是他们自己的反映。

    def __eq__(self, other):
        """Overrides the default implementation"""
        if isinstance(other, Number):
            return self.number == other.number
        return NotImplemented
    
    def __ne__(self, other):
        """Overrides the default implementation (unnecessary in Python 3)"""
        x = self.__eq__(other)
        if x is NotImplemented:
            return NotImplemented
        return not x
    

    归还 未实施 价值代替 False 即使对于新样式的类,如果 == != 如果操作数属于不相关类型(无继承),则需要运算符。

    我们到了吗?不完全是。我们有多少唯一的号码?

    len(set([n1, n2, n3])) # 3 -- oops
    

    def __hash__(self):
        """Overrides the default implementation"""
        return hash(tuple(sorted(self.__dict__.items())))
    
    len(set([n1, n2, n3])) # 1
    

    最终结果如下所示(我在最后添加了一些断言以进行验证):

    class Number:
    
        def __init__(self, number):
            self.number = number
    
        def __eq__(self, other):
            """Overrides the default implementation"""
            if isinstance(other, Number):
                return self.number == other.number
            return NotImplemented
    
        def __ne__(self, other):
            """Overrides the default implementation (unnecessary in Python 3)"""
            x = self.__eq__(other)
            if x is not NotImplemented:
                return not x
            return NotImplemented
    
        def __hash__(self):
            """Overrides the default implementation"""
            return hash(tuple(sorted(self.__dict__.items())))
    
    
    class SubNumber(Number):
        pass
    
    
    n1 = Number(1)
    n2 = Number(1)
    n3 = SubNumber(1)
    n4 = SubNumber(4)
    
    assert n1 == n2
    assert n2 == n1
    assert not n1 != n2
    assert not n2 != n1
    
    assert n1 == n3
    assert n3 == n1
    assert not n1 != n3
    assert not n3 != n1
    
    assert not n1 == n4
    assert not n4 == n1
    assert n1 != n4
    assert n4 != n1
    
    assert len(set([n1, n2, n3, ])) == 1
    assert len(set([n1, n2, n3, n4])) == 2
    
        2
  •  217
  •   Algorias    17 年前

    您需要小心继承:

    >>> class Foo:
        def __eq__(self, other):
            if isinstance(other, self.__class__):
                return self.__dict__ == other.__dict__
            else:
                return False
    
    >>> class Bar(Foo):pass
    
    >>> b = Bar()
    >>> f = Foo()
    >>> f == b
    True
    >>> b == f
    False
    

    更严格地检查类型,如下所示:

    def __eq__(self, other):
        if type(other) is type(self):
            return self.__dict__ == other.__dict__
        return False
    

    除此之外,您的方法将很好地工作,这就是特殊方法的用途。

        3
  •  162
  •   CTKeane    15 年前

    你描述的方式就是我一直做的方式。因为它是完全通用的,所以您可以将该功能分解为一个mixin类,并在您想要该功能的类中继承它。

    class CommonEqualityMixin(object):
    
        def __eq__(self, other):
            return (isinstance(other, self.__class__)
                and self.__dict__ == other.__dict__)
    
        def __ne__(self, other):
            return not self.__eq__(other)
    
    class Foo(CommonEqualityMixin):
    
        def __init__(self, item):
            self.item = item
    
        4
  •  17
  •   Aaron    7 年前

    这不是一个直接的答案,但似乎有足够的相关性,可以加上,因为它有时会节省一些冗长乏味的内容。直接从文档中剪切。。。


    functools.total_ordering(cls)

    给定一个定义了一个或多个丰富比较排序方法的类,这个类装饰器提供其余的方法。 这简化了指定所有可能的富比较操作所涉及的工作:

    该类必须定义其中一个 __lt__() , __le__() , __gt__() __ge__() . 此外,该类还应提供 __eq__() 方法

    @total_ordering
    class Student:
        def __eq__(self, other):
            return ((self.lastname.lower(), self.firstname.lower()) ==
                    (other.lastname.lower(), other.firstname.lower()))
        def __lt__(self, other):
            return ((self.lastname.lower(), self.firstname.lower()) <
                    (other.lastname.lower(), other.firstname.lower()))
    
        5
  •  9
  •   Matt Nelson    13 年前

    您不必同时覆盖这两者 __eq__ __ne__ __cmp__ 但这将对==,!==,的结果产生影响<&燃气轮机;等等

    is 测试对象标识。这意味着 b将是 True 在a和b都持有对同一对象的引用的情况下。在python中,始终在变量中保留对对象的引用,而不是实际对象,因此基本上,如果a是b,那么它们中的对象应该位于相同的内存位置。最重要的是,您将如何着手克服这种行为?

    __化学机械抛光__ 已从python 3中删除,因此请避免使用它。

        6
  •  6
  •   Aaron Hall    9 年前

    https://stackoverflow.com/a/30676267/541136 我已经证明了这一点,尽管定义是正确的 __ne__ __eq__ -而不是

    def __ne__(self, other):
        return not self.__eq__(other)
    

    你应使用:

    def __ne__(self, other):
        return not self == other
    
        7
  •  4
  •   too much php    17 年前

    我想你要找的两个术语是 平等 (==)和 身份 (是)。例如:

    >>> a = [1,2,3]
    >>> b = [1,2,3]
    >>> a == b
    True       <-- a and b have values which are equal
    >>> a is b
    False      <-- a and b are not the same list object
    
        8
  •  2
  •   mcrute    17 年前

    “is”测试将使用内置的“id()”函数测试标识,该函数本质上返回对象的内存地址,因此不可重载。

    但是,在测试类的相等性时,您可能希望对测试更加严格,只比较类中的数据属性:

    import types
    
    class ComparesNicely(object):
    
        def __eq__(self, other):
            for key, value in self.__dict__.iteritems():
                if (isinstance(value, types.FunctionType) or 
                        key.startswith("__")):
                    continue
    
                if key not in other.__dict__:
                    return False
    
                if other.__dict__[key] != value:
                    return False
    
             return True
    

    这段代码将只比较类的非函数数据成员,并跳过任何您通常想要的私有数据。对于普通的老Python对象,我有一个基类,它实现了uuu init_uuuuuu、uuuu str_uuu、uuuu repr_uuu和uuuuu eq_uuu,因此我的POPO对象不会承担所有额外(在大多数情况下是相同的)逻辑的负担。

        9
  •  2
  •   bluenote10    7 年前

    def comparable(cls):
        """ Class decorator providing generic comparison functionality """
    
        def __eq__(self, other):
            return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
    
        def __ne__(self, other):
            return not self.__eq__(other)
    
        cls.__eq__ = __eq__
        cls.__ne__ = __ne__
        return cls
    

    用法:

    @comparable
    class Number(object):
        def __init__(self, x):
            self.x = x
    
    a = Number(1)
    b = Number(1)
    assert a == b
    
        10
  •  2
  •   Will Charczuk    6 年前

    这包含了对Algorias答案的评论,并通过单个属性比较对象,因为我不关心整个dict。 hasattr(other, "id") 必须是真的,但我知道这是因为我在构造函数中设置了它。

    def __eq__(self, other):
        if other is self:
            return True
    
        if type(other) is not type(self):
            # delegate to superclass
            return NotImplemented
    
        return other.id == self.id
    
        11
  •  0
  •   avf    5 年前

    我编写了一个带有默认实现的自定义基 __ne__ 这完全是否定的 __eq__

    class HasEq(object):
      """
      Mixin that provides a default implementation of ``object.__neq__`` using the subclass's implementation of ``object.__eq__``.
    
      This overcomes Python's deficiency of ``==`` and ``!=`` not being symmetric when overloading comparison operators
      (i.e. ``not x == y`` *does not* imply that ``x != y``), so whenever you implement
      `object.__eq__ <https://docs.python.org/2/reference/datamodel.html#object.__eq__>`_, it is expected that you
      also implement `object.__ne__ <https://docs.python.org/2/reference/datamodel.html#object.__ne__>`_
    
      NOTE: in Python 3+ this is no longer necessary (see https://docs.python.org/3/reference/datamodel.html#object.__ne__)
      """
    
      def __ne__(self, other):
        """
        Default implementation of ``object.__ne__(self, other)``, delegating to ``self.__eq__(self, other)``.
    
        When overriding ``object.__eq__`` in Python, one should also override ``object.__ne__`` to ensure that
        ``not x == y`` is the same as ``x != y``
        (see `object.__eq__ <https://docs.python.org/2/reference/datamodel.html#object.__eq__>`_ spec)
    
        :return: ``NotImplemented`` if ``self.__eq__(other)`` returns ``NotImplemented``, otherwise ``not self.__eq__(other)``
        """
        equal = self.__eq__(other)
        # the above result could be either True, False, or NotImplemented
        if equal is NotImplemented:
          return NotImplemented
        return not equal
    

    __情商__ 还有基地。

    @functools.total_ordering