1

我对具有默认参数的函数有一些疑问。

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]


对不起我的长解释。这是我的问题:

  1. 这是一个问题吗?我的模块粉碎 my_list 变量的字典,然后现在 my_list 变量的默认地址比以前的要高。然后在其主体中使用名为 my_list 的全局变量的函数正在更改(增长),而我的默认参数没有。为什么在执行表达式时b看不到命名的全局变量?我知道,与 my_list 有不同的地址(因为相互对象(如列表)现在保证引用不同的、唯一的、新创建的列表),但是为什么 Python 实现了当 b 是函数参数时看不到全局变量?你能全面解释一下吗?my_lista + b[-1]bb

  2. 有没有办法使用 Veli.func_defaults[0] 获得相同的结果?然后执行此代码,假设我想更改名为 Veli 的函数的默认参数。我不能用 my_list 做到这一点,因为 my_list 和 b 有不同的地址。一种方法是更改​​列表的元素,Veli.func_defaults[0]。有什么不同的方法吗?

  3. (与上面的代码无关)如何获取变量的地址?例如,如何获取地址b?我使用内置函数,例如__hash__,但应该有更合适的方式。


笔记:

a)此代码可能出于任何原因无用,但我想了解意见。
b) linux2 上的 Python 2.6.6 (r266:84292, Sep 15 2010, 15:52:39)
[GCC 4.4.5]

4

2 回答 2

1
  1. 默认参数是函数定义时的界限。该行将对当时碰巧def Veli(a, b = my_list):引用的对象的引用放入. Python从不使用 pass-by-reference - 变量保存引用并且这些引用总是按值传递。因此,实际上保存在 中的内容,一个引用(指针),被复制了,没有人记得它来自哪里,也没有人费心去更新它。my_listfunc_defaultsb
  2. 不太清楚你在问什么,请澄清。b如果没有传递第二个参数,则将Veli.func_defaults[0]是,但如果传递了它显然会有所不同(好吧,除非调用者当然访问func_defaults......你应该假设他没有)。正如另一个答案中所建议的那样,您可以b默认None并使用全局my_listif b is None- 这将为您在每次通话时提供其参考的全新更新副本。也许您应该编写一个类并将默认值保留为属性self并应用通常的(... = None): if ... is None: use = default习语。
  3. 内置函数id。实际上,它的实现定义了 this 返回的内容(不一定是地址),只要它是一个表示对象身份的整数,即不同的对象(具有重叠的生命周期)具有不同id的并且相同的对象总是给出相同的id在其生命周期内。CPython 选择的简单返回值是地址。
于 2011-02-04T14:51:34.163 回答
1

我建议你看看这个现有的问题,因为你的问题本质上与同一个问题有关(尽管对该主题的看法略有不同)。

正如你所注意到的,当函数定义被执行时,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]
于 2011-02-04T14:54:49.587 回答