在Python中将一个变量的值赋给另一个变量时会发生什么?

Rus*_*aev 75 python variables object

这是我学习python的第二天(我知道C++和一些OOP的基础知识.),我对python中的变量有些轻微的混淆.

以下是我目前的理解方式:

Python变量是对象的引用(或指针?)(它们是可变的或不可变的).当我们有类似的东西时num = 5,5在内存中的某处创建不可变对象,并num在某个名称空间中创建名称 - 对象引用对.当我们有a = num,没有任何东西被复制,但现在两个变量都引用同一个对象并被a添加到同一个名称空间.

这就是我的书,用Python自动化无聊的东西,让我感到困惑.由于它是一本新手书,它没有提到对象,命名空间等,它试图解释以下代码:

>>> spam = 42
>>> cheese = spam
>>> spam = 100
>>> spam
100
>>> cheese
42
Run Code Online (Sandbox Code Playgroud)

它提供的解释与C++书籍完全相同,我不满意,因为我们正在处理对象的引用/指针.所以在这种情况下,我想在第3行,因为整数是不可变的,spam正被分配一个全新的指针/引用到内存中的不同位置,即它最初指向的内存未被修改.因此,我们cheese指的是提到的初始对象spam.这是正确的解释吗?

Jon*_*ler 80

作为C++开发人员,您可以将Python变量视为指针.

因此,当您编写时spam = 100,这意味着您"指定指针"(以前指向该对象42)指向该对象100.

之前,cheese被指派指向同一个指向的对象spam,恰好42在那个时候.由于你还没有修改过cheese,它仍然指向42.

在这种情况下,不变性与它无关,因为指针赋值不会改变被指向的对象的任何内容.

  • 不变性很重要,因为它意味着您可以安全地将参考视为 - 如果它是一个值.如果它们是值,那么处理可变对象风险更大. (6认同)
  • 与js中的对象相同 (2认同)

plu*_*ash 20

我看到它的方式有不同的语言观.

  • "语言律师"的观点.
  • "实用程序员"的观点.
  • "实施者"的观点.

从语言律师的角度来看,python变量总是"指向"一个对象.但是,与Java和C++不同,== <=> =等的behvaiour取决于变量所指向的对象的运行时类型.此外,在python内存管理由语言处理.

从实际的程序员角度来看,我们可以将整数,字符串,元组等不可变*对象而非直接值视为无关紧要的细节.例外情况是,当存储大量数值数据时,我们可能希望使用可以直接存储值的类型(例如numpy数组),而不是最终会有一个充满对微小对象的引用的数组的类型.

从实施者的角度来看,大多数语言都有某种假设规则,如果指定的行为是正确的,那么实施是正确的,无论事情是如何实际完成的.

所以,从语言律师的角度来看,你的解释是正确的.从实际程序员的角度来看,你的书是正确的.实现实际上取决于实现.在cpython中,整数是真实对象,尽管从缓存池中获取小值整数而不是重新创建.我不确定其他实现(例如pypy和jython)是做什么的.

*注意这里可变对象和不可变对象之间的区别.对于一个可变对象,我们必须小心处理它"像一个值",因为其他一些代码可能会改变它.对于不可变对象,我们没有这样的顾虑.


end*_*and 19

这是正确的你可以或多或少的变量作为指针.但是,示例代码将有助于解释这实际上是如何工作的.

首先,我们将大量使用该id功能:

返回对象的"标识".这是一个整数,在该生命周期内保证该对象是唯一且恒定的.具有非重叠生存期的两个对象可以具有相同的id()值.

这可能会在您的机器上返回不同的绝对值.

考虑这个例子:

>>> foo = 'a string'
>>> id(foo) 
4565302640
>>> bar = 'a different string'
>>> id(bar)
4565321816
>>> bar = foo
>>> id(bar) == id(foo)
True
>>> id(bar)
4565302640
Run Code Online (Sandbox Code Playgroud)

你可以看到:

  • 原始foo/bar具有不同的id,因为它们指向不同的对象
  • 当bar被分配给foo时,它们的id现在是相同的.这类似于它们都指向你在制作C++指针时看到的内存中的相同位置

当我们更改foo的值时,会将其分配给不同的id:

>>> foo = 42
>>> id(foo)
4561661488
>>> foo = 'oh no'
>>> id(foo)
4565257832
Run Code Online (Sandbox Code Playgroud)

一个有趣的观察结果是整数隐含地具有高达256的此功能:

>>> a = 100
>>> b = 100
>>> c = 100
>>> id(a) == id(b) == id(c)
True
Run Code Online (Sandbox Code Playgroud)

但是超过256,这不再是真的:

>>> a = 256
>>> b = 256
>>> id(a) == id(b)
True
>>> a = 257
>>> b = 257
>>> id(a) == id(b)
False
Run Code Online (Sandbox Code Playgroud)

但是分配ab确实会保持ID和以前一样所示:

>>> a = b
>>> id(a) == id(b)
True
Run Code Online (Sandbox Code Playgroud)


Dav*_*man 16

Python既不是pass-by-reference,也不是pass-by-value.Python变量不是指针,它们不是引用,它们不是值.Python变量是名称.

如果你需要相同的短语类型,或者可能是"按对象传递",可以将它想象为"按别名传递",因为你可以从指示它的任何变量中改变同一个对象,如果它是可变的,但是重新分配变量(别名)仅更改一个变量.

如果有帮助:C变量是您将值写入的框.Python名称是您放在值上的标记.

Python变量的名称是全局(或本地)命名空间中的一个键,它实际上是一个字典.底层值是内存中的某个对象.赋值给该对象命名.将一个变量赋值给另一个变量意味着两个变量都是同一个对象的名称.重新分配一个变量会更改该变量命名的对象,而不会更改其他变量.您已移动标记但未更改上一个对象或其上的任何其他标记.

在CPython实现的底层C代码中,每个Python对象都是一个PyObject*,所以如果你只有指向数据的指针(没有指针到指针,没有直接传递的值),你可以把它想象成像C一样工作.

你可以说Python是值传递,其中值是指针......或者你可以说Python是传递引用,其中引用是副本.

  • 称其为“按名称传递”的问题在于,已经存在一种称为“按名称调用”的参数传递约定,其含义完全不同。在按名称调用中,每次函数使用参数时都会计算参数表达式,如果函数不使用参数,则永远不会计算参数表达式。 (2认同)

Ole*_*kov 10

当你运行spam = 100python时,在内存中再创建一个对象但不改变现有对象.所以你仍然有指向cheese42和spam100的指针


bak*_*ble 8

spam = 100行中发生的事情是用另一个指向另一个对象的指针(类型,值)替换先前的值(int带有值的类型对象的42指针)int100

  • @GertKommer至少在CPython中,所有对象都存在于堆上."价值对象"没有区别.只有对象,*一切都是对象*.这就是为什么典型int的大小大约是28字节,这取决于Python版本,因为它具有整个Py_Object开销.小内容被缓存为CPython优化. (4认同)

pyl*_*ang 8

正如@DeepSpace在评论中提到的那样,Ned Batchelder在博客中对变量(名称)和值的分配做了很好的解释,在PyCon 2015上发表了关于Python名称和值的事实和神话的演讲.它可以让任何关注程度的Pythonistas都具有洞察力.