我在这里询问了有关stackoverflow的上一个问题:Python immutable types in function calls
明确表示只将对不可变对象的引用传递给函数,因此将元组传递给函数 not 会导致该对象的完整内存副本。
然而,根据:http://www.testingreflections.com/node/view/5126
“某些对象,如字符串,元组, 和数字,是不可变的。 涂改 它们在函数/方法里面会 创建一个新实例和原始实例 函数/方法之外的实例 没有改变。“
我写了一些测试代码,其中一个不可变对象被传递给一个函数。正如所料,我可以通过作为函数头部分定义的参数名称/引用来修改对象,并且所有更改仅在被调用函数中保留,使原始对象不受影响。
所以我的问题是:
只有在尝试更改/修改传入的对象时,才会创建新实例吗?我猜测如果对象没有改变,只需要引用它就可以了。 更重要的是,如果在尝试修改时确实创建了副本,python如何管理内存?使用零拷贝/写时复制,还是创建一个完整的复制对象(整个对象的大小保留在内存中)仅在被调用的函数中可见?
答案 0 :(得分:6)
如果你认为它们不是包含值的框,而是附加到对象的名称,你会更清楚地思考Python中的变量。任何对象都可以附加任意数量的名称;一些名称是函数的局部名称,并在函数返回时自动从对象中取出。
所以当你做这样的事情时:
name = "Slartibartfast"
person = name
有一个字符串对象,其中包含文本“Slartibartfast”,并且有两个名称可供其引用:name
和person
。在这两种情况下你得到相同的对象;您可以使用id()
函数进行验证。
字符串的“真实”名称是name
还是person
?这是一个棘手的问题。该字符串本身并不具有名称;它只是一个字符串。 name
不是包含“Slartibartfast”的框,它只是引用该对象的标识符。 person
在Python中具有完全相同的地位; name
并非“更重要”,因为它是首先分配的。
注意:某些对象(如函数和类)具有
__name__
属性,该属性包含用于在def
或class
语句中声明它的名称。这是对象的“真实姓名”,如果它可以说有一个。但是,您仍然可以通过任意数量的指定名称来引用它。
现在,假设您“修改”字符串以使其更具荷兰风味:
person = person.replace("art", "aart")
“修改”在引号中,因为您无法修改字符串。由于字符串是不可变的,每个字符串操作都会创建一个新字符串。它什么时候发生? 立即。在这种情况下,创建新字符串“Slaartibaartfast”并调整名称person
以引用它。但是,名称name
仍然引用原始字符串,因为您没有告诉它引用其他任何内容。只要至少有一个名称引用它,Python就会保持良好的旧“Slartibartfast”。
处理函数时没有什么不同:
def dutchnametag(name):
name = name.replace("art", "aart")
print "HELLO! My Dutch name is", name
person = "Slartibartfast"
dutchnametag(person)
这里我们将字符串“Slartibartfast”分配给全局名称person
。然后我们将该字符串传递给我们的函数,在那里它接收另外的本地名称name
。然后通过replace()
标识符调用字符串的name
方法,从而创建一个新字符串。然后重新分配标识符name
以指向新字符串。在函数外部,全局标识符person
仍然引用原始字符串,因为没有任何内容将其更改为指向其他任何内容。
答案 1 :(得分:2)
我不是在谈论python本身。但通常,在不可变数据结构中,您使用的每个需要更改状态的方法都将返回一个新对象(具有已修改状态)。旧的将保持不变。
例如,Java可变列表可以具有:
void addItem(Object item) { ... }
对应的不可变列表将有一个
行的方法List addItem(Object item) { ... }
因此,不可变数据结构通常没什么特别之处。在任何语言中,您都可以创建不可变的数据结构。但是有些语言很难或不可能创建可变数据结构(通常是函数式语言)。
某些语言可能提供伪不可变数据结构。它们使一些特殊的数据结构看起来像编码器不可变,而实际上它们不是。
答案 2 :(得分:1)
如果对象是不可变的,则无法更改它。您可以将新对象分配给以前与参数对象关联的名称。要做到这一点,首先需要创建一个新对象。所以是的,你会为一个完整的新对象分配空间。