标签归档:equality

Python如果不是== vs如果!=

问题:Python如果不是== vs如果!=

这两行代码有什么区别:

if not x == 'val':

if x != 'val':

一个比另一个更有效吗?

会更好用吗

if x == 'val':
    pass
else:

What is the difference between these two lines of code:

if not x == 'val':

and

if x != 'val':

Is one more efficient than the other?

Would it be better to use

if x == 'val':
    pass
else:

回答 0

使用dis一下两个版本生成的字节码:

not ==

  4           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               2 (==)
              9 UNARY_NOT           
             10 RETURN_VALUE   

!=

  4           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               3 (!=)
              9 RETURN_VALUE   

后者的操作较少,因此效率可能会略高一些。


致谢中指出了(感谢@Quincunx),您所进行的操作if foo != barif not foo == bar操作数量完全相同,只是COMPARE_OP更改并POP_JUMP_IF_TRUE切换为POP_JUMP_IF_FALSE

not ==

  2           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               2 (==)
              9 POP_JUMP_IF_TRUE        16

!=

  2           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               3 (!=)
              9 POP_JUMP_IF_FALSE       16

在这种情况下,除非每次比较所需的工作量有所不同,否则您根本看不到任何性能差异。


但是,请注意,这两个版本在逻辑上并不总是相同的,因为这将取决于所涉及对象的实现__eq____ne__针对对象的实现。根据数据模型文档

比较运算符之间没有隐含的关系。的真相x==y并不意味着那x!=y是错误的。

例如:

>>> class Dummy(object):
    def __eq__(self, other):
        return True
    def __ne__(self, other):
        return True


>>> not Dummy() == Dummy()
False
>>> Dummy() != Dummy()
True

最后,也许是最重要的一点:通常,两者逻辑上是相同的,x != y但比更具可读性not x == y

Using dis to look at the bytecode generated for the two versions:

not ==

  4           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               2 (==)
              9 UNARY_NOT           
             10 RETURN_VALUE   

!=

  4           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               3 (!=)
              9 RETURN_VALUE   

The latter has fewer operations, and is therefore likely to be slightly more efficient.


It was pointed out in the commments (thanks, @Quincunx) that where you have if foo != bar vs. if not foo == bar the number of operations is exactly the same, it’s just that the COMPARE_OP changes and POP_JUMP_IF_TRUE switches to POP_JUMP_IF_FALSE:

not ==:

  2           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               2 (==)
              9 POP_JUMP_IF_TRUE        16

!=

  2           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               3 (!=)
              9 POP_JUMP_IF_FALSE       16

In this case, unless there was a difference in the amount of work required for each comparison, it’s unlikely you’d see any performance difference at all.


However, note that the two versions won’t always be logically identical, as it will depend on the implementations of __eq__ and __ne__ for the objects in question. Per the data model documentation:

There are no implied relationships among the comparison operators. The truth of x==y does not imply that x!=y is false.

For example:

>>> class Dummy(object):
    def __eq__(self, other):
        return True
    def __ne__(self, other):
        return True


>>> not Dummy() == Dummy()
False
>>> Dummy() != Dummy()
True

Finally, and perhaps most importantly: in general, where the two are logically identical, x != y is much more readable than not x == y.


回答 1

@jonrsharpe对发生的事情有很好的解释。我以为只是将3个选项中的每一个运行10,000,000次时,就会显示出时间差异(足以显示出一点差异)。

使用的代码:

def a(x):
    if x != 'val':
        pass


def b(x):
    if not x == 'val':
        pass


def c(x):
    if x == 'val':
        pass
    else:
        pass


x = 1
for i in range(10000000):
    a(x)
    b(x)
    c(x)

和cProfile事件探查器结果:

因此,我们可以看到有间〜0.7%的非常微小差别if not x == 'val':if x != 'val':。其中,if x != 'val':是最快的。

但是,最令人惊讶的是,我们可以看到

if x == 'val':
        pass
    else:

实际上是最快的,胜过if x != 'val':〜0.3%。这不是很容易理解,但是我想如果您想忽略不计的性能改进,可以采用这种方法。

@jonrsharpe has an excellent explanation of what’s going on. I thought I’d just show the difference in time when running each of the 3 options 10,000,000 times (enough for a slight difference to show).

Code used:

def a(x):
    if x != 'val':
        pass


def b(x):
    if not x == 'val':
        pass


def c(x):
    if x == 'val':
        pass
    else:
        pass


x = 1
for i in range(10000000):
    a(x)
    b(x)
    c(x)

And the cProfile profiler results:

So we can see that there is a very minute difference of ~0.7% between if not x == 'val': and if x != 'val':. Of these, if x != 'val': is the fastest.

However, most surprisingly, we can see that

if x == 'val':
        pass
    else:

is in fact the fastest, and beats if x != 'val': by ~0.3%. This isn’t very readable, but I guess if you wanted a negligible performance improvement, one could go down this route.


回答 2

在第一个Python中,Python必须执行比必要更多的操作(而不是仅检查不等于它,而是必须检查它是否相等,因此还要执行一个操作)。不可能说出一次执行的区别,但是如果执行多次,第二次执行将更有效率。总的来说,我会用第二个,但从数学上讲它们是相同的

In the first one Python has to execute one more operations than necessary(instead of just checking not equal to it has to check if it is not true that it is equal, thus one more operation). It would be impossible to tell the difference from one execution, but if run many times, the second would be more efficient. Overall I would use the second one, but mathematically they are the same


回答 3

>>> from dis import dis
>>> dis(compile('not 10 == 20', '', 'exec'))
  1           0 LOAD_CONST               0 (10)
              3 LOAD_CONST               1 (20)
              6 COMPARE_OP               2 (==)
              9 UNARY_NOT
             10 POP_TOP
             11 LOAD_CONST               2 (None)
             14 RETURN_VALUE
>>> dis(compile('10 != 20', '', 'exec'))
  1           0 LOAD_CONST               0 (10)
              3 LOAD_CONST               1 (20)
              6 COMPARE_OP               3 (!=)
              9 POP_TOP
             10 LOAD_CONST               2 (None)
             13 RETURN_VALUE

在这里您可以看到该not x == y指令比的指令多x != y。因此,除非您进行数百万次比较,否则在大多数情况下,性能差异将很小,即使这样,也可能不会成为瓶颈。

>>> from dis import dis
>>> dis(compile('not 10 == 20', '', 'exec'))
  1           0 LOAD_CONST               0 (10)
              3 LOAD_CONST               1 (20)
              6 COMPARE_OP               2 (==)
              9 UNARY_NOT
             10 POP_TOP
             11 LOAD_CONST               2 (None)
             14 RETURN_VALUE
>>> dis(compile('10 != 20', '', 'exec'))
  1           0 LOAD_CONST               0 (10)
              3 LOAD_CONST               1 (20)
              6 COMPARE_OP               3 (!=)
              9 POP_TOP
             10 LOAD_CONST               2 (None)
             13 RETURN_VALUE

Here you can see that not x == y has one more instruction than x != y. So the performance difference will be very small in most cases unless you are doing millions of comparisons and even then this will likely not be the cause of a bottleneck.


回答 4

另一个需要注意的是,由于其他答案大多可以正确回答您的问题,因此,如果仅定义一个类__eq__()而不是__ne__(),则您COMPARE_OP (!=)将运行__eq__()并取反它。到那时,您的第三个选择可能会更有效率,但只有在需要速度时才应考虑使用,因为这很难快速理解。

An additional note, since the other answers answered your question mostly correctly, is that if a class only defines __eq__() and not __ne__(), then your COMPARE_OP (!=) will run __eq__() and negate it. At that time, your third option is likely to be a tiny bit more efficient, but should only be considered if you NEED the speed, since it’s difficult to understand quickly.


回答 5

这是关于您的阅读方式。not操作符是动态的,这就是为什么您可以将其应用于

if not x == 'val':

但是!=作为一个相反的运算符,可以更好地理解==它。

It’s about your way of reading it. not operator is dynamic, that’s why you are able to apply it in

if not x == 'val':

But != could be read in a better context as an operator which does the opposite of what == does.


回答 6

我想在上面扩展我的可读性注释。

再次,我完全同意可读性优先于其他(性能无关紧要)的问题。

我想指出的是,大脑对“阳性”的解释比对“阴性”的解释要快。例如,“停止”与“不走”(由于单词数量的差异,这是一个很糟糕的例子)。

因此有一个选择:

if a == b
    (do this)
else
    (do that)

在功能上等效于:

if a != b
    (do that)
else
    (do this)

较低的可读性/可理解性导致更多的错误。也许不是在初始编码中,而是(不如您聪明!)维护发生了变化…

I want to expand on my readability comment above.

Again, I completely agree with readability overriding other (performance-insignificant) concerns.

What I would like to point out is the brain interprets “positive” faster than it does “negative”. E.g., “stop” vs. “do not go” (a rather lousy example due to the difference in number of words).

So given a choice:

if a == b
    (do this)
else
    (do that)

is preferable to the functionally-equivalent:

if a != b
    (do that)
else
    (do this)

Less readability/understandability leads to more bugs. Perhaps not in initial coding, but the (not as smart as you!) maintenance changes…


False == 0和True == 1是实现细节还是由语言保证?

问题:False == 0和True == 1是实现细节还是由语言保证?

是否可以保证False == 0True == 1,在Python中(假设用户没有重新分配它们)?例如,是否以任何方式保证以下代码将始终产生相同的结果,而不管Python的版本如何(既有现有版本,也可能是未来版本)?

0 == False  # True
1 == True   # True
['zero', 'one'][False]  # is 'zero'

任何对官方文档的引用将不胜感激!

编辑:如许多答案所述,bool继承自int。因此,可以将问题改写为:“文档是否正式声明程序员可以依赖从整数(带有值01?)继承的布尔 ”。这个问题与编写不会因为实现细节而失败的健壮代码有关!

Is it guaranteed that False == 0 and True == 1, in Python (assuming that they are not reassigned by the user)? For instance, is it in any way guaranteed that the following code will always produce the same results, whatever the version of Python (both existing and, likely, future ones)?

0 == False  # True
1 == True   # True
['zero', 'one'][False]  # is 'zero'

Any reference to the official documentation would be much appreciated!

Edit: As noted in many answers, bool inherits from int. The question can therefore be recast as: “Does the documentation officially say that programmers can rely on booleans inheriting from integers, with the values 0 and 1?”. This question is relevant for writing robust code that won’t fail because of implementation details!


回答 0

在Python 2.x的,这是没有保证的,因为它是可能的True,并False重新分配。但是,即使发生这种情况,仍然正确返回布尔True和布尔False进行比较。

在Python 3.x中,TrueFalse是关键字,并且始终等于10

通常情况下,在Python 2中,并且始终在Python 3中:

False对象的类型bool是的子类int

object
   |
 int
   |
 bool

这是在您的示例['zero', 'one'][False]中起作用的唯一原因。它不适用于不是整数子类的对象,因为列表索引仅适用于整数或定义__index__方法的对象(感谢mark-dickinson)。

编辑:

当前的python版本和Python 3都是如此。python 2.6文档和python 3文档都说:

整数有两种类型:[…]整数(int)[…]布尔值(bool)

在布尔小节中:

布尔值:它们表示真值False和True布尔值在几乎所有上下文中的行为分别类似于值0和1,但exceptions是当转换为字符串时,字符串“ False”或“ True”分别返回。

对于Python 2也有:

在数字上下文中(例如,用作算术运算符的参数时),它们的[False和True]分别类似于整数0和1。

因此,布尔值在Python 2.6和3中被明确视为整数。

因此,在Python 4出现之前,您是安全的。;-)

In Python 2.x this is not guaranteed as it is possible for True and False to be reassigned. However, even if this happens, boolean True and boolean False are still properly returned for comparisons.

In Python 3.x True and False are keywords and will always be equal to 1 and 0.

Under normal circumstances in Python 2, and always in Python 3:

False object is of type bool which is a subclass of int:

object
   |
 int
   |
 bool

It is the only reason why in your example, ['zero', 'one'][False] does work. It would not work with an object which is not a subclass of integer, because list indexing only works with integers, or objects that define a __index__ method (thanks mark-dickinson).

Edit:

It is true of the current python version, and of that of Python 3. The docs for python 2.6 and the docs for Python 3 both say:

There are two types of integers: […] Integers (int) […] Booleans (bool)

and in the boolean subsection:

Booleans: These represent the truth values False and True […] Boolean values behave like the values 0 and 1, respectively, in almost all contexts, the exception being that when converted to a string, the strings “False” or “True” are returned, respectively.

There is also, for Python 2:

In numeric contexts (for example when used as the argument to an arithmetic operator), they [False and True] behave like the integers 0 and 1, respectively.

So booleans are explicitly considered as integers in Python 2.6 and 3.

So you’re safe until Python 4 comes along. ;-)


回答 1

链接到PEP讨论Python 2.3中的新布尔类型:http : //www.python.org/dev/peps/pep-0285/

将布尔值转换为int时,整数值始终为0或1,但是将int值转换为bool时,除0以外的所有整数的布尔值均为True。

>>> int(False)
0
>>> int(True)
1
>>> bool(5)
True
>>> bool(-5)
True
>>> bool(0)
False

Link to the PEP discussing the new bool type in Python 2.3: http://www.python.org/dev/peps/pep-0285/.

When converting a bool to an int, the integer value is always 0 or 1, but when converting an int to a bool, the boolean value is True for all integers except 0.

>>> int(False)
0
>>> int(True)
1
>>> bool(5)
True
>>> bool(-5)
True
>>> bool(0)
False

回答 2

在Python 2.x中,根本无法保证:

>>> False = 5
>>> 0 == False
False

因此它可能会改变。在Python 3.x中,True,False和None是保留字,因此上面的代码不起作用。

通常,使用布尔值时,您应该假设False始终具有0的整数值(只要您不进行上述更改即可),True可以具有任何其他值。我不一定要依靠True==1,但是在Python 3.x上,无论如何,情况总是如此。

In Python 2.x, it is not guaranteed at all:

>>> False = 5
>>> 0 == False
False

So it could change. In Python 3.x, True, False, and None are reserved words, so the above code would not work.

In general, with booleans you should assume that while False will always have an integer value of 0 (so long as you don’t change it, as above), True could have any other value. I wouldn’t necessarily rely on any guarantee that True==1, but on Python 3.x, this will always be the case, no matter what.


回答 3

很简单。由于布尔与将整数评估为布尔有关,因此只有0给出错误的答案。所有非零值,浮点数,整数(包括负数)或您拥有的所有值都将返回true。

一个为什么有用的很好的例子是确定设备的电源状态。On是任何非零值,off是零。从电子上讲,这是有道理的。

若要相对确定值之间的正确或错误,您必须将其与之进行比较。这适用于字符串和数值,使用==!=<> >=<=等。

您可以为变量分配一个整数,然后根据该变量值获得true或false。

Very simple. As bool relates to evaluating an integer as a bool, ONLY zero gives a false answer. ALL Non-Zero values, floats, integers, including negative numbers, or what have you, will return true.

A nice example of why this is useful is determining the power status of a device. On is any non-zero value, off is zero. Electronically speaking this makes sense.

To determine true or false relatively between values, you must have something to compare it to. This applies to strings and number values, using == or != or <, > >=, <=, etc.

You can assign an integer to a variable and then get true or false based on that variable value.


回答 4

只需编写int(False),您将得到0,如果键入int(True)它将输出1

Just write int(False) and you will get 0, if you type int(True) it will output 1


回答 5

假是布尔。它具有不同的类型。它是不同于整数的0的对象。

0 == False因为将False强制转换为整数,所以返回True。int(False)返回0

==运算符的python文档说(help(’==’)):

运营商<>==>=<=,和!=比较两个对象的值。这些对象不必具有相同的类型。如果两者都是数字,则它们将转换为通用类型。

结果,需要比较时将False转换为整数。但它不同于0。

>>> 0 is False
False

False is a bool. It has a different type. It is a different object from 0 which is an integer.

0 == False returns True because False is cast to an integer. int(False) returns 0

The python documentation of the == operator says (help(‘==’)):

The operators <, >, ==, >=, <=, and != compare the values of two objects. The objects need not have the same type. If both are numbers, they are converted to a common type.

As a consequence False is converted to an integer for the need of the comparison. But it is different from 0.

>>> 0 is False
False

比较对象实例的属性是否相等

问题:比较对象实例的属性是否相等

我有一个类MyClass,其中包含两个成员变量foobar

class MyClass:
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

我有这样的类,其每个具有相同值的两个实例foobar

x = MyClass('foo', 'bar')
y = MyClass('foo', 'bar')

但是,当我比较它们的相等性时,Python返回False

>>> x == y
False

如何使python认为这两个对象相等?

I have a class MyClass, which contains two member variables foo and bar:

class MyClass:
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

I have two instances of this class, each of which has identical values for foo and bar:

x = MyClass('foo', 'bar')
y = MyClass('foo', 'bar')

However, when I compare them for equality, Python returns False:

>>> x == y
False

How can I make python consider these two objects equal?


回答 0

您应该实现以下方法__eq__

class MyClass:
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

    def __eq__(self, other): 
        if not isinstance(other, MyClass):
            # don't attempt to compare against unrelated types
            return NotImplemented

        return self.foo == other.foo and self.bar == other.bar

现在它输出:

>>> x == y
True

请注意,实现__eq__会自动使您的类的实例变得不可散列,这意味着它们无法存储在集合和字典中。如果您不对不可变的类型进行建模(即属性foo和属性bar可能在对象的生存期内更改),则建议仅使实例保持不可散列状态。

如果要对不可变类型进行建模,则还应该实现datamodel hook __hash__

class MyClass:
    ...

    def __hash__(self):
        # necessary for instances to behave sanely in dicts and sets.
        return hash((self.foo, self.bar))

__dict__不建议使用通用的解决方案,例如遍历和比较值的想法-它永远不可能真正通用,因为其中__dict__可能包含不可比较或不可哈希的类型。

注意:请注意,在Python 3之前,您可能需要使用__cmp__而不是__eq__。Python 2用户可能还想实现__ne__,因为不等式的明智的默认行为(即反转相等结果)不会在Python 2中自动创建。

You should implement the method __eq__:

class MyClass:
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

    def __eq__(self, other): 
        if not isinstance(other, MyClass):
            # don't attempt to compare against unrelated types
            return NotImplemented

        return self.foo == other.foo and self.bar == other.bar

Now it outputs:

>>> x == y
True

Note that implementing __eq__ will automatically make instances of your class unhashable, which means they can’t be stored in sets and dicts. If you’re not modelling an immutable type (i.e. if the attributes foo and bar may change value within the lifetime of your object), then it’s recommend to just leave your instances as unhashable.

If you are modelling an immutable type, you should also implement the datamodel hook __hash__:

class MyClass:
    ...

    def __hash__(self):
        # necessary for instances to behave sanely in dicts and sets.
        return hash((self.foo, self.bar))

A general solution, like the idea of looping through __dict__ and comparing values, is not advisable – it can never be truly general because the __dict__ may have uncomparable or unhashable types contained within.

N.B.: be aware that before Python 3, you may need to use __cmp__ instead of __eq__. Python 2 users may also want to implement __ne__, since a sensible default behaviour for inequality (i.e. inverting the equality result) will not be automatically created in Python 2.


回答 1

您将覆盖对象中的丰富比较运算符

class MyClass:
 def __lt__(self, other):
      # return comparison
 def __le__(self, other):
      # return comparison
 def __eq__(self, other):
      # return comparison
 def __ne__(self, other):
      # return comparison
 def __gt__(self, other):
      # return comparison
 def __ge__(self, other):
      # return comparison

像这样:

    def __eq__(self, other):
        return self._id == other._id

You override the rich comparison operators in your object.

class MyClass:
 def __lt__(self, other):
      # return comparison
 def __le__(self, other):
      # return comparison
 def __eq__(self, other):
      # return comparison
 def __ne__(self, other):
      # return comparison
 def __gt__(self, other):
      # return comparison
 def __ge__(self, other):
      # return comparison

Like this:

    def __eq__(self, other):
        return self._id == other._id

回答 2

__eq__在您的类中实现该方法;像这样的东西:

def __eq__(self, other):
    return self.path == other.path and self.title == other.title

编辑:如果您希望您的对象比较且仅当它们具有相等的实例字典时才比较:

def __eq__(self, other):
    return self.__dict__ == other.__dict__

Implement the __eq__ method in your class; something like this:

def __eq__(self, other):
    return self.path == other.path and self.title == other.title

Edit: if you want your objects to compare equal if and only if they have equal instance dictionaries:

def __eq__(self, other):
    return self.__dict__ == other.__dict__

回答 3

总结一下:

  1. 建议您执行__eq__而不是__cmp__,除非您运行python <= 2.0(__eq__已在2.1中添加)
  2. 别忘了也要实现__ne__(应该是类似的东西return not self.__eq__(other)return not self == other非常特殊的情况除外)
  3. 不要忘记,必须在要比较的每个自定义类中实现运算符(请参见下面的示例)。
  4. 如果要与可以为None的对象进行比较,则必须实现它。解释器无法猜测…(请参见下面的示例)

    class B(object):
      def __init__(self):
        self.name = "toto"
      def __eq__(self, other):
        if other is None:
          return False
        return self.name == other.name
    
    class A(object):
      def __init__(self):
        self.toto = "titi"
        self.b_inst = B()
      def __eq__(self, other):
        if other is None:
          return False
        return (self.toto, self.b_inst) == (other.toto, other.b_inst)

As a summary :

  1. It’s advised to implement __eq__ rather than __cmp__, except if you run python <= 2.0 (__eq__ has been added in 2.1)
  2. Don’t forget to also implement __ne__ (should be something like return not self.__eq__(other) or return not self == other except very special case)
  3. Don`t forget that the operator must be implemented in each custom class you want to compare (see example below).
  4. If you want to compare with object that can be None, you must implement it. The interpreter cannot guess it … (see example below)

    class B(object):
      def __init__(self):
        self.name = "toto"
      def __eq__(self, other):
        if other is None:
          return False
        return self.name == other.name
    
    class A(object):
      def __init__(self):
        self.toto = "titi"
        self.b_inst = B()
      def __eq__(self, other):
        if other is None:
          return False
        return (self.toto, self.b_inst) == (other.toto, other.b_inst)
    

回答 4

根据您的具体情况,您可以执行以下操作:

>>> vars(x) == vars(y)
True

从对象的字段中查看Python字典

Depending on your specific case, you could do:

>>> vars(x) == vars(y)
True

See Python dictionary from an object’s fields


回答 5

使用Python 3.7(及更高版本)中的数据类,比较对象实例是否相等是一项内置功能。

数据类的反向移植适用于Python 3.6。

(Py37) nsc@nsc-vbox:~$ python
Python 3.7.5 (default, Nov  7 2019, 10:50:52) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from dataclasses import dataclass
>>> @dataclass
... class MyClass():
...     foo: str
...     bar: str
... 
>>> x = MyClass(foo="foo", bar="bar")
>>> y = MyClass(foo="foo", bar="bar")
>>> x == y
True

With Dataclasses in Python 3.7 (and above), a comparison of object instances for equality is an inbuilt feature.

A backport for Dataclasses is available for Python 3.6.

(Py37) nsc@nsc-vbox:~$ python
Python 3.7.5 (default, Nov  7 2019, 10:50:52) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from dataclasses import dataclass
>>> @dataclass
... class MyClass():
...     foo: str
...     bar: str
... 
>>> x = MyClass(foo="foo", bar="bar")
>>> y = MyClass(foo="foo", bar="bar")
>>> x == y
True

回答 6

比较对象实例时,将__cmp__调用该函数。

如果默认情况下==运算符对您不起作用,则始终可以__cmp__为该对象重新定义函数。

编辑:

正如已经指出的那样,__cmp__自3.0起不推荐使用该功能。相反,您应该使用“丰富比较”方法。

When comparing instances of objects, the __cmp__ function is called.

If the == operator is not working for you by default, you can always redefine the __cmp__ function for the object.

Edit:

As has been pointed out, the __cmp__ function is deprecated since 3.0. Instead you should use the “rich comparison” methods.


回答 7

我编写了此代码并将其放在test/utils我项目的模块中。对于不是Class的情况,只需按计划进行,这将遍历两个对象并确保

  1. 每个属性都等于对应属性
  2. 不存在悬挂属性(仅在一个对象上存在的属性)

大…不性感 …但是噢,它起作用了!

def assertObjectsEqual(obj_a, obj_b):

    def _assert(a, b):
        if a == b:
            return
        raise AssertionError(f'{a} !== {b} inside assertObjectsEqual')

    def _check(a, b):
        if a is None or b is None:
            _assert(a, b)
        for k,v in a.items():
            if isinstance(v, dict):
                assertObjectsEqual(v, b[k])
            else:
                _assert(v, b[k])

    # Asserting both directions is more work
    # but it ensures no dangling values on
    # on either object
    _check(obj_a, obj_b)
    _check(obj_b, obj_a)

您可以删除它,_assert并仅使用普通ol’ 对其进行清理,assert但失败时收到的消息将无济于事。

I wrote this and placed it in a test/utils module in my project. For cases when its not a class, just plan ol’ dict, this will traverse both objects and ensure

  1. every attribute is equal to its counterpart
  2. No dangling attributes exist (attrs that only exist on one object)

Its big… its not sexy… but oh boi does it work!

def assertObjectsEqual(obj_a, obj_b):

    def _assert(a, b):
        if a == b:
            return
        raise AssertionError(f'{a} !== {b} inside assertObjectsEqual')

    def _check(a, b):
        if a is None or b is None:
            _assert(a, b)
        for k,v in a.items():
            if isinstance(v, dict):
                assertObjectsEqual(v, b[k])
            else:
                _assert(v, b[k])

    # Asserting both directions is more work
    # but it ensures no dangling values on
    # on either object
    _check(obj_a, obj_b)
    _check(obj_b, obj_a)

You can clean it up a little by removing the _assert and just using plain ol’ assert but then the message you get when it fails is very unhelpful.


回答 8

您应该实现以下方法__eq__

 class MyClass:
      def __init__(self, foo, bar, name):
           self.foo = foo
           self.bar = bar
           self.name = name

      def __eq__(self,other):
           if not isinstance(other,MyClass):
                return NotImplemented
           else:
                #string lists of all method names and properties of each of these objects
                prop_names1 = list(self.__dict__)
                prop_names2 = list(other.__dict__)

                n = len(prop_names1) #number of properties
                for i in range(n):
                     if getattr(self,prop_names1[i]) != getattr(other,prop_names2[i]):
                          return False

                return True

You should implement the method __eq__:

 class MyClass:
      def __init__(self, foo, bar, name):
           self.foo = foo
           self.bar = bar
           self.name = name

      def __eq__(self,other):
           if not isinstance(other,MyClass):
                return NotImplemented
           else:
                #string lists of all method names and properties of each of these objects
                prop_names1 = list(self.__dict__)
                prop_names2 = list(other.__dict__)

                n = len(prop_names1) #number of properties
                for i in range(n):
                     if getattr(self,prop_names1[i]) != getattr(other,prop_names2[i]):
                          return False

                return True

回答 9

下面的工作(在我有限的测试中)是通过对两个对象层次结构进行深入比较而实现的。在处理各种情况下,包括对象本身或其属性为字典的情况。

def deep_comp(o1:Any, o2:Any)->bool:
    # NOTE: dict don't have __dict__
    o1d = getattr(o1, '__dict__', None)
    o2d = getattr(o2, '__dict__', None)

    # if both are objects
    if o1d is not None and o2d is not None:
        # we will compare their dictionaries
        o1, o2 = o1.__dict__, o2.__dict__

    if o1 is not None and o2 is not None:
        # if both are dictionaries, we will compare each key
        if isinstance(o1, dict) and isinstance(o2, dict):
            for k in set().union(o1.keys() ,o2.keys()):
                if k in o1 and k in o2:
                    if not deep_comp(o1[k], o2[k]):
                        return False
                else:
                    return False # some key missing
            return True
    # mismatched object types or both are scalers, or one or both None
    return o1 == o2

这是一个非常棘手的代码,因此请在注释中添加任何可能不适合您的情况。

Below works (in my limited testing) by doing deep compare between two object hierarchies. In handles various cases including the cases when objects themselves or their attributes are dictionaries.

def deep_comp(o1:Any, o2:Any)->bool:
    # NOTE: dict don't have __dict__
    o1d = getattr(o1, '__dict__', None)
    o2d = getattr(o2, '__dict__', None)

    # if both are objects
    if o1d is not None and o2d is not None:
        # we will compare their dictionaries
        o1, o2 = o1.__dict__, o2.__dict__

    if o1 is not None and o2 is not None:
        # if both are dictionaries, we will compare each key
        if isinstance(o1, dict) and isinstance(o2, dict):
            for k in set().union(o1.keys() ,o2.keys()):
                if k in o1 and k in o2:
                    if not deep_comp(o1[k], o2[k]):
                        return False
                else:
                    return False # some key missing
            return True
    # mismatched object types or both are scalers, or one or both None
    return o1 == o2

This is a very tricky code so please add any cases that might not work for you in comments.


回答 10

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

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

    def __eq__(self,other):
        return self.value == other.value

node1 = Node(1)
node2 = Node(1)

print(f'node1 id:{id(node1)}')
print(f'node2 id:{id(node2)}')
print(node1 == node2)
>>> node1 id:4396696848
>>> node2 id:4396698000
>>> True
class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

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

    def __eq__(self,other):
        return self.value == other.value

node1 = Node(1)
node2 = Node(1)

print(f'node1 id:{id(node1)}')
print(f'node2 id:{id(node2)}')
print(node1 == node2)
>>> node1 id:4396696848
>>> node2 id:4396698000
>>> True

回答 11

如果你正在处理一个或多个类,你不能从内部更改的,则有一些通用且简单的方法可以执行此操作,而这些方法也不依赖于diff特定的库:

最简单,非常不安全的复杂对象方法

pickle.dumps(a) == pickle.dumps(b)

pickle是用于Python对象的非常常见的序列化库,因此实际上可以对几乎所有内容进行序列化。在上面的代码段中,我正在将strfrom序列化为afromb。与下一种方法不同,该方法还具有对自定义类进行类型检查的优点。

最大的麻烦:由于特定的排序和[de / en]编码方法,pickle对于相等的对象可能不会产生相同的结果,尤其是在处理更复杂的对象(例如,嵌套的自定义类实例的列表)时,您会经常发现在某些第三方库中。对于这些情况,我建议使用其他方法:

彻底安全的任何对象方法

您可以编写一个递归反射,该反射将为您提供可序列化的对象,然后比较结果

from collections.abc import Iterable

BASE_TYPES = [str, int, float, bool, type(None)]


def base_typed(obj):
    """Recursive reflection method to convert any object property into a comparable form.
    """
    T = type(obj)
    from_numpy = T.__module__ == 'numpy'

    if T in BASE_TYPES or callable(obj) or (from_numpy and not isinstance(T, Iterable)):
        return obj

    if isinstance(obj, Iterable):
        base_items = [base_typed(item) for item in obj]
        return base_items if from_numpy else T(base_items)

    d = obj if T is dict else obj.__dict__

    return {k: base_typed(v) for k, v in d.items()}


def deep_equals(*args):
    return all(base_typed(args[0]) == base_typed(other) for other in args[1:])

现在不管您的对象是什么,都可以保证深度平等

>>> from sklearn.ensemble import RandomForestClassifier
>>>
>>> a = RandomForestClassifier(max_depth=2, random_state=42)
>>> b = RandomForestClassifier(max_depth=2, random_state=42)
>>> 
>>> deep_equals(a, b)
True

可比对象的数量也无关紧要

>>> c = RandomForestClassifier(max_depth=2, random_state=1000)
>>> deep_equals(a, b, c)
False

为此,我的用例是检查BDD测试中各种经过训练的机器学习模型之间的深层相等性。这些模型属于一组不同的第三方库。当然,__eq__像其他答案一样在这里实施对我来说不是一个选择。

覆盖所有基地

您可能处于一个或多个被比较的自定义类没有__dict__实现的情况下。无论如何,这并不普遍,但是sklearn的Random Forest分类器中的一个子类型就是这种情况<type 'sklearn.tree._tree.Tree'>。在逐个案例的情况下处理这些情况-例如,特别是,我决定用为我提供有关实例的代表性信息的__getstate__方法(在这种情况下,该方法)的内容替换患病类型的内容。为此,倒数第二行base_typed成为

d = obj if T is dict else obj.__dict__ if '__dict__' in dir(obj) else obj.__getstate__()

编辑:为组织着想,我取代的最后两行base_typedreturn dict_from(obj),并实现了真正通用的反映它容纳更多的晦涩库(我看着你,Doc2Vec)

def isproperty(prop, obj):
    return not callable(getattr(obj, prop)) and not prop.startswith('_')


def dict_from(obj):
    """Converts dict-like objects into dicts
    """
    if isinstance(obj, dict):
        # Dict and subtypes are directly converted
        d = dict(obj)

    elif '__dict__' in dir(obj):
        d = obj.__dict__

    elif str(type(obj)) == 'sklearn.tree._tree.Tree':
        # Replaces sklearn trees with their state metadata
        d = obj.__getstate__()

    else:
        # Extract non-callable, non-private attributes with reflection
        kv = [(p, getattr(obj, p)) for p in dir(obj) if isproperty(p, obj)]
        d = {k: v for k, v in kv}

    return {k: base_typed(v) for k, v in d.items()}

请注意,上述方法都不True会对具有相同键值对但键/值顺序不同的不同对象产生,如

>>> a = {'foo':[], 'bar':{}}
>>> b = {'bar':{}, 'foo':[]}
>>> pickle.dumps(a) == pickle.dumps(b)
False

但是,如果您愿意,则可以sorted预先使用Python的内置方法。

If you’re dealing with one or more classes which you can’t change from the inside, there are generic and simple ways to do this that also don’t depend on a diff-specific library:

Easiest, unsafe-for-very-complex-objects method

pickle.dumps(a) == pickle.dumps(b)

pickle is a very common serialization lib for Python objects, and will thus be able to serialize pretty much anything, really. In the above snippet I’m comparing the str from serialized a with the one from b. Unlike the next method, this one has the advantage of also type checking custom classes.

The biggest hassle: due to specific ordering and [de/en]coding methods, pickle may not yield the same result for equal objects, specially when dealing with more complex ones (e.g. lists of nested custom-class instances) like you’ll frequently find in some third-party libs. For those cases, I’d recommend a different approach:

Thorough, safe-for-any-object method

You could write a recursive reflection that’ll give you serializable objects, and then compare results

from collections.abc import Iterable

BASE_TYPES = [str, int, float, bool, type(None)]


def base_typed(obj):
    """Recursive reflection method to convert any object property into a comparable form.
    """
    T = type(obj)
    from_numpy = T.__module__ == 'numpy'

    if T in BASE_TYPES or callable(obj) or (from_numpy and not isinstance(T, Iterable)):
        return obj

    if isinstance(obj, Iterable):
        base_items = [base_typed(item) for item in obj]
        return base_items if from_numpy else T(base_items)

    d = obj if T is dict else obj.__dict__

    return {k: base_typed(v) for k, v in d.items()}


def deep_equals(*args):
    return all(base_typed(args[0]) == base_typed(other) for other in args[1:])

Now it doesn’t matter what your objects are, deep equality is assured to work

>>> from sklearn.ensemble import RandomForestClassifier
>>>
>>> a = RandomForestClassifier(max_depth=2, random_state=42)
>>> b = RandomForestClassifier(max_depth=2, random_state=42)
>>> 
>>> deep_equals(a, b)
True

The number of comparables doesn’t matter as well

>>> c = RandomForestClassifier(max_depth=2, random_state=1000)
>>> deep_equals(a, b, c)
False

My use case for this was checking deep equality among a diverse set of already trained Machine Learning models inside BDD tests. The models belonged to a diverse set of third-party libs. Certainly implementing __eq__ like other answers here suggest wasn’t an option for me.

Covering all the bases

You may be in a scenario where one or more of the custom classes being compared do not have a __dict__ implementation. That’s not common by any means, but it is the case of a subtype within sklearn’s Random Forest classifier: <type 'sklearn.tree._tree.Tree'>. Treat these situations in a case by case basis – e.g. specifically, I decided to replace the content of the afflicted type with the content of a method that gives me representative information on the instance (in this case, the __getstate__ method). For such, the second-to-last row in base_typed became

d = obj if T is dict else obj.__dict__ if '__dict__' in dir(obj) else obj.__getstate__()

Edit: for the sake of organization, I replaced the hideous oneliner above with return dict_from(obj). Here, dict_from is a really generic reflection made to accommodate more obscure libs (I’m looking at you, Doc2Vec)

def isproperty(prop, obj):
    return not callable(getattr(obj, prop)) and not prop.startswith('_')


def dict_from(obj):
    """Converts dict-like objects into dicts
    """
    if isinstance(obj, dict):
        # Dict and subtypes are directly converted
        d = dict(obj)

    elif '__dict__' in dir(obj):
        # Use standard dict representation when available
        d = obj.__dict__

    elif str(type(obj)) == 'sklearn.tree._tree.Tree':
        # Replaces sklearn trees with their state metadata
        d = obj.__getstate__()

    else:
        # Extract non-callable, non-private attributes with reflection
        kv = [(p, getattr(obj, p)) for p in dir(obj) if isproperty(p, obj)]
        d = {k: v for k, v in kv}

    return {k: base_typed(v) for k, v in d.items()}

Do mind none of the above methods yield True for objects with the same key-value pairs in differing order, as in

>>> a = {'foo':[], 'bar':{}}
>>> b = {'bar':{}, 'foo':[]}
>>> pickle.dumps(a) == pickle.dumps(b)
False

But if you want that you could use Python’s built-in sorted method beforehand anyway.


回答 12

如果要获得逐个属性的比较,并查看它是否失败以及在何处失败,则可以使用以下列表理解:

[i for i,j in 
 zip([getattr(obj_1, attr) for attr in dir(obj_1)],
     [getattr(obj_2, attr) for attr in dir(obj_2)]) 
 if not i==j]

在此的额外好处是,您可以将其压缩一行,然后在PyCharm中进行调试时输入“评估表达式”窗口。

If you want to get an attribute-by-attribute comparison, and see if and where it fails, you can use the following list comprehension:

[i for i,j in 
 zip([getattr(obj_1, attr) for attr in dir(obj_1)],
     [getattr(obj_2, attr) for attr in dir(obj_2)]) 
 if not i==j]

The extra advantage here is that you can squeeze it one line and enter in the “Evaluate Expression” window when debugging in PyCharm.


回答 13

我尝试了初始示例(请参见上面的7),但在ipython中不起作用。请注意,使用两个相同的对象实例实现时,cmp(obj1,obj2)返回“ 1”。奇怪的是,当我修改一个属性值并重新比较时,使用cmp(obj1,obj2),该对象将继续返回“ 1”。(叹…)

好的,所以您需要做的是迭代两个对象,并使用==符号比较每个属性。

I tried the initial example (see 7 above) and it did not work in ipython. Note that cmp(obj1,obj2) returns a “1” when implemented using two identical object instances. Oddly enough when I modify one of the attribute values and recompare, using cmp(obj1,obj2) the object continues to return a “1”. (sigh…)

Ok, so what you need to do is iterate two objects and compare each attribute using the == sign.


回答 14

与==比较时,类的实例不相等。最好的方法是屁股CMP函数添加到您的类中,这将完成任务。

如果要按内容进行比较,可以简单地使用cmp(obj1,obj2)

在您的情况下,如果cmp(doc1,doc2)的内容相同,则它将返回-1。

Instance of a class when compared with == comes to non-equal. The best way is to ass the cmp function to your class which will do the stuff.

If you want to do comparison by the content you can simply use cmp(obj1,obj2)

In your case cmp(doc1,doc2) It will return -1 if the content wise they are same.


“ ==”和“是”之间有区别吗?

问题:“ ==”和“是”之间有区别吗?

我的Google Fu使我失败了。

在Python中,以下两个相等测试是否等效?

n = 5
# Test one.
if n == 5:
    print 'Yay!'

# Test two.
if n is 5:
    print 'Yay!'

这是否适用于您要比较实例(list说)的对象?

好的,这样可以回答我的问题:

L = []
L.append(1)
if L == [1]:
    print 'Yay!'
# Holds true, but...

if L is [1]:
    print 'Yay!'
# Doesn't.

因此,==测试会重视在哪里is进行测试以查看它们是否是同一对象?

My Google-fu has failed me.

In Python, are the following two tests for equality equivalent?

n = 5
# Test one.
if n == 5:
    print 'Yay!'

# Test two.
if n is 5:
    print 'Yay!'

Does this hold true for objects where you would be comparing instances (a list say)?

Okay, so this kind of answers my question:

L = []
L.append(1)
if L == [1]:
    print 'Yay!'
# Holds true, but...

if L is [1]:
    print 'Yay!'
# Doesn't.

So == tests value where is tests to see if they are the same object?


回答 0

isTrue如果两个变量指向同一个对象(==如果变量引用的对象相等),则将返回。

>>> a = [1, 2, 3]
>>> b = a
>>> b is a 
True
>>> b == a
True

# Make a new copy of list `a` via the slice operator, 
# and assign it to variable `b`
>>> b = a[:] 
>>> b is a
False
>>> b == a
True

在您的情况下,第二项测试仅能工作,因为Python会缓存小的整数对象,这是实现细节。对于较大的整数,这不起作用:

>>> 1000 is 10**3
False
>>> 1000 == 10**3
True

字符串文字也是如此:

>>> "a" is "a"
True
>>> "aa" is "a" * 2
True
>>> x = "a"
>>> "aa" is x * 2
False
>>> "aa" is intern(x*2)
True

也请参阅此问题

is will return True if two variables point to the same object, == if the objects referred to by the variables are equal.

>>> a = [1, 2, 3]
>>> b = a
>>> b is a 
True
>>> b == a
True

# Make a new copy of list `a` via the slice operator, 
# and assign it to variable `b`
>>> b = a[:] 
>>> b is a
False
>>> b == a
True

In your case, the second test only works because Python caches small integer objects, which is an implementation detail. For larger integers, this does not work:

>>> 1000 is 10**3
False
>>> 1000 == 10**3
True

The same holds true for string literals:

>>> "a" is "a"
True
>>> "aa" is "a" * 2
True
>>> x = "a"
>>> "aa" is x * 2
False
>>> "aa" is intern(x*2)
True

Please see this question as well.


回答 1

有一条简单的经验法则可以告诉您何时使用==is

  • ==是为了价值平等。当您想知道两个对象是否具有相同的值时,请使用它。
  • is参考平等。当您想知道两个引用是否引用同一对象时,请使用它。

通常,在将某事物与简单类型进行比较时,通常会检查值是否相等,因此应使用==。例如,您的示例的目的可能是检查x是否具有等于2(==)的值,而不是检查x字面上是否指向与2相同的对象。


其他注意事项:由于CPython参考实现的工作方式,如果错误地用于is比较整数的参考相等性,则会得到意外且不一致的结果:

>>> a = 500
>>> b = 500
>>> a == b
True
>>> a is b
False

这几乎是我们所期望的:a并且b具有相同的值,但是是不同的实体。但是呢?

>>> c = 200
>>> d = 200
>>> c == d
True
>>> c is d
True

这与先前的结果不一致。这里发生了什么?事实证明,出于性能原因,Python的参考实现将-5..256范围内的整数对象作为单例实例进行缓存。这是一个演示此示例:

>>> for i in range(250, 260): a = i; print "%i: %s" % (i, a is int(str(i)));
... 
250: True
251: True
252: True
253: True
254: True
255: True
256: True
257: False
258: False
259: False

这是另一个不使用的明显原因is:当您错误地将其用于值相等时,该行为应由实现决定。

There is a simple rule of thumb to tell you when to use == or is.

  • == is for value equality. Use it when you would like to know if two objects have the same value.
  • is is for reference equality. Use it when you would like to know if two references refer to the same object.

In general, when you are comparing something to a simple type, you are usually checking for value equality, so you should use ==. For example, the intention of your example is probably to check whether x has a value equal to 2 (==), not whether x is literally referring to the same object as 2.


Something else to note: because of the way the CPython reference implementation works, you’ll get unexpected and inconsistent results if you mistakenly use is to compare for reference equality on integers:

>>> a = 500
>>> b = 500
>>> a == b
True
>>> a is b
False

That’s pretty much what we expected: a and b have the same value, but are distinct entities. But what about this?

>>> c = 200
>>> d = 200
>>> c == d
True
>>> c is d
True

This is inconsistent with the earlier result. What’s going on here? It turns out the reference implementation of Python caches integer objects in the range -5..256 as singleton instances for performance reasons. Here’s an example demonstrating this:

>>> for i in range(250, 260): a = i; print "%i: %s" % (i, a is int(str(i)));
... 
250: True
251: True
252: True
253: True
254: True
255: True
256: True
257: False
258: False
259: False

This is another obvious reason not to use is: the behavior is left up to implementations when you’re erroneously using it for value equality.


回答 2

==确定值是否相等,而is确定它们是否是完全相同的对象。

== determines if the values are equal, while is determines if they are the exact same object.


回答 3

==isPython 之间有区别吗?

是的,它们有非常重要的区别。

==:检查是否相等-语义是等效对象(不一定是同一对象)将被测试为相等。如文档所述

运算符<,>,==,> =,<=和!=比较两个对象的值。

is:检查身份-语义是对象(保存在内存中)对象。再次,文档说

运算符isis not对象身份测试:x is y当且仅当xy是相同对象时,才为true 。使用该id()功能确定对象身份。x is not y产生反真值。

因此,对身份的检查与对对象ID的相等性检查相同。那是,

a is b

是相同的:

id(a) == id(b)

where id是返回整数的内建函数,该整数“保证同时存在的对象之间是唯一的”(请参阅​​参考资料help(id)),而where ab则是任意对象。

其他使用说明

您应该将这些比较用于它们的语义。使用is检查身份和==检查平等。

因此,通常,我们使用is来检查身份。当我们检查一个仅在内存中存在一次的对象(在文档中称为“单个”)时,这通常很有用。

用例is包括:

  • None
  • 枚举值(当使用枚举模块中的枚举时)
  • 通常是模块
  • 通常是由类定义产生的类对象
  • 通常由函数定义产生的函数对象
  • 在内存中应该只存在一次的所有其他内容(通常是所有单例)
  • 您希望通过身份获得的特定对象

通常的用例==包括:

  • 数字,包括整数
  • 清单
  • 词典
  • 自定义可变对象
  • 在大多数情况下,其他内置的不可变对象

一般使用情况下,再次对==,就是你想可能不是对象相同的对象,相反,它可能是一个相当于一个

PEP 8方向

PEP 8,标准库的官方Python样式指南还提到了以下两个用例is

与单例之类的比较None应始终使用isis not,而不应使用相等运算符。

另外,当心if x您的意思if x is not None,例如当测试是否将默认None 设置为的变量或参数设置为其他值时,请当心编写。另一个值可能具有在布尔上下文中可能为false的类型(例如容器)!

从身份推断平等

如果is为true,通常可以推断出相等性-从逻辑上讲,如果对象是自身,则它应该测试为等同于自身。

在大多数情况下,此逻辑是正确的,但它依赖于__eq__特殊方法的实现。正如文档所说,

相等比较(==!=)的默认行为基于对象的标识。因此,具有相同身份的实例的相等比较会导致相等,而具有不同身份的实例的相等比较会导致不平等。这种默认行为的动机是希望所有对象都是自反的(即x为y意味着x == y)。

为了保持一致性,建议:

平等比较应该是自反的。换句话说,相同的对象应该比较相等:

x is y 暗示 x == y

我们可以看到这是自定义对象的默认行为:

>>> class Object(object): pass
>>> obj = Object()
>>> obj2 = Object()
>>> obj == obj, obj is obj
(True, True)
>>> obj == obj2, obj is obj2
(False, False)

相反,通常也是如此-如果某项测试的结果不相等,则通常可以推断出它们不是同一对象。

由于可以对相等性测试进行自定义,因此该推论并不总是适用于所有类型。

一个exceptions

一个显着的exceptions是nan-它总是被测试为不等于自身:

>>> nan = float('nan')
>>> nan
nan
>>> nan is nan
True
>>> nan == nan           # !!!!!
False

检查身份比检查相等性要快得多(可能需要递归检查成员)。

但是它不能替代相等性,在相等性中您可能会发现多个对象相等。

请注意,比较列表和元组的相等性将假定对象的身份相同(因为这是一个快速检查)。如果逻辑不一致,这可能会产生矛盾-就是这样nan

>>> [nan] == [nan]
True
>>> (nan,) == (nan,)
True

警示故事:

问题是试图is用来比较整数。您不应该假定整数的实例与另一个引用获得的实例相同。这个故事解释了为什么。

一个注释者的代码依赖于以下事实:小整数(包括-5至256)在Python中是单例,而不是检查是否相等。

哇,这可能会导致一些隐患。我有一些检查a是否为b的代码,它可以按我的意愿工作,因为a和b通常很小。该错误仅在生产六个月后才出现在今天,因为a和b最终足够大而无法缓存。– gwg

它在开发中起作用。它可能已经通过了一些单元测试。

它可以在生产中使用-直到代码检查出大于256的整数为止,此时它在生产中失败了。

这是生产失败,可能已在代码审查中或可能通过样式检查器捕获。

让我强调一下:不要is用于比较整数。

Is there a difference between == and is in Python?

Yes, they have a very important difference.

==: check for equality – the semantics are that equivalent objects (that aren’t necessarily the same object) will test as equal. As the documentation says:

The operators <, >, ==, >=, <=, and != compare the values of two objects.

is: check for identity – the semantics are that the object (as held in memory) is the object. Again, the documentation says:

The operators is and is not test for object identity: x is y is true if and only if x and y are the same object. Object identity is determined using the id() function. x is not y yields the inverse truth value.

Thus, the check for identity is the same as checking for the equality of the IDs of the objects. That is,

a is b

is the same as:

id(a) == id(b)

where id is the builtin function that returns an integer that “is guaranteed to be unique among simultaneously existing objects” (see help(id)) and where a and b are any arbitrary objects.

Other Usage Directions

You should use these comparisons for their semantics. Use is to check identity and == to check equality.

So in general, we use is to check for identity. This is usually useful when we are checking for an object that should only exist once in memory, referred to as a “singleton” in the documentation.

Use cases for is include:

  • None
  • enum values (when using Enums from the enum module)
  • usually modules
  • usually class objects resulting from class definitions
  • usually function objects resulting from function definitions
  • anything else that should only exist once in memory (all singletons, generally)
  • a specific object that you want by identity

Usual use cases for == include:

  • numbers, including integers
  • strings
  • lists
  • sets
  • dictionaries
  • custom mutable objects
  • other builtin immutable objects, in most cases

The general use case, again, for ==, is the object you want may not be the same object, instead it may be an equivalent one

PEP 8 directions

PEP 8, the official Python style guide for the standard library also mentions two use-cases for is:

Comparisons to singletons like None should always be done with is or is not, never the equality operators.

Also, beware of writing if x when you really mean if x is not None — e.g. when testing whether a variable or argument that defaults to None was set to some other value. The other value might have a type (such as a container) that could be false in a boolean context!

Inferring equality from identity

If is is true, equality can usually be inferred – logically, if an object is itself, then it should test as equivalent to itself.

In most cases this logic is true, but it relies on the implementation of the __eq__ special method. As the docs say,

The default behavior for equality comparison (== and !=) is based on the identity of the objects. Hence, equality comparison of instances with the same identity results in equality, and equality comparison of instances with different identities results in inequality. A motivation for this default behavior is the desire that all objects should be reflexive (i.e. x is y implies x == y).

and in the interests of consistency, recommends:

Equality comparison should be reflexive. In other words, identical objects should compare equal:

x is y implies x == y

We can see that this is the default behavior for custom objects:

>>> class Object(object): pass
>>> obj = Object()
>>> obj2 = Object()
>>> obj == obj, obj is obj
(True, True)
>>> obj == obj2, obj is obj2
(False, False)

The contrapositive is also usually true – if somethings test as not equal, you can usually infer that they are not the same object.

Since tests for equality can be customized, this inference does not always hold true for all types.

An exception

A notable exception is nan – it always tests as not equal to itself:

>>> nan = float('nan')
>>> nan
nan
>>> nan is nan
True
>>> nan == nan           # !!!!!
False

Checking for identity can be much a much quicker check than checking for equality (which might require recursively checking members).

But it cannot be substituted for equality where you may find more than one object as equivalent.

Note that comparing equality of lists and tuples will assume that identity of objects are equal (because this is a fast check). This can create contradictions if the logic is inconsistent – as it is for nan:

>>> [nan] == [nan]
True
>>> (nan,) == (nan,)
True

A Cautionary Tale:

The question is attempting to use is to compare integers. You shouldn’t assume that an instance of an integer is the same instance as one obtained by another reference. This story explains why.

A commenter had code that relied on the fact that small integers (-5 to 256 inclusive) are singletons in Python, instead of checking for equality.

Wow, this can lead to some insidious bugs. I had some code that checked if a is b, which worked as I wanted because a and b are typically small numbers. The bug only happened today, after six months in production, because a and b were finally large enough to not be cached. – gwg

It worked in development. It may have passed some unittests.

And it worked in production – until the code checked for an integer larger than 256, at which point it failed in production.

This is a production failure that could have been caught in code review or possibly with a style-checker.

Let me emphasize: do not use is to compare integers.


回答 4

is和之间有什么区别==

==is不同的比较!正如其他人已经说过的:

  • == 比较对象的值。
  • is 比较对象的引用。

在Python中,例如,在这种情况下value1,名称指的是对象,并value2指代int存储值的实例1000

value1 = 1000
value2 = value1

因为value2引用相同的对象is==将给出True

>>> value1 == value2
True
>>> value1 is value2
True

在以下示例中,名称value1value2引用不同的int实例,即使它们都存储相同的整数:

>>> value1 = 1000
>>> value2 = 1000

因为相同的值(整数)存储==将是True,这就是为什么它通常被称为“值比较”。但是is会返回,False因为这些是不同的对象:

>>> value1 == value2
True
>>> value1 is value2
False

什么时候使用?

通常,is比较起来要快得多。这就是为什么CPython缓存(或者最好是重用)某些对象,例如小整数,某些字符串等。但是,这应该被视为实现细节,即使在没有警告的情况下也可以随时更改(即使可能性很小)。

is应在以下情况下使用

  • 想要检查两个对象是否真的是同一对象(不仅仅是相同的“值”)。一个示例可以是如果使用单例对象作为常量。
  • 想比较一个值和一个Python 常量。Python中的常量为:

    • None
    • True1个
    • False1个
    • NotImplemented
    • Ellipsis
    • __debug__
    • 类(例如int is intint is float
    • 内置模块或第三方模块中可能存在其他常量。例如np.ma.masked来自NumPy模块)

其他所有情况下,您都应使用==检查是否相等。

我可以自定义行为吗?

==在其他答案中还没有提到某些方面:它是Python“ Data model”的一部分。这意味着可以使用该__eq__方法自定义其行为。例如:

class MyClass(object):
    def __init__(self, val):
        self._value = val

    def __eq__(self, other):
        print('__eq__ method called')
        try:
            return self._value == other._value
        except AttributeError:
            raise TypeError('Cannot compare {0} to objects of type {1}'
                            .format(type(self), type(other)))

这只是一个人工的例子,用来说明该方法的确是这样的:

>>> MyClass(10) == MyClass(10)
__eq__ method called
True

请注意,默认情况下(如果__eq__在类或超类中找不到的其他实现)则__eq__使用is

class AClass(object):
    def __init__(self, value):
        self._value = value

>>> a = AClass(10)
>>> b = AClass(10)
>>> a == b
False
>>> a == a

因此,实现__eq__您想要的不仅仅是定制类的引用比较,实际上很重要!

另一方面,您无法自定义is检查。它总是会比较公正,如果你有相同的参考。

这些比较是否总是返回布尔值?

由于__eq__可以重新实现或覆盖,因此不限于return TrueFalse。它可以返回任何内容(但是在大多数情况下,它应该返回一个布尔值!)。

例如,对于NumPy数组,==它将返回一个数组:

>>> import numpy as np
>>> np.arange(10) == 2
array([False, False,  True, False, False, False, False, False, False, False], dtype=bool)

但是is支票总是会返回TrueFalse


1正如亚伦·霍尔在评论中提到的那样:

通常,您不应该执行任何操作is Trueis False检查,因为一个人通常在将条件隐式转换为布尔值的上下文中使用这些“检查” (例如,在if语句中)。因此,进行is True比较隐式的布尔类型转换要比仅仅进行布尔类型转换做更多的工作-并且您将自己限制为布尔值(不认为它是pythonic)。

就像PEP8提到的那样:

不要将布尔值与TrueFalse使用进行比较==

Yes:   if greeting:
No:    if greeting == True:
Worse: if greeting is True:

What’s the difference between is and ==?

== and is are different comparison! As others already said:

  • == compares the values of the objects.
  • is compares the references of the objects.

In Python names refer to objects, for example in this case value1 and value2 refer to an int instance storing the value 1000:

value1 = 1000
value2 = value1

Because value2 refers to the same object is and == will give True:

>>> value1 == value2
True
>>> value1 is value2
True

In the following example the names value1 and value2 refer to different int instances, even if both store the same integer:

>>> value1 = 1000
>>> value2 = 1000

Because the same value (integer) is stored == will be True, that’s why it’s often called “value comparison”. However is will return False because these are different objects:

>>> value1 == value2
True
>>> value1 is value2
False

When to use which?

Generally is is a much faster comparison. That’s why CPython caches (or maybe reuses would be the better term) certain objects like small integers, some strings, etc. But this should be treated as implementation detail that could (even if unlikely) change at any point without warning.

You should only use is if you:

  • want to check if two objects are really the same object (not just the same “value”). One example can be if you use a singleton object as constant.
  • want to compare a value to a Python constant. The constants in Python are:

    • None
    • True1
    • False1
    • NotImplemented
    • Ellipsis
    • __debug__
    • classes (for example int is int or int is float)
    • there could be additional constants in built-in modules or 3rd party modules. For example np.ma.masked from the NumPy module)

In every other case you should use == to check for equality.

Can I customize the behavior?

There is some aspect to == that hasn’t been mentioned already in the other answers: It’s part of Pythons “Data model”. That means its behavior can be customized using the __eq__ method. For example:

class MyClass(object):
    def __init__(self, val):
        self._value = val

    def __eq__(self, other):
        print('__eq__ method called')
        try:
            return self._value == other._value
        except AttributeError:
            raise TypeError('Cannot compare {0} to objects of type {1}'
                            .format(type(self), type(other)))

This is just an artificial example to illustrate that the method is really called:

>>> MyClass(10) == MyClass(10)
__eq__ method called
True

Note that by default (if no other implementation of __eq__ can be found in the class or the superclasses) __eq__ uses is:

class AClass(object):
    def __init__(self, value):
        self._value = value

>>> a = AClass(10)
>>> b = AClass(10)
>>> a == b
False
>>> a == a

So it’s actually important to implement __eq__ if you want “more” than just reference-comparison for custom classes!

On the other hand you cannot customize is checks. It will always compare just if you have the same reference.

Will these comparisons always return a boolean?

Because __eq__ can be re-implemented or overridden, it’s not limited to return True or False. It could return anything (but in most cases it should return a boolean!).

For example with NumPy arrays the == will return an array:

>>> import numpy as np
>>> np.arange(10) == 2
array([False, False,  True, False, False, False, False, False, False, False], dtype=bool)

But is checks will always return True or False!


1 As Aaron Hall mentioned in the comments:

Generally you shouldn’t do any is True or is False checks because one normally uses these “checks” in a context that implicitly converts the condition to a boolean (for example in an if statement). So doing the is True comparison and the implicit boolean cast is doing more work than just doing the boolean cast – and you limit yourself to booleans (which isn’t considered pythonic).

Like PEP8 mentions:

Don’t compare boolean values to True or False using ==.

Yes:   if greeting:
No:    if greeting == True:
Worse: if greeting is True:

回答 5

他们是完全不同的is检查对象身份,同时==检查是否相等(一个概念取决于两个操作数的类型)。

幸运的巧合是“ is”似乎可以正确地使用小整数(例如5 == 4 + 1)。那是因为CPython通过使整数成为单例来优化整数存储范围(-5到256)。此行为完全取决于实现,并且不能保证在所有较小的转换操作方式下都可以保留该行为。

例如,Python 3.5还使短字符串单身,但将它们切片会破坏此行为:

>>> "foo" + "bar" == "foobar"
True
>>> "foo" + "bar" is "foobar"
True
>>> "foo"[:] + "bar" == "foobar"
True
>>> "foo"[:] + "bar" is "foobar"
False

They are completely different. is checks for object identity, while == checks for equality (a notion that depends on the two operands’ types).

It is only a lucky coincidence that “is” seems to work correctly with small integers (e.g. 5 == 4+1). That is because CPython optimizes the storage of integers in the range (-5 to 256) by making them singletons. This behavior is totally implementation-dependent and not guaranteed to be preserved under all manner of minor transformative operations.

For example, Python 3.5 also makes short strings singletons, but slicing them disrupts this behavior:

>>> "foo" + "bar" == "foobar"
True
>>> "foo" + "bar" is "foobar"
True
>>> "foo"[:] + "bar" == "foobar"
True
>>> "foo"[:] + "bar" is "foobar"
False

回答 6

https://docs.python.org/library/stdtypes.html#comparisons

is身份 ==测试,是否相等

每个(小)整数值都映射到单个值,因此,每个3都是相同且相等的。这是实现细节,但不是语言规范的一部分

https://docs.python.org/library/stdtypes.html#comparisons

is tests for identity == tests for equality

Each (small) integer value is mapped to a single value, so every 3 is identical and equal. This is an implementation detail, not part of the language spec though


回答 7

您的回答是正确的。该is运算符比较两个对象的身份。该==操作比较两个对象的值。

一旦创建了对象,其身份就不会改变。您可能会认为它是对象在内存中的地址。

您可以通过定义__cmp__方法或丰富的比较方法(例如)来控制对象值的比较行为__eq__

Your answer is correct. The is operator compares the identity of two objects. The == operator compares the values of two objects.

An object’s identity never changes once it has been created; you may think of it as the object’s address in memory.

You can control comparison behaviour of object values by defining a __cmp__ method or a rich comparison method like __eq__.


回答 8

看一下Stack Overflow问题,Python的“ is”运算符在使用整数时表现异常

最主要的原因是“ is”检查它们是否是同一对象,而不只是彼此相等(小于256的数字是特例)。

Have a look at Stack Overflow question Python’s “is” operator behaves unexpectedly with integers.

What it mostly boils down to is that “is” checks to see if they are the same object, not just equal to each other (the numbers below 256 are a special case).


回答 9

简而言之,is检查两个引用是否指向同一对象。==检查两个对象是否具有相同的值。

a=[1,2,3]
b=a        #a and b point to the same object
c=list(a)  #c points to different object 

if a==b:
    print('#')   #output:#
if a is b:
    print('##')  #output:## 
if a==c:
    print('###') #output:## 
if a is c:
    print('####') #no output as c and a point to different object 

In a nutshell, is checks whether two references point to the same object or not.== checks whether two objects have the same value or not.

a=[1,2,3]
b=a        #a and b point to the same object
c=list(a)  #c points to different object 

if a==b:
    print('#')   #output:#
if a is b:
    print('##')  #output:## 
if a==c:
    print('###') #output:## 
if a is c:
    print('####') #no output as c and a point to different object 

回答 10

正如John Feminella所说,大多数时候,您将使用==和!=,因为您的目标是比较值。我只想对剩下的时间做些什么:

NoneType只有一个实例,即None是一个单例。因此foo == Nonefoo is None意思相同。但是,is测试速度更快,并且要使用Pythonic约定foo is None

如果您要对垃圾收集进行自省或处理,或者检查自定义构建的字符串实习小工具是否正常工作,则可能有一个用例foo是is bar

True和False也是(现在)单例,但是没有用例,foo == True也没有用例foo is True

As John Feminella said, most of the time you will use == and != because your objective is to compare values. I’d just like to categorise what you would do the rest of the time:

There is one and only one instance of NoneType i.e. None is a singleton. Consequently foo == None and foo is None mean the same. However the is test is faster and the Pythonic convention is to use foo is None.

If you are doing some introspection or mucking about with garbage collection or checking whether your custom-built string interning gadget is working or suchlike, then you probably have a use-case for foo is bar.

True and False are also (now) singletons, but there is no use-case for foo == True and no use case for foo is True.


回答 11

他们中的大多数人已经回答了这一点。正如补充说明(基于我的理解和实验,但不是来自书面记录)的声明

==如果变量引用的对象相等

从上面的答案应该理解为

==如果变量引用的对象相等并且属于相同类型/类的对象

。我根据以下测试得出了这个结论:

list1 = [1,2,3,4]
tuple1 = (1,2,3,4)

print(list1)
print(tuple1)
print(id(list1))
print(id(tuple1))

print(list1 == tuple1)
print(list1 is tuple1)

这里的列表和元组的内容相同,但类型/类不同。

Most of them already answered to the point. Just as an additional note (based on my understanding and experimenting but not from a documented source), the statement

== if the objects referred to by the variables are equal

from above answers should be read as

== if the objects referred to by the variables are equal and objects belonging to the same type/class

. I arrived at this conclusion based on the below test:

list1 = [1,2,3,4]
tuple1 = (1,2,3,4)

print(list1)
print(tuple1)
print(id(list1))
print(id(tuple1))

print(list1 == tuple1)
print(list1 is tuple1)

Here the contents of the list and tuple are same but the type/class are different.


回答 12

is和equals(==)之间的Python区别

is运算符可能看起来与相等运算符相同,但它们并不相同。

is检查两个变量是否指向同一对象,而==符号检查两个变量的值是否相同。

因此,如果is运算符返回True,则相等性肯定为True,但相反的情况可能为True,也可能不是True。

这是一个演示相似性和差异性的示例。

>>> a = b = [1,2,3]
>>> c = [1,2,3]
>>> a == b
True
>>> a == c
True
>>> a is b
True
>>> a is c
False
>>> a = [1,2,3]
>>> b = [1,2]
>>> a == b
False
>>> a is b
False
>>> del a[2]
>>> a == b
True
>>> a is b
False
Tip: Avoid using is operator for immutable types such as strings and numbers, the result is unpredictable.

Python difference between is and equals(==)

The is operator may seem like the same as the equality operator but they are not same.

The is checks if both the variables point to the same object whereas the == sign checks if the values for the two variables are the same.

So if the is operator returns True then the equality is definitely True, but the opposite may or may not be True.

Here is an example to demonstrate the similarity and the difference.

>>> a = b = [1,2,3]
>>> c = [1,2,3]
>>> a == b
True
>>> a == c
True
>>> a is b
True
>>> a is c
False
>>> a = [1,2,3]
>>> b = [1,2]
>>> a == b
False
>>> a is b
False
>>> del a[2]
>>> a == b
True
>>> a is b
False
Tip: Avoid using is operator for immutable types such as strings and numbers, the result is unpredictable.

回答 13

当这篇文章中的其他人详细回答了这个问题时,我将主要强调字符串之间的比较is以及可以给出不同结果的== 字符串,我敦促程序员谨慎使用它们。

为了进行字符串比较,请确保使用==代替is

str = 'hello'
if (str is 'hello'):
    print ('str is hello')
if (str == 'hello'):
    print ('str == hello')

出:

str is hello
str == hello

在下面的例子中==,并is会得到不同的结果:

str = 'hello sam'
    if (str is 'hello sam'):
        print ('str is hello sam')
    if (str == 'hello sam'):
        print ('str == hello sam')

出:

str == hello sam

结论:

is谨慎使用以比较字符串

As the other people in this post answer the question in details, I would emphasize mainly the comparison between is and == for strings which can give different results and I would urge programmers to carefully use them.

For string comparison, make sure to use == instead of is:

str = 'hello'
if (str is 'hello'):
    print ('str is hello')
if (str == 'hello'):
    print ('str == hello')

Out:

str is hello
str == hello

But in the below example == and is will get different results:

str = 'hello sam'
    if (str is 'hello sam'):
        print ('str is hello sam')
    if (str == 'hello sam'):
        print ('str == hello sam')

Out:

str == hello sam

Conclusion:

Use is carefully to compare between strings


在Python类中支持等价(“平等”)的优雅方法

问题:在Python类中支持等价(“平等”)的优雅方法

编写自定义类时,通过==!=运算符允许等效性通常很重要。在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 比较方法有什么特别的缺点吗?

注意:需要澄清一点-当__eq____ne__未定义时,您会发现以下行为:

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

也就是说,a == b评估为False因为它确实运行了a is b,所以对身份进行了测试(即“ ab?是同一对象”)。

__eq____ne__定义,你会发现这种行为(这是一个我们后):

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

When writing custom classes it is often important to allow equivalence by means of the == and != operators. In Python, this is made possible by implementing the __eq__ and __ne__ special methods, respectively. The easiest way I’ve found to do this is the following method:

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)

Do you know of more elegant means of doing this? Do you know of any particular disadvantages to using the above method of comparing __dict__s?

Note: A bit of clarification–when __eq__ and __ne__ are undefined, you’ll find this behavior:

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

That is, a == b evaluates to False because it really runs a is b, a test of identity (i.e., “Is a the same object as b?”).

When __eq__ and __ne__ are defined, you’ll find this behavior (which is the one we’re after):

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

回答 0

考虑这个简单的问题:

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__函数,如文档所述:

比较运算符之间没有隐含的关系。的真相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中,不再需要这样做,因为文档指出:

默认情况下,除非为,否则将__ne__()委托给__eq__()结果并将其取反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有两种类:

  • 经典样式(或旧样式)类,它们继承自object,并声明为class A:class A():或者经典样式类class A(B):在哪里B

  • 新样式类,那些从继承object和声明为class A(object)class A(B):其中B一个新式类。Python 3中只被声明为新的样式类class A:class A(object):class A(B):

对于经典风格的类,比较操作始终调用第一个操作数的方法,而对于新风格的类,则始终调用子类操作数的方法,而不管操作数的顺序如何

所以在这里,如果Number是经典样式的类:

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

如果Number是一个新式类:

  • 双方n1 == n3n3 == n1打电话n3.__eq__;
  • n1 != n3n3 != n1打电话n3.__ne__

要解决Python 2经典样式类的==!=运算符的不可交换性问题,当不支持操作数类型时,__eq____ne__方法应返回NotImplemented值。该文档NotImplemented值定义为:

如果数字方法和丰富比较方法未实现所提供操作数的操作,则可能返回此值。(然后,解释程序将根据操作员尝试执行反射操作或其他回退。)其真实值是true。

在这种情况下操作者的代表的比较操作的反射的方法的的其他操作数。该文档将反映的方法定义为:

这些方法没有交换参数版本(当左参数不支持该操作但右参数支持该操作时使用);相反,__lt__()and __gt__()是彼此的反射,__le__()and __ge__()是彼此的反射,and __eq__()and __ne__()是自己的反射。

结果看起来像这样:

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

如果操作数是不相关的类型(无继承),如果需要and 运算符的可交换性,那么即使对于新式类,也要返回NotImplemented值而不是False正确的做法。==!=

我们到了吗?不完全的。我们有多少个唯一数字?

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

集合使用对象的哈希值,默认情况下,Python返回对象标识符的哈希值。让我们尝试覆盖它:

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

Consider this simple problem:

class Number:

    def __init__(self, number):
        self.number = number


n1 = Number(1)
n2 = Number(1)

n1 == n2 # False -- oops

So, Python by default uses the object identifiers for comparison operations:

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

Overriding the __eq__ function seems to solve the problem:

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

In Python 2, always remember to override the __ne__ function as well, as the documentation states:

There are no implied relationships among the comparison operators. The truth of x==y does not imply that x!=y is false. Accordingly, when defining __eq__(), one should also define __ne__() so that the operators will behave as expected.

def __ne__(self, other):
    """Overrides the default implementation (unnecessary in Python 3)"""
    return not self.__eq__(other)


n1 == n2 # True
n1 != n2 # False

In Python 3, this is no longer necessary, as the documentation states:

By default, __ne__() delegates to __eq__() and inverts the result unless it is NotImplemented. There are no other implied relationships among the comparison operators, for example, the truth of (x<y or x==y) does not imply x<=y.

But that does not solve all our problems. Let’s add a subclass:

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

Note: Python 2 has two kinds of classes:

  • classic-style (or old-style) classes, that do not inherit from object and that are declared as class A:, class A(): or class A(B): where B is a classic-style class;

  • new-style classes, that do inherit from object and that are declared as class A(object) or class A(B): where B is a new-style class. Python 3 has only new-style classes that are declared as class A:, class A(object): or class A(B):.

For classic-style classes, a comparison operation always calls the method of the first operand, while for new-style classes, it always calls the method of the subclass operand, regardless of the order of the operands.

So here, if Number is a classic-style class:

  • n1 == n3 calls n1.__eq__;
  • n3 == n1 calls n3.__eq__;
  • n1 != n3 calls n1.__ne__;
  • n3 != n1 calls n3.__ne__.

And if Number is a new-style class:

  • both n1 == n3 and n3 == n1 call n3.__eq__;
  • both n1 != n3 and n3 != n1 call n3.__ne__.

To fix the non-commutativity issue of the == and != operators for Python 2 classic-style classes, the __eq__ and __ne__ methods should return the NotImplemented value when an operand type is not supported. The documentation defines the NotImplemented value as:

Numeric methods and rich comparison methods may return this value if they do not implement the operation for the operands provided. (The interpreter will then try the reflected operation, or some other fallback, depending on the operator.) Its truth value is true.

In this case the operator delegates the comparison operation to the reflected method of the other operand. The documentation defines reflected methods as:

There are no swapped-argument versions of these methods (to be used when the left argument does not support the operation but the right argument does); rather, __lt__() and __gt__() are each other’s reflection, __le__() and __ge__() are each other’s reflection, and __eq__() and __ne__() are their own reflection.

The result looks like this:

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

Returning the NotImplemented value instead of False is the right thing to do even for new-style classes if commutativity of the == and != operators is desired when the operands are of unrelated types (no inheritance).

Are we there yet? Not quite. How many unique numbers do we have?

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

Sets use the hashes of objects, and by default Python returns the hash of the identifier of the object. Let’s try to override it:

def __hash__(self):
    """Overrides the default implementation"""
    return hash(tuple(sorted(self.__dict__.items())))

len(set([n1, n2, n3])) # 1

The end result looks like this (I added some assertions at the end for validation):

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

回答 1

您需要小心继承:

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

除此之外,您的方法会很好地工作,这就是专用方法的目的。

You need to be careful with inheritance:

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

Check types more strictly, like this:

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

Besides that, your approach will work fine, that’s what special methods are there for.


回答 2

您描述的方式就是我一直以来所做的方式。由于它是完全通用的,因此您始终可以将该功能分解为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

The way you describe is the way I’ve always done it. Since it’s totally generic, you can always break that functionality out into a mixin class and inherit it in classes where you want that functionality.

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

回答 3

这不是一个直接的答案,但似乎足够相关,可以解决,因为它有时可以节省一些冗长的乏味。从文档中直接切出…


functools.total_ordering(cls)

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

这个类必须定义之一__lt__()__le__()__gt__(),或__ge__()。另外,该类应提供一个__eq__()方法。

2.7版中的新功能

@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()))

Not a direct answer but seemed relevant enough to be tacked on as it saves a bit of verbose tedium on occasion. Cut straight from the docs…


functools.total_ordering(cls)

Given a class defining one or more rich comparison ordering methods, this class decorator supplies the rest. This simplifies the effort involved in specifying all of the possible rich comparison operations:

The class must define one of __lt__(), __le__(), __gt__(), or __ge__(). In addition, the class should supply an __eq__() method.

New in version 2.7

@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()))

回答 4

您不必覆盖两者,__eq____ne__只能覆盖,__cmp__但这将对==,!==,<,>等结果产生影响。

is测试对象身份。这意味着,当a和b都持有对同一对象的引用时,isb就会出现True。在python中,您始终会在变量中持有对对象的引用,而不是实际对象,因此从本质上来说,如果a为b为true,则其中的对象应位于相同的内存位置。最重要的是,为什么您要继续压倒这种行为?

编辑:我不知道__cmp__从python 3中删除了,所以避免它。

You don’t have to override both __eq__ and __ne__ you can override only __cmp__ but this will make an implication on the result of ==, !==, < , > and so on.

is tests for object identity. This means a is b will be True in the case when a and b both hold the reference to the same object. In python you always hold a reference to an object in a variable not the actual object, so essentially for a is b to be true the objects in them should be located in the same memory location. How and most importantly why would you go about overriding this behaviour?

Edit: I didn’t know __cmp__ was removed from python 3 so avoid it.


回答 5

从这个答案: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

From this answer: https://stackoverflow.com/a/30676267/541136 I have demonstrated that, while it’s correct to define __ne__ in terms __eq__ – instead of

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

you should use:

def __ne__(self, other):
    return not self == other

回答 6

我认为您要查找的两个术语是相等(==)和同一性(is)。例如:

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

I think that the two terms you’re looking for are equality (==) and identity (is). For example:

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

回答 7

“ 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对象,我有一个实现__init__,__str__,__repr__和__eq__的基类,因此我的POPO对象不承担所有额外(在大多数情况下相同)逻辑的负担。

The ‘is’ test will test for identity using the builtin ‘id()’ function which essentially returns the memory address of the object and therefore isn’t overloadable.

However in the case of testing the equality of a class you probably want to be a little bit more strict about your tests and only compare the data attributes in your class:

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

This code will only compare non function data members of your class as well as skipping anything private which is generally what you want. In the case of Plain Old Python Objects I have a base class which implements __init__, __str__, __repr__ and __eq__ so my POPO objects don’t carry the burden of all that extra (and in most cases identical) logic.


回答 8

我喜欢使用泛型类装饰器,而不是使用子类/混合器

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

Instead of using subclassing/mixins, I like to use a generic class decorator

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

Usage:

@comparable
class Number(object):
    def __init__(self, x):
        self.x = x

a = Number(1)
b = Number(1)
assert a == b

回答 9

这合并了对Algorias答案的评论,并通过单个属性比较对象,因为我不在乎整个字典。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

This incorporates the comments on Algorias’ answer, and compares objects by a single attribute because I don’t care about the whole dict. hasattr(other, "id") must be true, but I know it is because I set it in the constructor.

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

为什么使用’==’或’is’比较字符串有时会产生不同的结果?

问题:为什么使用’==’或’is’比较字符串有时会产生不同的结果?

我有一个Python程序,其中将两个变量设置为value 'public'。在条件表达式我有比较var1 is var2其失败,但如果我把它改为var1 == var2返回True

现在,如果我打开Python解释器并进行相同的“是”比较,则成功。

>>> s1 = 'public'
>>> s2 = 'public'
>>> s2 is s1
True

我在这里想念什么?

I’ve got a Python program where two variables are set to the value 'public'. In a conditional expression I have the comparison var1 is var2 which fails, but if I change it to var1 == var2 it returns True.

Now if I open my Python interpreter and do the same “is” comparison, it succeeds.

>>> s1 = 'public'
>>> s2 = 'public'
>>> s2 is s1
True

What am I missing here?


回答 0

is是身份测试,==是平等测试。您的代码中发生的情况将在解释器中进行模拟,如下所示:

>>> a = 'pub'
>>> b = ''.join(['p', 'u', 'b'])
>>> a == b
True
>>> a is b
False

所以,难怪他们不一样吧?

换句话说:isid(a) == id(b)

is is identity testing, == is equality testing. what happens in your code would be emulated in the interpreter like this:

>>> a = 'pub'
>>> b = ''.join(['p', 'u', 'b'])
>>> a == b
True
>>> a is b
False

so, no wonder they’re not the same, right?

In other words: is is the id(a) == id(b)


回答 1

这里的其他答案是正确的:is用于身份比较,而==用于相等比较。由于您关心的是相等性(两个字符串应包含相同的字符),因此在这种情况下,is运算符完全是错误的,您应该==改用。

is交互工作的原因是(大多数)字符串文字默认情况下是interned。从维基百科:

插入的字符串可加快字符串比较的速度,这有时是严重依赖带有字符串键的哈希表的应用程序(例如编译器和动态编程语言运行时)的性能瓶颈。在不进行实习的情况下,检查两个不同的字符串是否相等涉及检查两个字符串的每个字符。这很慢,原因有几个:字符串的长度固有地为O(n);它通常需要从多个内存区域进行读取,这需要时间。并且读取将填满处理器缓存,这意味着可用于其他需求的缓存较少。对于插入的字符串,在原始的内部操作之后,一个简单的对象身份测试就足够了;这通常被实现为指针相等性测试,

因此,当程序中有两个具有相同值的字符串文字(在程序源代码中逐字键入的单词,并用引号引起来)时,Python编译器将自动内插字符串,使它们都存储在相同的位置内存位置。(请注意,这并不总是会发生,并且发生这种情况的规则非常复杂,因此请不要在生产代码中依赖此行为!)

由于在您的交互式会话中,两个字符串实际上都存储在相同的存储位置中,因此它们具有相同的标识,因此is操作符将按预期工作。但是,如果您通过其他方法构造一个字符串(即使该字符串包含完全相同的字符),则该字符串可能相等,但它不是同一字符串 -也就是说,它具有不同的标识,因为它是存储在内存中的其他位置。

Other answers here are correct: is is used for identity comparison, while == is used for equality comparison. Since what you care about is equality (the two strings should contain the same characters), in this case the is operator is simply wrong and you should be using == instead.

The reason is works interactively is that (most) string literals are interned by default. From Wikipedia:

Interned strings speed up string comparisons, which are sometimes a performance bottleneck in applications (such as compilers and dynamic programming language runtimes) that rely heavily on hash tables with string keys. Without interning, checking that two different strings are equal involves examining every character of both strings. This is slow for several reasons: it is inherently O(n) in the length of the strings; it typically requires reads from several regions of memory, which take time; and the reads fills up the processor cache, meaning there is less cache available for other needs. With interned strings, a simple object identity test suffices after the original intern operation; this is typically implemented as a pointer equality test, normally just a single machine instruction with no memory reference at all.

So, when you have two string literals (words that are literally typed into your program source code, surrounded by quotation marks) in your program that have the same value, the Python compiler will automatically intern the strings, making them both stored at the same memory location. (Note that this doesn’t always happen, and the rules for when this happens are quite convoluted, so please don’t rely on this behavior in production code!)

Since in your interactive session both strings are actually stored in the same memory location, they have the same identity, so the is operator works as expected. But if you construct a string by some other method (even if that string contains exactly the same characters), then the string may be equal, but it is not the same string — that is, it has a different identity, because it is stored in a different place in memory.


回答 2

is关键字是对象标识一个测试而==是一个值比较。

如果使用is,则当且仅当对象是同一对象时,结果才为true。但是,==只要对象的值相同,就为真。

The is keyword is a test for object identity while == is a value comparison.

If you use is, the result will be true if and only if the object is the same object. However, == will be true any time the values of the object are the same.


回答 3

最后要注意的一点是,您可以使用该sys.intern函数来确保获得对相同字符串的引用:

>>> from sys import intern
>>> a = intern('a')
>>> a2 = intern('a')
>>> a is a2
True

如上所述,您不应该is用来确定字符串的相等性。但这可能有助于了解您是否有某种奇怪的要求要使用is

请注意,该intern函数以前是Python 2的内置函数,但已移至sysPython 3 的模块中。

One last thing to note, you may use the sys.intern function to ensure that you’re getting a reference to the same string:

>>> from sys import intern
>>> a = intern('a')
>>> a2 = intern('a')
>>> a is a2
True

As pointed out above, you should not be using is to determine equality of strings. But this may be helpful to know if you have some kind of weird requirement to use is.

Note that the intern function used to be a builtin on Python 2 but was moved to the sys module in Python 3.


回答 4

is是身份测试,==是平等测试。这意味着is检查两种事物是相同的还是等同的。

假设您有一个简单的person对象。如果它的名字叫“ Jack”并且是“ 23”岁,则相当于另一个23岁的Jack,但不是同一个人。

class Person(object):
   def __init__(self, name, age):
       self.name = name
       self.age = age

   def __eq__(self, other):
       return self.name == other.name and self.age == other.age

jack1 = Person('Jack', 23)
jack2 = Person('Jack', 23)

jack1 == jack2 #True
jack1 is jack2 #False

他们是同一年龄,但他们不是同一个人。一个字符串可能等效于另一个,但它不是同一对象。

is is identity testing, == is equality testing. What this means is that is is a way to check whether two things are the same things, or just equivalent.

Say you’ve got a simple person object. If it is named ‘Jack’ and is ’23’ years old, it’s equivalent to another 23yr old Jack, but its not the same person.

class Person(object):
   def __init__(self, name, age):
       self.name = name
       self.age = age

   def __eq__(self, other):
       return self.name == other.name and self.age == other.age

jack1 = Person('Jack', 23)
jack2 = Person('Jack', 23)

jack1 == jack2 #True
jack1 is jack2 #False

They’re the same age, but they’re not the same instance of person. A string might be equivalent to another, but it’s not the same object.


回答 5

这是一个旁注,但是在惯用的python中,您经常会看到类似以下内容:

if x is None: 
    # some clauses

这是安全的,因为保证存在Null对象的一个​​实例(即None)

This is a side note, but in idiomatic python, you will often see things like:

if x is None: 
    # some clauses

This is safe, because there is guaranteed to be one instance of the Null Object (i.e., None).


回答 6

如果不确定自己在做什么,请使用’==’。如果您对此有更多了解,可以对已知对象(例如“无”)使用“ is”。

否则,您将最终想知道为什么事情不起作用以及为什么会发生这种情况:

>>> a = 1
>>> b = 1
>>> b is a
True
>>> a = 6000
>>> b = 6000
>>> b is a
False

我什至不确定在不同的python版本/实现之间是否可以保证某些事情保持不变。

If you’re not sure what you’re doing, use the ‘==’. If you have a little more knowledge about it you can use ‘is’ for known objects like ‘None’.

Otherwise you’ll end up wondering why things doesn’t work and why this happens:

>>> a = 1
>>> b = 1
>>> b is a
True
>>> a = 6000
>>> b = 6000
>>> b is a
False

I’m not even sure if some things are guaranteed to stay the same between different python versions/implementations.


回答 7

根据我在python中的有限经验,is用于比较两个对象以查看它们是否是同一对象,而不是两个具有相同值的不同对象。 ==用于确定值是否相同。

这是一个很好的例子:

>>> s1 = u'public'
>>> s2 = 'public'
>>> s1 is s2
False
>>> s1 == s2
True

s1是unicode字符串,并且s2是普通字符串。它们不是同一类型,但是具有相同的值。

From my limited experience with python, is is used to compare two objects to see if they are the same object as opposed to two different objects with the same value. == is used to determine if the values are identical.

Here is a good example:

>>> s1 = u'public'
>>> s2 = 'public'
>>> s1 is s2
False
>>> s1 == s2
True

s1 is a unicode string, and s2 is a normal string. They are not the same type, but are the same value.


回答 8

我认为这与以下事实有关:当“ is”比较结果为false时,将使用两个不同的对象。如果评估结果为true,则表示内部使用的是完全相同的对象,而不是创建一个新对象,这可能是因为您在不到2秒的时间内创建了它们,并且在优化和使用相同的对象。

这就是为什么您应该使用相等运算符==而不是is来比较字符串对象的值的原因。

>>> s = 'one'
>>> s2 = 'two'
>>> s is s2
False
>>> s2 = s2.replace('two', 'one')
>>> s2
'one'
>>> s2 is s
False
>>> 

在此示例中,我创建了s2,它是一个以前等于’one’的不同字符串对象,但它与并不相同s,因为解释器没有使用相同的对象,因为我最初并未将其分配给’one’,如果我有的话,会让他们成为同一个对象。

I think it has to do with the fact that, when the ‘is’ comparison evaluates to false, two distinct objects are used. If it evaluates to true, that means internally it’s using the same exact object and not creating a new one, possibly because you created them within a fraction of 2 or so seconds and because there isn’t a large time gap in between it’s optimized and uses the same object.

This is why you should be using the equality operator ==, not is, to compare the value of a string object.

>>> s = 'one'
>>> s2 = 'two'
>>> s is s2
False
>>> s2 = s2.replace('two', 'one')
>>> s2
'one'
>>> s2 is s
False
>>> 

In this example, I made s2, which was a different string object previously equal to ‘one’ but it is not the same object as s, because the interpreter did not use the same object as I did not initially assign it to ‘one’, if I had it would have made them the same object.


回答 9

我相信这被称为“ interned”字符串。在优化模式下,Python会这样做,Java也会这样做,C和C ++也会这样做。

如果您使用两个相同的字符串,而不是通过创建两个字符串对象来浪费内存,则具有相同内容的所有已嵌入字符串都指向相同的内存。

这导致Python“ is”运算符返回True,因为两个内容相同的字符串指向同一个字符串对象。这也将在Java和C语言中发生。

但是,这仅对节省内存有用。您不能依靠它来测试字符串是否相等,因为各种解释器和编译器以及JIT引擎不能总是这样做。

I believe that this is known as “interned” strings. Python does this, so does Java, and so do C and C++ when compiling in optimized modes.

If you use two identical strings, instead of wasting memory by creating two string objects, all interned strings with the same contents point to the same memory.

This results in the Python “is” operator returning True because two strings with the same contents are pointing at the same string object. This will also happen in Java and in C.

This is only useful for memory savings though. You cannot rely on it to test for string equality, because the various interpreters and compilers and JIT engines cannot always do it.


回答 10

我回答了这个问题,尽管这个问题已经很老了,因为上面没有答案引用了语言参考

实际上,is运算符检查身份,而==运算符检查是否相等,

从语言参考:

类型影响对象行为的几乎所有方面。甚至对象身份的重要性在某种意义上也受到影响:对于不可变类型,计算新值的操作实际上可能返回对具有相同类型和值的任何现有对象的引用,而对于可变对象,则不允许这样做。例如,在a = 1之后;b = 1,取决于实现,a和b可以或可以不使用值1引用同一对象,但是在c = []之后;d = [],保证c和d引用两个不同的,唯一的,新创建的空列表。(请注意,c = d = []将相同的对象分配给c和d。)

因此,根据上述陈述,我们可以推断出,使用“ is”检查时,不可变类型的字符串可能会失败,而使用“ is”检查时,则可能会检查成功

同样适用于int,tuple也是不可变的类型

I am answering the question even though the question is to old because no answers above quotes the language reference

Actually the is operator checks for identity and == operator checks for equality,

From Language Reference:

Types affect almost all aspects of object behavior. Even the importance of object identity is affected in some sense: for immutable types, operations that compute new values may actually return a reference to any existing object with the same type and value, while for mutable objects this is not allowed. E.g., after a = 1; b = 1, a and b may or may not refer to the same object with the value one, depending on the implementation, but after c = []; d = [], c and d are guaranteed to refer to two different, unique, newly created empty lists. (Note that c = d = [] assigns the same object to both c and d.)

so from above statement we can infer that the strings which is an immutable type may fail when checked with “is” and may checked succeed when checked with “is”

The same applies for int,tuple which are also immutable types


回答 11

==运营商测试值等价。该is运营商的测试对象的身份,Python的测试是否两者实际上是同一个对象(即住在内存中的地址相同)。

>>> a = 'banana'
>>> b = 'banana'
>>> a is b 
True

在此例如,Python只创建了一个字符串对象,都ab参照它。原因是Python在内部缓存和重用了一些字符串作为优化,实际上在内存中只有一个字符串“ banana”,由a和b共享;要触发正常行为,您需要使用更长的字符串:

>>> a = 'a longer banana'
>>> b = 'a longer banana'
>>> a == b, a is b
(True, False)

创建两个列表时,将获得两个对象:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False

在这种情况下,我们可以说这两个列表是等效的,因为它们具有相同的元素,但是不相同,因为它们不是相同的对象。如果两个对象相同,则它们也是等效的,但是如果它们相等,则它们不一定相同。

如果a引用对象,则分配b = a,然后,则两个变量都引用同一个对象:

>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True

The == operator test value equivalence. The is operator tests object identity, Python tests whether the two are really the same object(i.e., live at the same address in memory).

>>> a = 'banana'
>>> b = 'banana'
>>> a is b 
True

In this example, Python only created one string object, and both a and b refers to it. The reason is that Python internally caches and reuses some strings as an optimization, there really is just a string ‘banana’ in memory, shared by a and b; To trigger the normal behavior, you need to use longer strings:

>>> a = 'a longer banana'
>>> b = 'a longer banana'
>>> a == b, a is b
(True, False)

When you create two lists, you get two objects:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False

In this case we would say that the two lists are equivalent, because they have the same elements, but not identical, because they are not the same object. If two objects are identical, they are also equivalent, but if they are equivalent, they are not necessarily identical.

If a refers to an object and you assign b = a, then both variables refer to the same object:

>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True

回答 12

is将比较内存位置。它用于对象级比较。

==将比较程序中的变量。用于在值级别进行检查。

is 检查地址级别是否相等

== 检查价值水平是否相等

is will compare the memory location. It is used for object-level comparison.

== will compare the variables in the program. It is used for checking at a value level.

is checks for address level equivalence

== checks for value level equivalence


回答 13

is是身份测试,==是相等性测试(请参阅Python文档)。

在大多数情况下,如果a is b,则a == b。但是也有exceptions,例如:

>>> nan = float('nan')
>>> nan is nan
True
>>> nan == nan
False

因此,您只能is用于身份测试,而不能用于相等性测试。

is is identity testing, == is equality testing (see Python Documentation).

In most cases, if a is b, then a == b. But there are exceptions, for example:

>>> nan = float('nan')
>>> nan is nan
True
>>> nan == nan
False

So, you can only use is for identity tests, never equality tests.