代码之家  ›  专栏  ›  技术社区  ›  fmark

在Python中,关键字“is”可能等价于相等运算符的类型

  •  3
  • fmark  · 技术社区  · 14 年前

    对于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. ==
    2. 我怎么知道一个赋值是否会导致一个对象的副本被生成,或者对它的引用被生成?

    更新 如果作业总是参照的,为什么下面不打印 2

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

    为什么这不等同于以下C代码段:

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

    很抱歉这个问题太生疏了,但我是一个Python新手,我觉得理解这个问题很重要。有没有什么读物可以帮助你理解这些问题?

    3 回复  |  直到 12 年前
        1
  •  10
  •   Ferdinand Beyer    14 年前

    这个 is 运算符测试两个对象在物理上是否相同,也就是说它们在内存中的地址是否相同。也可以使用 id() 功能:

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

    == 另一方面,运算符测试语义相等性。也可以通过实现 __eq__() 功能。在语义上,如果两个不同的列表的元素都相等,那么它们是相等的,但在物理上它们将是不同的对象。

    要比较这些类型,请参见以下示例:

    >>> "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 */
    
        2
  •  7
  •   Community rcollyer    7 年前

    不。

    看到了吗 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
  •  1
  •   6502    12 年前

    如果你来自C或C++背景,那么很可能简单地解释Python中的所有变量都是指针。所以声明

     a = 1
    

    确实大致类似于

     Object *a = new Integer(1);
    

    这个 is == 运算符涉及的计算取决于对象的类型。

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

     int *a = getFromCacheOrCreateNewInteger(1);
    

    因此,有时(但这是一个实现细节)不可变对象可能是同一个对象 1+1 is 2-1 ,但不保证):

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

    更让人困惑的是,即使Python中的alla变量确实是指针,但令人惊讶的是Python中没有指针概念,换句话说,无法传递函数,在哪个变量中应该存储某些内容。

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

    另一个额外的烦恼是,如果您真的需要为没有名称的局部变量(例如列表的元素)传递setter,那么它不能是匿名的 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) 但这或多或少是一个意外事件; λ 在Python中是如此的令人讨厌以至于一旦他们发现这是可能的,另一个限制将被添加到语言中以禁止它;-))。

    如果您想改为更改一个命名的local,这在python2.x中是不可能的(但是在python3.x和Python中是可能的) nonlocal ).

    关于何时执行复制以及何时只复制指针的问题,答案非常简单。Python从来不会自动生成副本。。。如果你想做一个拷贝,你必须自己明确地做。这就是为什么经常看到这样的代码:

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

    这个 [:] 这里的符号表示类实例想要存储 复制 如果您创建 Polygon 使用点列表,稍后修改此列表,则几何体不会更改。