14

我不确定我是否理解 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(据我所知是指内存位置)保持不变。

因此,在整数的情况下,它(有效地)通过值传递,而其他数据结构(有效地)通过引用传递。我尝试更改 list 、 number 和 dictionary 来测试数据结构是否已就地更改。数字不在列表中,字典在。

我在上面有效地使用了这个词,因为参数传递的“按对象调用”样式似乎具有两种方式,具体取决于上面代码中传递的数据结构

对于更复杂的数据结构(比如 numpy 数组等),是否有任何快速的经验法则来识别哪些参数将通过引用传递,哪些参数将通过值传递?

4

3 回答 3

14

关键的区别在于,在 C 风格的语言中,变量是内存中的一个盒子,你可以在其中放置东西。在 Python 中,变量是一个名称。

Python 既不是引用调用也不是值调用。这是更明智的事情!(事实上​​,我在学习更常见的语言之前就学习了 Python,所以按值调用和按引用调用对我来说似乎很陌生。)

在 Python 中,有东西,有名字。列表、整数、字符串和自定义对象都是东西。x, y, 和z是名称。写作

x = []

意思是“构建一个新事物[]并为其命名x”。写作

x = []
foo = lambda x: x.append(None)
foo(x)

意思是“用 name 构造一个新事物[],用 namex构造一个新函数(这是另一个事物)foo,并用 name 调用foo这个事物x”。现在foo只是附加None到它收到的任何内容,所以这减少到“附加None到空列表”。写作

x = 0
def foo(x):
    x += 1
foo(x)

0意思是“用 name构造一个新事物x,构造一个新函数foo,然后调用foox。在里面foo,赋值只是说“重命名为 1 加上它以前x名字”,但这并没有改变0。

于 2012-04-21T20:57:42.770 回答
10

其他人已经发布了很好的答案。我认为还有一件事会有所帮助:

 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]已经变异了!

于 2012-04-21T21:06:26.113 回答
7

Python 中的数字、字符串和元组是不可变的;使用扩充分配将重新绑定名称。

您的其他类型只是mutated,并且保持相同的对象。

于 2012-04-21T20:58:58.387 回答