“是”的类型关键字可能相当于Python中的相等运算符
对于 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
这引发了几个问题:
==
/is
等价性与不变性相关吗?- 上述行为是指定的还是实施细节?
- 最重要的是(也是最基本的),我如何知道赋值是否会导致生成对象的副本,或者生成对其的引用?
更新: 如果赋值总是通过引用,为什么下面的代码不打印 2
?:
>>> a = 1
>>> b = a
>>> a = 2
>>> b
1
为什么这不等于下面的 C 代码片段:
int a = 1;
int *b = &a;
a = 2;
printf("%d\n", *b);
对于这个问题的新颖性表示歉意,但我是一个 Python 新手并认为理解这一点很重要。您是否建议阅读任何书籍来理解此类问题?
For some types in Python, the is
operator seems to be equivalent to the ==
operator. For example:
>>> 1 is 1
True
>>> "a spoon" is "a spoon"
True
>>> (1 == 1) is (2 == 2)
True
However, this is not always the case:
>>> [] == []
True
>>> [] is []
False
This makes sense for mutable types such as lists. However, immutable types such as tuples seem to display the same behavior:
>>> (1, 2) == (1, 2)
True
>>> (1, 2) is (1, 2)
False
This raises several questions:
- Is the
==
/is
equivalence related to immutability? - Are the behaviors above specified, or an implementation detail?
- Most importantly (and basically), how can I know if an assignment will result in a copy of an object being made, or a reference to it being made?
Update:
If assignment is always by reference, why doesn't the following print 2
?:
>>> a = 1
>>> b = a
>>> a = 2
>>> b
1
Why isn't this equivalent to the following C snippet:
int a = 1;
int *b = &a;
a = 2;
printf("%d\n", *b);
Apologies for the newbness of this question, but I am a Python novice and feel that it is important to understand this. Is there any reading you would recommend to understand these sort of issues?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
is 运算符测试两个对象在物理上是否相同,这意味着它们在内存中是否具有相同的地址。这也可以使用
id()
函数进行测试:另一方面,
==
运算符测试语义相等性。这也可以通过实现 __eq__() 函数由自定义类覆盖。从语义上讲,如果两个不同的列表的元素都相等,则它们相等,但从物理上讲,它们将是不同的对象。像字符串和元组这样的不可变类型可能会被 Python 实现池化,因此两个文字字符串对象实际上在物理上是相同的。但这并不意味着您始终可以使用 is 来比较这些类型,如以下示例所示:
Python 中的赋值总是将对对象的引用绑定到变量名称,并且从不暗示副本。
更新
与 C 类似,认为 Python 变量始终是指针:
大致相当于:
The
is
operator tests if two objects are physically the same, that means if they have the same address in memory. This can also be tested using theid()
function:The
==
operator on the other hand, tests for semantical equality. This can also be overridden by custom classes by implementing the__eq__()
function. Semantically, two different lists are equal if their elements are all equal, but physically they will be different objects.Immutable types like strings and tuples might be pooled by the Python implementation, so that two literal string objects are in fact physically identical. But this does not mean that you can always use
is
to compare those types, as demonstrated in the following example:Assignments in Python always bind the reference to an object to a variable name and never implies a copy.
UPDATE
Analogous to C, think of Python variables always being pointers:
Roughly equivalent to:
否。
请参阅Python '== ' 与 'is' 比较字符串,'is' 有时会失败,为什么? 为什么它适用于字符串,以及 Python“is”运算符在整数上的表现出乎意料为什么它对整数起作用(因此布尔值对于相同的原因)。
实施细节。
分配始终是通过引用进行的。仅当您显式使用
copy.copy
(或类似的东西)时才会完成复制。编辑:“通过引用”我并不是指 C++ 中的引用。 Python 的赋值将重新绑定变量。它更像是
No.
See Python ‘==’ vs ‘is’ comparing strings, ‘is’ fails sometimes, why? on why it works on strings, and Python “is” operator behaves unexpectedly with integers on why it works on integers (thus bools for the same reason).
Implementation detail.
Assignment is always by reference. Copying is done only if you explicitly use
copy.copy
(or something like that).Edit: By "by reference" I don't mean references in C++. Python's assignment will rebind the variable. It's more like
如果您有 C 或 C++ 背景,那么可能更容易理解 Python 中的所有变量实际上都是指针。因此,该语句
确实大致类似于
is
运算符检查指针是否相等,而==
运算符则涉及依赖于对象类型的计算。这个方案有点复杂,如果对象是不可变的(例如整数),那么出于效率原因,上面的代码确实有点像
有时(但这是一个实现细节)不可变对象可能是相同的对象 < code>is 即使它们是从逻辑角度独立创建的(例如,
1+1 可能是 2-1
,但不能保证):添加更多令人困惑的是,即使Python中的所有变量确实都是指针,令人惊讶的是Python中也没有指针概念,换句话说,没有办法传递一个函数来存储你的变量中的某些内容。
为此,您需要传递名称(如果变量是全局变量)或传递要调用的 setter 函数(如果变量是局部变量)。这其实并不是一个大烦恼,因为在大多数情况下你只需要多个返回值,而 Python 已经很好地处理了这个问题:
另一个额外的烦恼是,如果你真的需要为没有名称的局部变量(例如元素)传递 setter列表)它不能是匿名
lambda
,因为赋值是一个语句,而lambda
只允许单个表达式 >。但是,您可以为此定义本地函数...(好吧。我撒谎了...确实可以使用 lambda 值:mylocal.__setitem__(0, value) 但这或多或少是不需要的事件;
lambda
在 Python 中是如此令人讨厌,以至于一旦他们发现这是可能的,就会在该语言中添加另一个限制来禁止它;-))。如果您想更改命名的本地变量,这在 Python 2.x 中是不可能的(但在 Python 3.x 和
nonlocal
中是可能的)。关于何时执行复制以及何时仅复制指针的问题,答案非常简单。 Python 永远不会自动创建副本...如果你想创建副本,你必须自己显式地执行。这就是为什么常见的代码如下:
[:]
表示法在这里意味着类实例想要存储传递的列表的副本,这样如果您使用点列表创建一个Polygon
实例,然后修改该列表,然后几何图形不会改变。If you come from a C or C++ background then it's probably simpler to rationalize that all variables in Python are indeed pointers. So the statement
is indeed roughly similar to
The
is
operator checks for pointer equality and the==
operator instead involves a computation that depends on the type of objects.A little complication to this scheme is that if the objects are immutable (e.g. an integer) then for efficiency reasons the code above is indeed a bit more like
so sometimes (but it's an implementation detail) immutable objects may be are the same object for
is
even if they are created independently from a logical point of view (e.g. may be that1+1 is 2-1
, but no guarantees):To add a bit more of confusion is that even if indeed alla variables in Python are pointers quite surprisingly there is no pointer concept in Python, in other words there is no way to pass a function in which of your variables something should be stored.
To do that you need either to pass a name (if the variable is a global) or to pass a setter function to be called (if the variable is a local). This is not really a big annoyance since in most cases you just want multiple return values and this is handled nicely already by Python:
Another extra annoyance is that if you really need to pass a setter for a local variable without a name (e.g. an element of a list) it cannot be an anonymous
lambda
because assignment is a statement andlambda
is only allowed a single expression. You can however define local functions for that...(OK. I lied... it's indeed possible to use
lambda value: mylocal.__setitem__(0, value)
but this is more or less an unwanted incident;lambda
is so hated in Python that probably once they discover this is possible another limitation will be added to the language to forbid it ;-) ).If you want to change a named local instead this is just impossible in Python 2.x (but possible with Python 3.x and
nonlocal
).About the question of when copies are performed and when instead just the pointer is copied the answer is very simple. Python never ever makes a copy automatically... if you want to make a copy the you must do it yourself explicitly. This is why for example is common to see code like:
The
[:]
notation means here that the class instance wants to store a copy of the list passed so that if you create an instance ofPolygon
with a list of points and the later modify this list then the geometry doesn't change.