Python: &= 运算符
Python: &= operator
当我尝试使用 &=
和 |=
运算符 or/and 两个集合时,我得到了一些奇怪的结果。
s1 = {1,2,3}
s2 = {2,3,4}
tmp = s1
tmp &= s2
果然tmp是{2,3},不知道为什么s1
也改成了{2,3}。
但是,如果我这样做:
tmp = tmp & s2
那么,s1
不变!谁能为我解释 &=
运算符下面发生了什么?
叫做intersection_update
。 return 设置 s 只保留在 t 中也找到的元素。正如您在这张照片中看到的那样;
您正在重建第一个有交集的集合。
按值而不是按引用复制集
tmp = set(s1)
(因为 s1
是 set
的一个实例)
&=
(set.__iadd__
) for set
is implemented differently with &
(set.__add
).
set &= ...
是使用 set.intersection_update
实现的,它就地更新集合。
相关CPython代码(Object/setobject.c
):
set_iand(PySetObject *so, PyObject *other)
{
PyObject *result;
if (!PyAnySet_Check(other))
Py_RETURN_NOTIMPLEMENTED;
result = set_intersection_update(so, other); // <----
if (result == NULL)
return NULL;
Py_DECREF(result);
Py_INCREF(so);
return (PyObject *)so;
}
导致意外结果的不是 &=
运算符,而是 Python 在内存中存储对象并使用变量(名称)引用它们的方式。
Python中的一切都是一个对象,它存储在内存中的某个地方。声明一个变量只是告诉 Python 你正在引用存储在内存中特定位置的特定对象,使用变量名作为对该对象的引用。
您可以使用内置 id()
函数获取有关对象内存位置的一些信息,例如:
s1 = {1,2,3}
s2 = {2,3,4}
tmp = s1
id(tmp)
1763330653544
id(s1)
1763330653544
关于内置 id()
函数的一些信息:
Return the “identity” of an object. This is an integer which is
guaranteed to be unique and constant for this object during its
lifetime. Two objects with non-overlapping lifetimes may have the same
id() value.
CPython implementation detail: This is the address of the object in memory.
Link to Python docs in id() function
如您所见,通过使用id()
函数,名称tmp
和s1
所引用的对象是相同的,因为返回的整数值是相同的.
所以,当你改变其中一个时,另一个也会改变。实际上,我在最后一句话中所说的在技术上是不正确的,因为没有 'either',内存中只有一组对象具有两个不同的引用(tmp
和 s1
)。
s1 = {3, 4, 5}
s2 = s1
s2.add(6)
s1
{3, 4, 5, 6}
id(s1)
1763330653320
id(s2)
1763330653320
尽管并不总是这么简单,所以如果您想了解这一点,我建议您查找 Python 内存管理和变量引用。
真正的 Python 似乎很好地解释了对象引用(使用 names/variables),link to the page.
当我尝试使用 &=
和 |=
运算符 or/and 两个集合时,我得到了一些奇怪的结果。
s1 = {1,2,3}
s2 = {2,3,4}
tmp = s1
tmp &= s2
果然tmp是{2,3},不知道为什么s1
也改成了{2,3}。
但是,如果我这样做:
tmp = tmp & s2
那么,s1
不变!谁能为我解释 &=
运算符下面发生了什么?
叫做intersection_update
。 return 设置 s 只保留在 t 中也找到的元素。正如您在这张照片中看到的那样;
您正在重建第一个有交集的集合。
按值而不是按引用复制集
tmp = set(s1)
(因为 s1
是 set
的一个实例)
&=
(set.__iadd__
) for set
is implemented differently with &
(set.__add
).
set &= ...
是使用 set.intersection_update
实现的,它就地更新集合。
相关CPython代码(Object/setobject.c
):
set_iand(PySetObject *so, PyObject *other)
{
PyObject *result;
if (!PyAnySet_Check(other))
Py_RETURN_NOTIMPLEMENTED;
result = set_intersection_update(so, other); // <----
if (result == NULL)
return NULL;
Py_DECREF(result);
Py_INCREF(so);
return (PyObject *)so;
}
导致意外结果的不是 &=
运算符,而是 Python 在内存中存储对象并使用变量(名称)引用它们的方式。
Python中的一切都是一个对象,它存储在内存中的某个地方。声明一个变量只是告诉 Python 你正在引用存储在内存中特定位置的特定对象,使用变量名作为对该对象的引用。
您可以使用内置 id()
函数获取有关对象内存位置的一些信息,例如:
s1 = {1,2,3}
s2 = {2,3,4}
tmp = s1
id(tmp)
1763330653544
id(s1)
1763330653544
关于内置 id()
函数的一些信息:
Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.
CPython implementation detail: This is the address of the object in memory.
Link to Python docs in id() function
如您所见,通过使用id()
函数,名称tmp
和s1
所引用的对象是相同的,因为返回的整数值是相同的.
所以,当你改变其中一个时,另一个也会改变。实际上,我在最后一句话中所说的在技术上是不正确的,因为没有 'either',内存中只有一组对象具有两个不同的引用(tmp
和 s1
)。
s1 = {3, 4, 5}
s2 = s1
s2.add(6)
s1
{3, 4, 5, 6}
id(s1)
1763330653320
id(s2)
1763330653320
尽管并不总是这么简单,所以如果您想了解这一点,我建议您查找 Python 内存管理和变量引用。
真正的 Python 似乎很好地解释了对象引用(使用 names/variables),link to the page.