了解Python的传递函数参数的对象风格
我不知道我通过传递函数参数的对象风格了解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(据我所知,指向memeory位置)保持不变。
所以在整数的情况下,它是(有效)通过值传递和其他数据结构(有效)通过引用传递。 我试着改变列表,数字和字典来testing数据结构是否已经改变了。 这个数字不在名单和字典里。
我在上面有效地使用了这个词,因为根据上面代码中传递的数据结构,“调用对象”parameter passing的风格似乎是两种方式
对于更复杂的数据结构(比如numpy数组等),是否有任何快速的规则来判断哪个参数将被引用传递,哪些是通过值传递的呢?
关键的区别在于,在C风格的语言中,variables是内存中的东西, 在Python中,variables是一个名字。
Python既不是通过引用来调用,也不是按值调用。 这是更明智的东西! (事实上,在我学习Python之前,我学会了Python,所以对于我来说,按值调用和按参考调用看起来很奇怪。)
在Python中,有东西 ,有名字 。 列表,整数,string和自定义对象都是事物。 x
, y
和z
是名称。 写作
x = []
意思是“构build一个新的东西[]
并给它一个名字x
”。 写作
x = [] foo = lambda x: x.append(None) foo(x)
的意思是“用名字x
构造一个新事物[]
,用名字foo
构造一个新的函数(这是另外一个东西),并且在名字为x
的东西上调用foo
。 现在foo
只是将None
附加到它所收到的任何内容,所以这会减less为“将None
附加到空列表”。 写作
x = 0 def foo(x): x += 1 foo(x)
意思是“用名字x
构造一个新的事物0
,构造一个新的函数foo
,并在x
上调用foo
”。 在foo
里面,赋值只是说“把x
重命名为1加上它以前的东西 ”,但是这不会改变0。
其他人已经发布了很好的答案。 还有一件事我认为会有所帮助:
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]
已经发生了变化!
Python中的数字,string和元组是不可变的; 使用增强的分配将重新绑定名称。
你的其他types只是变异 ,并保持相同的对象。
有人纠正我,因为我无法想象这是pythonic的方式来做到这一点,但…
x=[0] def foo(x) x[0] += 1 foo(x)
foo将增加x [0]的值
我再次欢迎有人告诉我这样做的正确方法。