Python!=操作与“不是”


249

在对此问题的评论中,我看到了一条建议使用的声明

result is not None

result != None

我想知道两者之间的区别是什么,为什么要推荐一个而不是另一个?



1
嗯 虽然这两个问题的答案是相同的概念,但我认为此处的赞扬和详细答案确实有助于身份和平等测试的概念。
viksit”,2010年

Answers:


301

==是一项平等测试。它检查右侧和左侧是否是相等的对象(根据它们__eq____cmp__方法)。

is身份测试。它检查右侧和左侧是否是同一对象。没有方法调用完成,对象不能影响is操作。

您可以将is(和is not)用于单例,例如None,您不关心可能假装为的对象,None或者想要与之进行比较时要防止对象破裂的地方None


3
谢谢您的回答-您能否详细说明与无对象相比对象破裂的情况?
viksit”,2010年

3
@viksit。 None方法很少,几乎没有属性。如果您的__eq__测试需要一个方法或属性,则可能会中断。 def __eq__( self, other ): return self.size == other.size。例如,如果other碰巧会中断None
S.Lott

36
我最喜欢的理解方法是:Python is就像Java一样==。Python ==就像Java一样.equals()。当然,这只有在您了解Java的情况下才有用。
MatrixFrog 2010年

4
@MatrixFrog:在PHP或JavaScript中,我们会说那is===(非常相等),反之is not则是!==(不完全相等)。
Orwellophile

3
is not单个运算符,还是只是否定is内部like 的结果not foo is bar
Asad Moosvi '17

150

首先,让我再谈几个问题。如果您只是想回答问题,请向下滚动到“回答问题”。

定义

对象标识:创建对象时,可以将其分配给变量。然后,您也可以将其分配给另一个变量。还有一个。

>>> button = Button()
>>> cancel = button
>>> close = button
>>> dismiss = button
>>> print(cancel is close)
True

在这种情况下,cancelclose,和dismiss都指的是同一个对象在内存中。您仅创建了一个Button对象,并且所有三个变量都引用了该对象。我们说cancelclosedismiss都指同一对象; 也就是说,它们只引用一个对象。

对象相等性:比较两个对象时,通常不必关心它是否指向内存中完全相同的对象。使用对象相等性,您可以定义自己的规则以比较两个对象。当您写作时if a == b:,您实际上是在说if a.__eq__(b):。这使您可以定义一个__eq__方法,a以便可以使用自己的比较逻辑。

平等比较的理由

理由:两个对象具有完全相同的数据,但不完全相同。(它们不是内存中的同一对象。) 示例:字符串

>>> greeting = "It's a beautiful day in the neighbourhood."
>>> a = unicode(greeting)
>>> b = unicode(greeting)
>>> a is b
False
>>> a == b
True

注意:我在这里使用unicode字符串,因为Python足够聪明,可以重用常规字符串,而无需在内存中创建新的字符串。

在这里,我有两个unicode字符串ab。它们具有完全相同的内容,但是在内存中它们不是相同的对象。但是,当我们比较它们时,我们希望它们比较相等。这里发生的是unicode对象已实现该__eq__方法。

class unicode(object):
    # ...

    def __eq__(self, other):
        if len(self) != len(other):
            return False

        for i, j in zip(self, other):
            if i != j:
                return False

        return True

注意:__eq__on unicode的实现肯定比这更有效。

原理:两个对象具有不同的数据,但是如果某些关键数据相同,则将它们视为同一对象。 示例:大多数类型的模型数据

>>> import datetime
>>> a = Monitor()
>>> a.make = "Dell"
>>> a.model = "E770s"
>>> a.owner = "Bob Jones"
>>> a.warranty_expiration = datetime.date(2030, 12, 31)
>>> b = Monitor()
>>> b.make = "Dell"
>>> b.model = "E770s"
>>> b.owner = "Sam Johnson"
>>> b.warranty_expiration = datetime.date(2005, 8, 22)
>>> a is b
False
>>> a == b
True

在这里,我有两台戴尔显示器,ab。它们具有相同的品牌和型号。但是,它们在内存中既没有相同的数据也没有相同的对象。但是,当我们比较它们时,我们希望它们比较相等。这里发生的事情是Monitor对象实现了该__eq__方法。

class Monitor(object):
    # ...

    def __eq__(self, other):
        return self.make == other.make and self.model == other.model

回答你的问题

比较时None,请始终使用is not。在Python中,没有一个是单例的-内存中只有一个实例。

通过比较身份,可以非常快速地执行此操作。Python检查您所引用的对象是否与全局None对象具有相同的内存地址-两个数字的非常非常快速的比较。

通过比较相等性,Python必须检查您的对象是否具有__eq__方法。如果不是,它将检查每个超类以寻找一种__eq__方法。如果找到一个,Python会调用它。如果该__eq__方法很慢并且在注意到另一个对象为时不立即返回,则尤其糟糕None

你没有实现__eq__吗?然后,Python可能会找到该__eq__方法object并改用该方法-无论如何,它只会检查对象的身份。

在Python中比较大多数其他内容时,您将使用!=


42

考虑以下:

class Bad(object):
    def __eq__(self, other):
        return True

c = Bad()
c is None # False, equivalent to id(c) == id(None)
c == None # True, equivalent to c.__eq__(None)

1
这是一个非常有用和简单的示例。谢谢。
msarafzadeh'2

18

None是单例,因此身份比较将始终有效,而对象可以通过伪造相等比较.__eq__()


有趣啊!在哪种情况下,您可能想伪造相等比较?我猜想这在某种程度上会对安全性产生影响。
viksit”,2010年

1
这不是伪造平等,而是实现平等。有很多原因想要定义一个对象与另一个对象的比较方式。
Thomas Wouters'2

1
我要说的是混乱的含义比安全性的含义更多。
Greg Hewgill'2

2
我还没有提出伪造平等的理由None,但是关于None对其他类型实施平等的副作用可能会出现不正确的行为。它不仅仅涉及安全性,还涉及正确性。
伊格纳西奥·巴斯克斯

嗯,我知道了。Thx的澄清。
viksit”,2010年

10
>>>()是()
真正
>>> 1是1
真正
>>>(1,)==(1,)
真正
>>>(1,)是(1,)
假
>>> a =(1,)
>>> b = a
>>> a是b
真正

有些对象是单例,因此is与它们等效==。大多数不是。


4
其中大多数只能通过巧合/实现细节来工作。()1不是固有单身。
Mike Graham 2010年

1
在CPython实现中,小整数(-NSMALLNEGINTS <= n <= NSMALLPOSINTS)和空元组单例。实际上,它没有记录或保证,但不太可能更改。
短暂

3
它是如何实现的,但没有意义,没有用或没有教育意义。
Mike Graham 2010年

1
特别是,CPython不是唯一的Python实现。对我来说,依靠在Python实现中可能变化的行为似乎通常是一个Bad Idea™。
me_and 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.