带默认参数的函数问题
我有一些关于带默认参数的函数的问题。
import sys
from random import randint
my_list = [1,2,3]
def Veli(a, b = my_list):
my_list.append(randint(1,1500))
print a + b[-1]
print "Number of Refs:", sys.getrefcount(my_list)
print "Def args:", Veli.func_defaults
Veli(1) # This is 4
Veli(1) # We almost always obtain different result because of randint
Veli(1) # Again different results.
print Veli.func_defaults
print "Number of Refs:", sys.getrefcount(my_list)
Veli(1)
my_list = [-5] # old my_list has different address
print "Number of Refs:", sys.getrefcount(my_list) # Less than previous
print "Def args:", Veli.func_defaults
Veli(1) # Now gives same results.
print "Def args:", Veli.func_defaults
Veli(1) # Same result again...
输出结果:(一些数字的结果取决于randint返回的值,当然。)
引用次数:3
默认参数:([1, 2, 3],)
322
1119
740
([1, 2, 3, 321, 1118, 739],)
引用次数:3
303
引用次数:2
默认参数:([1, 2, 3, 321, 1118, 739, 302],)
303
默认参数:([1, 2, 3, 321, 1118, 739, 302],)
303
下面的代码会输出一个比之前小的数字,因为b和my_list不再指向同一个地址。
print "#ref:", sys.getrefcount(my_list) # Less than previous
现在,我们有一种方法(唯一的方法?)来访问b,函数的默认参数:
Veli.func_defaults[0]
抱歉我解释得这么长。以下是我的问题:
这算是个问题吗?我的模块中的字典覆盖了my_list变量,现在my_list的地址和之前不同了。使用全局变量my_list的函数在变化(增长),而我的默认参数却没有。为什么在执行b +
b[-1]
这个表达式时,b
看不到名为my_list
的全局变量?我知道,b
和my_list的地址不同(因为相互对象(比如列表)保证指向不同、唯一的新创建的列表),但为什么Python的实现让b
在作为函数参数时看不到全局变量?你能详细解释一下吗?有没有办法通过
Veli.func_defaults[0]
获得相同的结果?这段代码执行后,假设我想改变我名为Veli的函数的默认参数。我不能用my_list来做到这一点,因为my_list和b的地址不同。一个方法是改变列表的元素,Veli.func_defaults[0]
。还有其他方法吗?(这与上面的代码关系不大)我怎么能获取变量的地址?例如,如何获取
b
的地址?我使用内置函数,比如__hash__
,但应该有更合适的方法。
备注:
a) 这些代码可能出于某种原因没什么用,但我想了解大家的看法。
b) Python 2.6.6 (r266:84292, 2010年9月15日,15:52:39)
[GCC 4.4.5] 在linux2上
2 个回答
我建议你看看这个已有的问题,因为你的问题其实和那个问题是同一个类型的,只是角度稍微不同。
正如你所提到的,当函数的定义被执行时,b
会指向my_list
。当你之后把my_list
改为指向另一个对象时,b
不会受到影响。
如果你想实现“懒绑定”,你可以这样写你的函数:
def Veli(a, b=None):
if b is None:
b = my_list # Gets value of my_list at call time
my_list.append(randint(1,1500))
print a + b[-1]
- 默认参数是在定义函数的时候就确定的。比如这一行
def Veli(a, b = my_list):
,它把当时my_list
指向的对象的引用放到了func_defaults
里。Python 其实是不会用引用传递的——变量保存的是引用,而这些引用都是按值传递的。所以,实际上保存在b
里的,是一个引用(指针)的副本,没人记得它是从哪里来的,也没人会去更新它。 不太确定你在问什么,请澄清一下。如果没有传第二个参数,你可以像其他答案建议的那样,把b
就会是Veli.func_defaults[0]
,但如果传了第二个参数,那当然就不一样了(当然,前提是调用者没有访问func_defaults
...你应该假设他没有)。b
的默认值设为None
,然后如果b 是 None
,就使用全局的my_list
——这样每次调用时就能得到一个最新的引用副本。也许你应该写一个类,把默认值作为self
的一个属性,并使用常见的(... = None): if ... is None: use = default
这种写法。- 内置函数
id
。其实它返回的是什么是由实现决定的(不一定是地址),只要它是一个表示对象身份的整数,也就是说,不同的对象(即使它们的生命周期有重叠)会有不同的id
,而同一个对象在其生命周期内总是会返回相同的id
。CPython 选择的简单返回值是地址。