【问题标题】:Types for which "is" keyword may be equivalent to equality operator in Python“is”关键字可能等同于 Python 中的相等运算符的类型
【发布时间】:2011-03-14 04:42:59
【问题描述】:

对于 Python 中的某些类型,is 运算符似乎等同于 == 运算符。例如:

>>> 1 is 1
True
>>> "a spoon" is "a spoon"
True
>>> (1 == 1) is (2 == 2)
True

但是,情况并非总是如此:

>>> [] == []
True
>>> [] is []
False

这对于诸如列表之类的可变类型是有意义的。但是,元组等不可变类型似乎表现出相同的行为:

>>> (1, 2) == (1, 2)
True
>>> (1, 2) is (1, 2)
False

这引发了几个问题:

  1. == / is 等价性是否与不变性有关?
  2. 上面的行为是指定的还是实现细节?
  3. 最重要的是(并且基本上),我如何知道分配是否会导致生成对象的副本或对其进行引用?

更新: 如果赋值总是通过引用,为什么下面的不打印2?:

>>> a = 1
>>> b = a
>>> a = 2
>>> b
1

为什么这不等价于下面的 C sn-p:

int a = 1;
int *b = &a;
a = 2;
printf("%d\n", *b);

对于这个问题的新奇表示歉意,但我是 Python 新手,觉得理解这一点很重要。你有什么推荐的读物来理解这些问题吗?

【问题讨论】:

    标签: python reference identity variable-assignment immutability


    【解决方案1】:

    is 运算符测试两个对象在物理上是否相同,这意味着它们在内存中的地址是否相同。这也可以使用id() 函数进行测试:

    >>> a = 1
    >>> b = 1
    >>> a is b
    True
    >>> id(a) == id(b)
    True
    

    另一方面,== 运算符测试语义相等性。这也可以通过实现__eq__() 函数被自定义类覆盖。从语义上讲,如果两个不同的列表的元素都相等,则它们是相等的,但在物理上它们将是不同的对象。

    像字符串和元组这样的不可变类型可能会被 Python 实现池化,因此两个文字字符串对象实际上在物理上是相同的。但这并不意味着您总是可以使用is 来比较这些类型,如下例所示:

    >>> "foobar" is "foobar"   # The interpreter knows that the string literals are
    True                       # equal and creates only one shared object.
    >>> a = "foobar"
    >>> b = "foobar"
    >>> a is b        # "foobar" comes from the pool, so it is still the same object.
    True
    >>> b = "foo"     # Here, we construct another string "foobar" dynamically that is
    >>> b += "bar"    # physically not the same as the pooled "foobar".
    >>> a == b
    True
    >>> a is b
    False
    

    Python 中的赋值总是将一个对象的引用绑定到一个变量名,并且从不暗示一个副本。

    更新

    类似于 C,认为 Python 变量总是指针:

    >>> a = 1
    >>> b = a
    >>> a = 2
    >>> b
    1
    

    大致相当于:

    const int ONE = 1;
    const int TWO = 2;
    
    int *a = &ONE;
    int *b = a;  /* b points to 1 */
    a = &TWO;    /* a points to 2, b still points to 1 */
    

    【讨论】:

    • 过于简单:python中没有变量,只有名字
    • “python 中没有变量,只有名称”是一个误导性的、无益的口号。
    【解决方案2】:

    == / 是否与不变性相关?

    没有。

    请参阅 Python ‘==’ vs ‘is’ comparing strings, ‘is’ fails sometimes, why? 了解它为何适用于字符串,并参阅 Python “is” operator behaves unexpectedly with integers 了解它为何适用于整数(因此 bools 出于同样的原因)。

    上面的行为是具体的,还是实现细节?

    实现细节。

    我如何知道分配是否会导致生成对象的副本或对其的引用?

    分配总是通过引用。仅当您明确使用copy.copy(或类似的东西)时才会进行复制。

    编辑:“通过引用”我并不是指 C++ 中的引用。 Python 的赋值将重新绑定变量。更像是

    // int* a, *b;
    a = new int(1);
    b = a;
    a = new int(2);
    printf("%d\n", *b);
    

    【讨论】:

    • 已更新问题作为回应,如果可以,请更新答案。
    • 啊哈,我想我现在明白了,谢谢。有什么可以推荐的读物吗?
    【解决方案3】:

    如果您来自 C 或 C++ 背景,那么合理解释 Python 中的所有变量确实是指针可能更简单。所以声明

     a = 1
    

    确实和

    差不多
     Object *a = new Integer(1);
    

    is 运算符检查指针是否相等,而 == 运算符则涉及取决于对象类型的计算。

    这个方案有点复杂,如果对象是不可变的(例如整数),那么出于效率原因,上面的代码确实有点像

     int *a = getFromCacheOrCreateNewInteger(1);
    

    所以有时(但它是一个实现细节)不可变对象可能是 is 的同一个对象,即使它们是从逻辑角度独立创建的(例如,可能是 1+1 is 2-1,但不能保证):

    >>> 1+2 is 2+1
    True
    >>> 99999+1 is 1+99999
    False
    >>> 
    

    更令人困惑的是,即使 Python 中的所有变量确实是指针,令人惊讶的是,Python 中没有指针概念,换句话说,没有办法传递一个函数,你的变量应该是什么存储。

    为此,您需要传递名称(如果变量是全局变量)或传递要调用的 setter 函数(如果变量是局部变量)。这并不是什么大问题,因为在大多数情况下,您只需要多个返回值,而 Python 已经很好地处理了这一点:

    def foo(x):
        return x+1, x-1
    
    a, b = foo(12)
    

    另一个额外的烦恼是,如果您确实需要为没有名称的局部变量(例如列表的元素)传递 setter,则它不能是匿名的lambda,因为赋值是一个语句lambda 只允许一个表达式。但是,您可以为此定义本地函数...

    def foo(x, setter):
        setter(x + 1)
    
    def bar():
        mylocal = [1,2,3]
    
        def setFirst(value):
            mylocal[0] = value
    
        foo(32, setFirst)
    

    (好吧。我撒谎了……确实可以使用lambda value: mylocal.__setitem__(0, value),但这或多或少是一个不受欢迎的事件;lambda 在 Python 中是如此讨厌,以至于一旦他们发现这是可能的,可能会添加另一个限制禁止它的语言;-) )。

    如果您想更改命名本地,这在 Python 2.x 中是不可能的(但在 Python 3.x 和 nonlocal 中可能)。

    关于何时执行复制以及何时仅复制指针的问题,答案非常简单。 Python 永远不会自动制作副本……如果您想制作副本,则必须自己明确地进行。这就是为什么例如常见的代码如下:

    class Polygon:
        def __init__(pointlist):
            self.pointlist = pointlist[:]
    

    [:] 表示法在这里表示类实例想要存储传递的列表的副本,以便如果您创建带有点列表的Polygon 实例,然后修改这个列表那么几何就不会改变。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-07
      • 2012-09-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多