我不确定我是否通过传递函数参数的对象样式来理解Python的调用概念(在此处解释http://effbot.org/zone/call-by-object.htm)。似乎没有足够的例子来澄清这个概念(或者我的google-fu可能很弱!:D)
我写了这个有点人为的Python程序,试图理解这个概念
def foo( itnumber, ittuple, itlist, itdict ):
itnumber +=1
print id(itnumber) , itnumber
print id(ittuple) , ittuple
itlist.append(3.4)
print id(itlist) , itlist
itdict['mary'] = 2.3
print id(itdict), itdict
# Initialize a number, a tuple, a list and a dictionary
tnumber = 1
print id( tnumber ), tnumber
ttuple = (1, 2, 3)
print id( ttuple ) , ttuple
tlist = [1, 2, 3]
print id( tlist ) , tlist
tdict = tel = {'jack': 4098, 'sape': 4139}
print '-------'
# Invoke a function and test it
foo(tnumber, ttuple, tlist , tdict)
print '-------'
#Test behaviour after the function call is over
print id(tnumber) , tnumber
print id(ttuple) , ttuple
print id(tlist) , tlist
print id(tdict), tdict
程序的输出是
146739376 1
3075201660 (1, 2, 3)
3075103916 [1, 2, 3]
3075193004 {'sape': 4139, 'jack': 4098}
---------
146739364 2
3075201660 (1, 2, 3)
3075103916 [1, 2, 3, 3.4]
3075193004 {'sape': 4139, 'jack': 4098, 'mary': 2.3}
---------
146739376 1
3075201660 (1, 2, 3)
3075103916 [1, 2, 3, 3.4]
3075193004 {'sape': 4139, 'jack': 4098, 'mary': 2.3}
正如您所看到的,除了传递的整数之外,对象id(我理解的是指内存位置)保持不变。
因此,在整数的情况下,它(有效地)通过值传递,而其他数据结构(有效地)通过引用传递。我尝试更改列表,数字和字典,以测试数据结构是否已更改。这个号码不在列表中 字典是。
我上面使用了有效这个词,因为参数传递的'call-by-object'样式似乎表现出两种方式,具体取决于上面代码中传递的数据结构
对于更复杂的数据结构(比如numpy数组等),是否有任何快速的经验法则 识别哪些参数将通过引用传递以及哪些参数通过值传递?
答案 0 :(得分:12)
关键的区别在于,在C风格的语言中,变量是内存中放置内容的框。在Python中,变量是名称。
Python既不是引用调用也不是按值调用。这是更明智的事情! (事实上,在我学习更常见的语言之前,我学习了Python,因此按值调用和引用调用对我来说似乎很奇怪。)
在Python中,有的东西,还有名称。所有内容都是列表,整数,字符串和自定义对象。 x
,y
和z
是名称。写
x = []
表示“构建新事物[]
并将其命名为x
”。写
x = []
foo = lambda x: x.append(None)
foo(x)
表示“构建名为[]
的新事物x
,构建名为foo
的新函数(这是另一件事),并在事物上调用foo
名称为x
“。现在foo
只是将None
添加到收到的内容中,因此这会减少为“将None
附加到空列表”。写
x = 0
def foo(x):
x += 1
foo(x)
表示“构建名为0
的新内容x
,构建新功能foo
,并在foo
上调用x
”。在foo
内,作业只是说“将x
重命名为1加上以前的”,但这不会改变的事物 0。
答案 1 :(得分:9)
其他人已经发布了很好的答案。我认为还有一件事会有所帮助:
x = expr
评估expr
并将x
绑定到结果。另一方面:
x.operate()
执行到 x
,因此可以更改它(导致相同的底层对象具有不同的值)。
有趣的案例包括:
x += expr
转换为 x = x + expr
(重新绑定)或x.__iadd__(expr)
(修改),有时以非常特殊的方式:
>>> x = 1
>>> x += 2
>>> x
3
(所以x
反弹,因为整数是不可变的)
>>> x = ([1], 2)
>>> x
([1], 2)
>>> x[0] += [3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> x
([1, 3], 2)
这里x[0]
本身是可变的,就地突变了;但是后来Python也试图改变x
本身(和x.__iadd__
一样),因为元组是不可变的,所以这是错误的。但到那时x[0]
已经发生了变异!
答案 2 :(得分:7)
Python中的数字,字符串和元组是不可变的;使用扩充分配将重新绑定名称。
您的其他类型仅仅是变异,并且保持相同的对象。