来自函数内部的Python不可变对象

时间:2011-04-13 23:55:46

标签: python

我在这里询问了有关stackoverflow的上一个问题:Python immutable types in function calls

明确表示只将对不可变对象的引用传递给函数,因此将元组传递给函数 not 会导致该对象的完整内存副本。

然而,根据:http://www.testingreflections.com/node/view/5126

  

“某些对象,如字符串,元组,   和数字,是不可变的。 涂改   它们在函数/方法里面会   创建一个新实例和原始实例   函数/方法之外的实例   没有改变。“

我写了一些测试代码,其中一个不可变对象被传递给一个函数。正如所料,我可以通过作为函数头部分定义的参数名称/引用来修改对象,并且所有更改仅在被调用函数中保留,使原始对象不受影响。

所以我的问题是:

只有在尝试更改/修改传入的对象时,才会创建新实例吗?我猜测如果对象没有改变,只需要引用它就可以了。 更重要的是,如果在尝试修改时确实创建了副本,python如何管理内存?使用零拷贝/写时复制,还是创建一个完整的复制对象(整个对象的大小保留在内存中)仅在被调用的函数中可见?

3 个答案:

答案 0 :(得分:6)

如果你认为它们不是包含值的框,而是附加到对象的名称,你会更清楚地思考Python中的变量。任何对象都可以附加任意数量的名称;一些名称是函数的局部名称,并在函数返回时自动从对象中取出。

所以当你做这样的事情时:

name = "Slartibartfast"
person = name

有一个字符串对象,其中包含文本“Slartibartfast”,并且有两个名称可供其引用:nameperson。在这两种情况下你得到相同的对象;您可以使用id()函数进行验证。

字符串的“真实”名称是name还是person?这是一个棘手的问题。该字符串本身并不具有名称;它只是一个字符串。 name不是包含“Slartibartfast”的框,它只是引用该对象的标识符。 person在Python中具有完全相同的地位; name并非“更重要”,因为它是首先分配的。

  

注意:某些对象(如函数和类)具有__name__属性,该属性包含用于在defclass语句中声明它的名称。这是对象的“真实姓名”,如果它可以说有一个。但是,您仍然可以通过任意数量的指定名称来引用它。

现在,假设您“修改”字符串以使其更具荷兰风味:

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)

如果对象是不可变的,则无法更改它。您可以将新对象分配给以前与参数对象关联的名称。要做到这一点,首先需要创建一个新对象。所以是的,你会为一个完整的新对象分配空间。