网友问题:
def foo(a=[]):
a.append(5)
return a
Python 新手希望这个不带参数调用的函数始终返回一个只有一个元素的列表:。相反,结果非常不同,并且非常令人惊讶(对于新手):[5]
>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]
>>> foo()
[5, 5, 5, 5]
>>> foo()
我的一位经理曾经第一次遇到这个功能,并称其为该语言的“戏剧性设计缺陷”。我回答说,这种行为有一个潜在的解释,如果你不了解内部结构,这确实是非常令人费解和出乎意料的。但是,我无法回答(对自己)以下问题:在函数定义而不是函数执行时绑定默认参数的原因是什么?我怀疑有经验的行为是否有实际用途(谁真的在 C 中使用了静态变量,没有滋生错误?
编辑:
巴切克举了一个有趣的例子。连同你的大部分评论,特别是Utaal的评论,我进一步阐述了:
>>> def a():
... print("a executed")
... return []
...
>>>
>>> def b(x=a()):
... x.append(5)
... print(x)
...
a executed
>>> b()
[5]
>>> b()
[5, 5]
对我来说,设计决策似乎与将参数范围放在何处有关:在函数内部,还是与函数“一起”?
在函数内部执行绑定意味着在调用函数时有效地绑定到指定的默认值,而不是定义函数,这将带来一个严重的缺陷:该行将是“混合”的,因为(函数对象的)绑定的一部分将在定义时发生,而部分(默认参数的分配)将在函数调用时发生。x
def
实际行为更加一致:该行的所有内容在执行该行时都会被评估,这意味着在函数定义时。
网友回答:
假设您有以下代码
fruits = ("apples", "bananas", "loganberries")
def eat(food=fruits):
...
当我看到 eat 的声明时,最不令人惊讶的是认为如果不给出第一个参数,它将等于元组("apples", "bananas", "loganberries")
但是,假设稍后在代码中,我做了类似的事情
def some_random_function():
global fruits
fruits = ("blueberries", "mangos")
那么,如果在函数执行而不是函数声明时绑定默认参数,我会惊讶地(以一种非常糟糕的方式)发现水果已经改变。这将比发现上面的函数正在改变列表更令人惊讶。foo
真正的问题在于可变变量,所有语言在某种程度上都有这个问题。这里有一个问题:假设在 Java 中我有以下代码:
StringBuffer s = new StringBuffer("Hello World!");
Map<StringBuffer,Integer> counts = new HashMap<StringBuffer,Integer>();
counts.put(s, 5);
s.append("!!!!");
System.out.println( counts.get(s) ); // does this work?
现在,我的映射是使用将密钥放入映射时密钥的值,还是通过引用存储密钥?无论哪种方式,都有人感到惊讶;要么是试图使用与他们放入的值相同的值来获取对象的人,要么是似乎无法检索其对象的人,即使他们使用的键实际上是用于将其放入映射的同一对象(这实际上是为什么 Python 不允许将其可变的内置数据类型用作字典键)。StringBuffer
Map
你的例子是一个很好的例子,Python新手会感到惊讶和咬。但我认为,如果我们“修复”这个问题,那么这只会造成一种不同的情况,它们会被咬,而且这种情况会更不直观。此外,在处理可变变量时总是如此;你总是会遇到这样的情况:有人可能会根据他们编写的代码直观地期望一种或相反的行为。
我个人喜欢 Python 目前的方法:定义函数时计算默认函数参数,并且该对象始终是默认对象。我想他们可以使用空列表来特例,但这种特殊大小写会引起更大的惊讶,更不用说向后不兼容了。
网友回答:
实际上,这不是设计缺陷,也不是因为内部或性能。它只是因为Python中的函数是一等对象,而不仅仅是一段代码。
一旦你这样想,那么它就完全有意义了:函数是一个根据其定义被评估的对象;默认参数是一种“成员数据”,因此它们的状态可能会从一个调用更改为另一个调用 – 与任何其他对象完全相同。
无论如何,effbot(Fredrik Lundh)在Python的默认参数值中对这种行为的原因有一个很好的解释。
我发现它很清楚,我真的建议阅读它以更好地了解函数对象的工作原理。
网友回答:
文档的相关部分:
执行函数定义时,默认参数值从左到右计算。这意味着在定义函数时,表达式将计算一次,并且每次调用都使用相同的“预先计算”值。当默认参数是可变对象(例如列表或字典)时,这一点尤其重要:如果函数修改对象(例如,通过将项目附加到列表),则默认值实际上被修改。这通常不是本意。解决此问题的一种方法是用作默认值,并在函数主体中显式测试它,例如:
None
def whats_on_the_telly(penguin=None): if penguin is None: penguin = [] penguin.append("property of the zoo") return penguin
模板简介:该模板名称为【在Python中,不带参数调用的函数始终返回一个只有一个元素的列表】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【Python】栏目查找您需要的精美模板。