请考虑以下代码:
a = { 'b': { 'c': 'hello' } }
c_reference = a['b']
c_reference['c'] = 'goodbye'
Python中是否有保证 始终是参考而非副本?
它肯定会简化这样的代码块:
self.metrics['farms'][fid]['user_sessions']['updated'] = True
self.metrics['farms'][fid]['user_sessions']['last_sample'] = metric['last_sample']
self.metrics['farms'][fid]['user_sessions']['local'] = metric['local']
如果相反它可能是:
metric_sample = self.metrics['farms'][fid]['user_sessions']
metric_sample['updated'] = True
... etc ...
答案 0 :(得分:4)
简单赋值从不在Python中复制对象,它总是让左侧的变量引用与右侧相同的对象。没有任何对象实际改变。
对c_reference['c'] = 'goodbye'
之类的索引对象的赋值既不会,又突变 c_reference
引用的对象。这意味着被称为c_refernce
的对象已更改( mutated 表示相同)。所有涉及c_reference的变量仍然存在,所以他们都会看到"参见"改变。
例如,
>>> a = [1, 2, 3]
>>> b = a # Now they both refer to the same list
>>> a[2] = 4 # Mutates (changes) the list
>>> b
[1, 2, 4] # Changed
>>> a = [4, 5, 6] # Assigns a new list to a
>>> b
[1, 2, 4] # That didn't change what b refers to
列表是可变的,因此可以更改。元组,字符串,数字不是:
>>> a = (1, 2, 3)
>>> a[2] = 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
注意,像+=
这样的运算符令人惊讶,它们的效果取决于对象的可变性:
>>> a = [1,2,3]
>>> b = a
>>> a[2] += 1 # a is mutable, so this mutates
>>> b
[1, 2, 4] # So they still refer to the same, now mutated item
>>> a = 3
>>> b = a
>>> a += 1 # A number isn't mutable, so this let's a refer to the _new object_ that is the result of 3 + 1
>>> b # Still refers to the old one
3
这样的language reference字样(遗漏了):
将对象分配给单个目标按递归方式定义如下:名称绑定到当前本地名称空间中的对象。
所以我所谓的变量叫做 name ,它只是绑定到对象上。这就是保证。
c_reference[2]
之类的表单属于&#34;目标是订阅&#34;:
如果目标是订阅:评估引用中的主表达式。它应该产生可变序列对象(例如列表)或映射对象(例如字典)。接下来,评估下标表达式。
如果primary是可变序列对象(例如列表),则下标必须生成一个普通整数。如果是负数,则将序列的长度添加到其中。结果值必须是小于序列长度的非负整数,并且要求序列将分配的对象分配给具有该索引的项目。如果索引超出范围,则引发IndexError(分配给下标序列不能将新项添加到列表中)。
如果primary是映射对象(例如字典),则下标必须具有与映射的键类型兼容的类型,然后要求映射创建将下标映射到分配的键/数据对。宾语。这可以用相同的键值替换现有的键/值对,或者插入新的键/值对(如果不存在具有相同值的键)。
所以&#34;要求映射创建一个键/数据对,它将下标映射到指定的对象&#34;。这意味着映射发生了变异,新的对在映射中创建。
请注意,我遗漏了很多关于其他案例的详细信息。
答案 1 :(得分:2)
&em; &#34;原来&#34;对象(或其副本)和&#34;对&#34;的引用他们。您在Python中处理的 Everything 是一个参考。对同一底层对象的多个引用都具有相同的状态。这类似于&#34;硬链接&#34;如果您熟悉文件系统,请使用它。
因此,如果您的意思是&#34;是否有任何保证..?&#34;在&#34;的意义上,我可以做一个事后测试来验证......?&#34;然后没有答案,因为这个问题是基于有缺陷的假设。但是可以做的是测试两个引用是否通过询问另一个is
来引用同一个底层对象:
a = { 'foo': [1,2,3] }
b = a['foo']
c = a['foo']
d = [1,2,3]
b is a['foo'] # returns True - same object
b is c # returns True - same object
b is d # returns False - not the same object
b == d # returns True - different objects, same value
另一方面,如果您指的是 a-priori 保证a['b']
查找的工作方式:在您的示例中,a
是dict
},c_reference
确实保证不会成为副本,仅仅因为dict
的工作原理。它也是方括号下标在我能想到的所有标准Python容器中工作的方式。通常,要创建Python对象的副本,您必须做额外的工作:将结果传递给兼容的构造函数 - 在您的情况下可能是dict()
因为a['b']
本身是也是dict
- 或使用copy
模块。
这是Pythonic的一般做事方式,它是内置和标准Python对象的工作方式,但没有一般保证。如果a
不是标准容器,而是某个自定义第三方类的实例,则无法阻止该类的实现者实现__getitem__
,a['b']
返回一个副本,或者确实返回任意的东西。
答案 2 :(得分:0)
是。所有python对象都是引用。如果您想复制对象并获取对象的引用,您可以使用copy.copy()
:
import copy
copy.copy(obj)
或使用copy.deepcopy
复制对象及其包含的所有对象。
传入的参数实际上是对象的引用(但引用按值传递)