Python中是否有任何保证对象是引用?

时间:2016-12-15 13:01:39

标签: python

请考虑以下代码:

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 ...

3 个答案:

答案 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']查找的工作方式:在您的示例中,adict },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复制对象及其包含的所有对象。

  

传入的参数实际上是对象的引用(但引用按值传递)