奇怪的行为。类外的函数更改它的实例变量。

33 浏览
0 Comments

奇怪的行为。类外的函数更改它的实例变量。

这个问题已经有答案了:

如何通过引用传递变量?

我不确定我是否理解了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数组等),有没有任何快速的规则可以识别哪些参数将被引用传递,哪些参数将被按值传递?

admin 更改状态以发布 2023年5月22日
0
0 Comments

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

 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 "", line 1, in 
TypeError: 'tuple' object does not support item assignment
>>> x
([1, 3], 2)

此处x[0]本身是可变的,已经被原地改变;但然后Python也尝试变异x本身(如x.__iadd__),这会导致错误,因为元组是不可变的。但是此时x[0]已经发生了变化!

0
0 Comments

主要区别在于C-style语言中,变量是内存中的一个箱子,你可以往里面放东西。在Python中,变量是一个名字。

Python既不是按引用调用也不是按值调用。它更合理!(事实上,我在学会一般的语言之前已经学会了Python,所以按值调用和按引用调用对我来说非常奇怪。)

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

x = []

的意思是“构造一个新物体[],并给它命名为x”。写

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

的意思是“构造一个新物体[],命名为x,构造一个新的函数(另一个物体)命名为foo,并在名字为x的物体上调用foo”。现在,foo只是将None附加到它接收到的任何东西上,因此这缩减为“将None附加到空列表中”。写

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

的意思是“构造一个名为x的新物体0,构造一个名为foo的新函数,并在x上调用foo”。在foo内部,赋值只是将“x”的名称更改为1加上它过去的值,但这并不会改变物体0。

0