理解 Python 传递函数参数的按对象调用风格[重复]
- 2025-03-04 08:24:00
- admin 原创
- 87
问题描述:
我不确定我是否理解了 Python 中通过对象调用传递函数参数的概念(解释见http://effbot.org/zone/call-by-object.htm)。似乎没有足够的例子来很好地阐明这个概念(或者我的谷歌能力可能很弱!: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(据我理解,它是指内存位置)保持不变。
因此,对于整数,它是(实际上)通过值传递的,而其他数据结构(实际上)通过引用传递。我尝试更改列表、数字和字典,以测试数据结构是否已就地更改。数字不是,但列表和字典是。
我在上面使用了有效这个词,因为“按对象调用”风格的参数传递似乎根据上述代码中传递的数据结构以两种方式运行
对于更复杂的数据结构(例如 numpy 数组等),是否有任何快速的经验法则来识别哪些参数将通过引用传递以及哪些参数将通过值传递?
解决方案 1:
关键的区别在于,在 C 语言中,变量是内存中的一个盒子,用于存放内容。而在 Python 中,变量是一个名称。
Python 既不是按引用调用也不是按值调用。它是一种更合理的方法!(事实上,我在学习更常见的语言之前就学习了 Python,所以按值调用和按引用调用对我来说很奇怪。)
在 Python 中,有事物,也有名称。列表、整数、字符串和自定义对象都是事物。x
、y
和z
是名称。写作
x = []
意思是“构造一个新事物[]
并给它命名x
”。写作
x = []
foo = lambda x: x.append(None)
foo(x)
[]
意思是“用 name构造一个新东西x
,用 name 构造一个新函数(另一个东西)foo
,然后foo
用 name 调用这个东西x
”。现在foo
只需将其附加None
到它收到的任何内容,因此这简化为“附加None
到空列表”。写作
x = 0
def foo(x):
x += 1
foo(x)
0
表示“用名称构造一个新事物x
,构造一个新函数foo
,然后foo
调用x
”。在里面foo
,赋值只是说“重命名x
为 1 加上原来的名称”,但这并没有改变事物0。
解决方案 2:
其他人已经发布了很好的答案。我认为还有一件事会有所帮助:
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]
已经变异了!
解决方案 3:
Python 中的数字、字符串和元组是不可变的;使用增强赋值将重新绑定名称。
您的其他类型仅仅发生了变异,并且仍然是同一个对象。
扫码咨询,免费领取项目管理大礼包!