任何修补Python足够长的人都被以下问题咬伤(或撕成碎片):
def foo(a=[]):
a.append(5)
return a
Run Code Online (Sandbox Code Playgroud)
Python新手希望这个函数总能返回一个只包含一个元素的列表:[5].结果却非常不同,而且非常惊人(对于新手来说):
>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]
>>> foo()
[5, 5, 5, 5]
>>> foo()
Run Code Online (Sandbox Code Playgroud)
我的一位经理曾经第一次遇到这个功能,并称其为该语言的"戏剧性设计缺陷".我回答说这个行为有一个潜在的解释,如果你不理解内部,那确实非常令人费解和意想不到.但是,我无法回答(对自己)以下问题:在函数定义中绑定默认参数的原因是什么,而不是在函数执行时?我怀疑经验丰富的行为有实际用途(谁真的在C中使用静态变量,没有繁殖错误?)
编辑:
巴泽克提出了一个有趣的例子.再加上你的大部分评论和特别是Utaal,我进一步阐述了:
>>> def a():
... print("a executed")
... return []
...
>>>
>>> def b(x=a()):
... x.append(5)
... print(x)
...
a executed
>>> b()
[5]
>>> b()
[5, 5]
Run Code Online (Sandbox Code Playgroud)
对我而言,似乎设计决策是相对于放置参数范围的位置:在函数内部还是"与它一起"?
在函数内部进行绑定意味着在调用函数时x有效地绑定到指定的默认值,而不是定义,这会产生一个深层次的缺陷:def在某种意义上,该行将是"混合"的(部分绑定)函数对象)将在定义时发生,并在函数调用时发生部分(默认参数的赋值).
实际行为更加一致:执行该行时,该行的所有内容都会得到评估,这意味着在函数定义中.
python language-design least-astonishment default-parameters
有时,使用默认参数(即空列表)似乎很自然.然而,Python在这些情况下会出现意外行为.
例如,我有一个功能:
def my_func(working_list = []):
working_list.append("a")
print(working_list)
Run Code Online (Sandbox Code Playgroud)
第一次使用默认值调用它将起作用,但之后的调用将使用不断更新的列表.
那么,获得我想要的行为的pythonic方法是什么(每个调用都有一个新的列表)?
我的大部分编程背景都是用Java编写的,而且我仍然使用Java编写大部分编程.但是,我开始在工作中学习一些辅助项目的Python,我想学习它尽可能独立于我的Java背景 - 即我不想只用Python编写Java.我应该注意哪些事情?
一个简单的例子 - 当浏览Python教程时,我发现了一个函数(例如列表)的默认可变参数被持久化(从调用到调用时记住).这对我作为Java程序员来说是违反直觉的,很难让我理解.(如果您不理解该示例,请参见此处和此处.)
有人还向我提供了这个列表,我觉得这个列表很有用,但很简短.任何人都有任何其他Java程序员可能会滥用Python的例子......?或者Java程序员会错误地假设或难以理解的东西?
编辑:好的,简要概述了我链接的文章所解决的原因,以防止答案中的重复(如比尔蜥蜴所建议的).(如果我在措辞方面犯了错误,请告诉我,我刚开始使用Python,所以我可能完全不了解所有概念.而且免责声明 - 这些将非常简短,所以如果你不明白它在检查链接时会得到什么.)
(如果你觉得这个问题很有意思,那么请查看链接.:)非常好.)
我理解一个人不应该在Python中使用可变的默认参数值(有一些例外),因为这个值只在定义函数时被评估和存储一次,而不是每次稍后调用该函数.
我对此的理解是这样的(使用下面的例子;请原谅我不精确的语言,因为我只是Python编程的初学者,因为这个而被困在我教科书的功能章节中):
def f(x = [1, 2, 3]):
x.append(4)
print(x)
f()
f()
Run Code Online (Sandbox Code Playgroud)
1)定义函数f,x(f中的局部变量)采用默认变量[1,2,3](甚至在调用函数之前)
2)当调用f()时,x仍然是[1,2,3],因为没有传递给它的参数,并且x继续具有其默认值
3)使用append修改x,变为[1,2,3,4],并按原样打印
然而,这是我的困惑所在.我假设:
4)当f结束时,x被破坏(在堆栈中或你称之为的任何东西)并且不再与列表对象[1,2,3,4]**相关联**
5)回收列表对象[1,2,3,4],因为没有变量引用它了
因此,
6)当第二次调用f()时,我希望Python输出一个错误,因为x现在不再有与之关联的值.换句话说,当Python被回收/销毁时,Python如何重用上次评估的默认值?
感谢您的所有帮助和解释!
**我从Ned Batchelder的变量名称分配页面得到的这种理解(见下文)
