【问题标题】:Understanding python's name binding了解python的名称绑定
【发布时间】:2015-01-30 02:53:54
【问题描述】:

我正在尝试为自己澄清 Python 的“分配”值规则 到变量。

以下 Python 和 C++ 的比较是否有效?

  1. 在 C/C++ 中,int a=7 语句的意思是,内存分配给一个名为 a 的整数变量(= 符号的 LEFT 上的数量) 然后才将值 7 存储在其中。

  2. 在 Python 中,语句 a=7 的意思是,一个值为 7 的 无名 整数对象(=RIGHT 侧的数量)是首先创建并存储在内存中的某个位置。那么名称a就绑定到这个对象了。

以下 C++ 和 Python 程序的输出似乎证实了这一点,但我希望得到一些反馈,我是否正确。

C++ 为ab 生成不同的内存位置 而ab 似乎指的是Python 中的相同位置 (通过 id() 函数的输出)

C++ 代码

#include<iostream>
using namespace std;
int main(void)
{
  int a = 7;
  int b = a; 
  cout << &a <<  "  " << &b << endl; // a and b point to different locations in memory
  return 0;
}

输出:0x7ffff843ecb8 0x7ffff843ecbc

Python:代码

a = 7
b = a
print id(a), ' ' , id(b) # a and b seem to refer to the same location

输出:23093448 23093448

【问题讨论】:

标签: python variables


【解决方案1】:

是的,你基本上是正确的。在 Python 中,变量名可以被认为是一个值的绑定。这是人们在真正开始深入了解(深入了解)Python 时往往会经历的那些“哈哈”时刻之一。

在 Python 中分配给变量名会使名称绑定到与其当前绑定的值不同的 值(如果确实已经绑定),而不是更改它当前绑定的值:

a = 7   # Create 7, bind a to it.
        #     a -> 7

b = a   # Bind b to the thing a is currently bound to.
        #     a
        #      \
        #       *-> 7
        #      /
        #     b

a = 42  # Create 42, bind a to it, b still bound to 7.
        #     a -> 42
        #     b -> 7

我说的是“创建”,但不一定如此 - 如果某个值已经存在于某处,它可能会被重复使用。

如果底层数据是不可变的(无法更改),这通常会使 Python 看起来与其他语言的行为方式相同(想到 C 和 C++)。这是因为7(名称绑定到的实际对象)无法更改。

但是,对于可变数据(与在 C 中使用指针或在 C++ 中使用引用相同),人们有时会感到惊讶,因为他们没有意识到它背后的值是共享的:

>>> a = [1,2,3]     # a -> [1,2,3]
>>> print(a)
[1, 2, 3]

>>> b = a           # a,b -> [1,2,3]
>>> print(b)
[1, 2, 3]

>>> a[1] = 42       # a,b -> [1,42,3]
>>> print(a) ; print(b)
[1, 42, 3]
[1, 42, 3]

您需要了解a[1] = 42a = [1, 42, 3] 不同。后者是一个赋值,导致a被重新绑定到不同的对象,因此独立于b

前者只是更改ab 绑定的可变数据,这就是它影响两者的原因。

有一些方法可以获取可变值的独立副本,例如:

b = a[:]
b = [item for item in a]
b = list(a)

这些将工作到一个级别(b = a 可以被认为是工作到零级别)这意味着如果a 列表包含其他可变的东西,那些仍将在ab 之间共享:

>>> a = [1, [2, 3, 4], 5]
>>> b = a[:]
>>> a[0] = 8             # This is independent.
>>> a[1][1] = 9          # This is still shared.
>>> print(a) ; print(b)  # Shared bit will 'leak' between a and b.
[8, [2, 9, 4], 5]
[1, [2, 9, 4], 5]

对于真正独立的副本,您可以使用deepcopy,它将根据需要将两个对象分离到尽可能多的级别。

【讨论】:

  • 我对用指针来解释 Python 名称的感觉很复杂。当然,这对已经理解指针的人会有所帮助,但它也鼓励他们将 Python 概念转化为他们的老派思维方式,而不是拥抱 Python 方式。所以我更愿意说 Python 赋值最终是一个字典操作:对象被放入一个字典中,以名字为键。从问题中使用的语言来看,smilingbuddha 似乎已经明白了这一点。 :)
  • @PM2Ring,将其更改为使用“引用”,希望这将使其不太可能被锁定到特定的语言概念。
  • 别担心,pax。但是,嘿,这是你的决定,我不声称自己是一名完美的 Python 老师。 :) 这里的答案应该是永恒的,但我发现以交互方式解释这类东西通常更容易,调整解释直到我找到读者/听众点击的东西。
【解决方案2】:

在您的示例代码中,由于“int”是 C++ 中的内置类型,因此运算符“=”不能被重载,但“=”并不总是创建新对象,它们也可以引用相同的目的。 python对象模块有点像Java,大部分对象是引用而不是副本。

你也可以试试这个:

a = 7
b = 7
print id(a), ' ' , id(b) 

它输出相同的结果,因为 python 会发现 a 和 b 都指向同一个 const 变量

【讨论】:

    猜你喜欢
    • 2013-09-30
    • 1970-01-01
    • 2015-04-11
    • 2016-11-26
    • 1970-01-01
    • 1970-01-01
    • 2017-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多