append方法如何在python中工作?

时间:2015-01-07 14:24:29

标签: python python-2.7

我学习编程语言,测验问题和解决方案是这样的:

def foo(x):
   x.append (3)
   x = [8]
   return x

x=[1, 5]
y= foo(x)
print x
print y

为什么打印如下:

[1 5 3 ]
[8]

为什么x不等于8 ??

5 个答案:

答案 0 :(得分:4)

另外两个答案很棒。我建议你试试id来获取地址。

请参阅以下示例

def foo(x):
   x.append (3)
   print "global",id(x)
   x = [8]
   print "local ",id(x)
   return x

x=[1, 5]
print "global",id(x)
y= foo(x)
print "global",id(x)
print x
print y

输出

global 140646798391920
global 140646798391920
local  140646798392928
global 140646798391920
[1, 5, 3]
[8]

如您所见,变量x的地址在您操作时保持不变,但在您使用=时会发生变化。函数内部的变量赋值使变量本身变为函数

答案 1 :(得分:3)

你有很多事情要做。所以,让我们一步一步走。

x = [1,5]您正在分配1,5到x的列表

y=foo(x)你正在调用foo并传入x并指定从foo返回的任何内容

foo内调用x.append(3),它会将3附加到传入的列表中。

然后设置x = [8],它现在是对局部变量x的引用,然后从foo返回最终设置y = [8]

答案 2 :(得分:2)

对象引用

理解这一点的关键是Python使用对象引用传递变量。这些类似于c ++这样的语言中的指针,但在非常关键的方面有所不同。

进行作业时(使用赋值运算符=):

x = [1, 5]

实际上已经创造了两件事。首先是对象本身,它是列表[1, 5]。该对象是第二个实体的独立实体,它是对象的(全局)对象引用

Object(s)       Object Reference(s)
[1, 5]          x (global)                    <--- New object created

在python中,对象通过对象引用传递给函数;他们没有通过&#34;参考&#34;或者&#34;按价值&#34;就像在c ++中一样。这意味着当x传递到foo函数时,会创建一个新的本地对象引用

Object(s)       Object Reference(s)
[1, 5]          x (global), x (local to foo)  <--- Now two object references

现在foo内部我们调用x.append(3),其中直接更改对象(由foo-local x对象引用引用)本身:

Object(s)       Object Reference(s)
[1, 5, 3]       x (global), x (local to foo)  <--- Still two object references

接下来,我们会做一些不同的事情。我们将local-foo x对象引用(或重新分配,因为之前已存在的对象引用)分配给 NEW LIST

Object(s)       Object Reference(s)
[1, 5, 3]       x (global)                    <--- One object reference remains
[8]             x (local to foo)              <--- New object created

请注意,全局对象引用x仍然存在!它没有受到影响。我们只将local-foo x对象引用重新分配给新列表。

最后,应该很清楚,当函数返回时,我们有:

Object(s)       Object Reference(s)
[1, 5, 3]       x (global)                    <--- Unchanged
[8]             y (global), x (local to foo)  <--- New object reference created

边栏

请注意,local-foo x对象引用仍然存在!这是理解的重要行为,因为如果我们做这样的事情:

def f(a = []):
    a.append(1)
    return a
f()
f()
f()

我们不会得到:

[1]
[1]
[1]

相反,我们会得到:

[1]
[1, 1]
[1, 1, 1]

语句a = []仅在程序首次运行时由解释器评估ONCE,并且该对象引用永远不会被删除(除非我们删除函数本身)。

因此,当调用f()时,local-f a不会更改回[]。它记得&#34;它以前的价值;这是因为本地对象引用仍然有效(即,它尚未被删除),因此该对象不会在函数调用之间收集垃圾。

与指针对比

对象引用与指针不同的一种方式是赋值。如果Python使用实际指针,您会期望以下行为:

a = 1
b = a
b = 2
assert a == 2

但是,这个断言会产生错误。原因是b = 2不影响对象&#34;指向&#34;通过对象引用,b。它创建一个NEW对象(2)并将b重新分配给该对象。

对象引用与指针不同的另一种方式是删除:

a = 1
b = a
del a
assert b is None

此断言会产生错误。原因与上面的例子相同; del a不影响对象&#34;指向&#34;通过对象引用b。它只是删除对象引用a。对象引用b及其指向的对象1不受影响。

您可能会问,&#34;那么我该如何删除实际对象?&#34;答案是你可以做到的!您只能删除对该对象的所有引用。一旦不再引用任何对象,该对象就有资格进行垃圾回收,并且它将被删除(尽管您可以强制使用gc模块)。此功能称为内存管理,它是Python的主要优势之一,也是Python首先使用对象引用的原因之一。

可变性

需要理解的另一个主题是有两种类型的对象:可变对象和不可变对象。可以更改可变对象,而不能更改不可变对象。

list,例如[1, 5],是可变的。另一方面,tupleint是不可变的。

append方法

现在所有这一切都很明确,我们应该能够直截了当地回答问题&#34; append如何在Python中工作?&#34; append()方法的结果是对可变列表对象本身的操作 list已更改&#34;就位#34;可以这么说。没有创建新的list,然后将其分配给foo-local x。这与赋值运算符=形成对比,赋值运算符{{1}}创建一个NEW对象并将该对象分配给对象引用。

答案 3 :(得分:1)

append函数修改传递给函数的x,而为x分配新内容会更改本地范围的值并返回它。

答案 4 :(得分:1)

xfoo的范围特定于函数,并且独立于主调用上下文。 x内的foo开始在主上下文中引用与x相同的对象,因为这是传入的参数,但是当您使用赋值运算符将其设置为{{ 1}}您已经分配了[8]x内的新对象,现在与主要上下文中的foo完全不同。为了进一步说明,请尝试将x更改为:

foo

当我执行时,我得到了这个输出:

def foo(x):
   print("Inside foo(): id(x) = " + str(id(x)))
   x.append (3)
   print("After appending: id(x) = " + str(id(x)))
   x = [8]
   print("After setting x to [8], id(x) = " + str(id(x)))
   return x

(你看到的ID会有所不同,但我希望这一点仍然明确)

您可以看到Inside foo(): id(x) = 4418625336 After appending: id(x) = 4418625336 After setting x to [8], id(x) = 4418719896 [1, 5, 3] [8] 只是改变现有对象 - 无需分配新对象。但是一旦append运算符执行,就会分配一个新对象(并最终返回到主上下文,并将其分配给=)。