当前位置: 首页 > 面试题库 >

“最少惊讶”和可变的默认参数

郗福
2023-03-14
问题内容

长时间修改Python的任何人都被以下问题咬伤(或弄成碎片):

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中使用了静态变量,却没有滋生bug?)

编辑:

巴切克举了一个有趣的例子。连同您的大部分评论,特别是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行将是“混合的”,即部分绑定(函数对象)将在定义时发生,部分(默认参数的分配)将在函数调用时发生。

实际行为更加一致:执行该行时将评估该行的所有内容,即在函数定义时进行评估。


问题答案:

实际上,这不是设计缺陷,也不是由于内部因素或性能所致。
这完全是因为Python中的函数是一流的对象,而不仅仅是一段代码。

一旦您想到这种方式,就完全有道理了:函数是根据其定义求值的对象;默认参数属于“成员数据”,因此它们的状态可能会从一个调用更改为另一个调用-完全与其他任何对象一样。

无论如何,Effbot在Python的Default Parameter Values中都很好地解释了这种现象的原因。
我发现它很清楚,我真的建议您阅读它,以更好地了解函数对象的工作原理。



 类似资料:
  • 我遇到了一种巧妙的方法,让命名图普尔从这里使用默认参数。 节点(val=无,left=无,right=无) 如果您希望“right”的默认值为空列表,您会怎么做?您可能知道,使用可变的默认参数(例如列表)是不可以的。 有没有一个简单的方法来实现这一点?

  • 问题内容: 在我的应用程序中,我遇到了以下问题,并对结果感到惊讶: (两个整数)。 这是什么意思? 问题答案: 对于实际值,即结果为。 您使用整数除法得到的结果将向下舍入为的更负值。(也称为“地板部门”) 这就是为什么您会得到以下令人困惑的答案的原因: 注: 这是在Python 3中,其中的结果,“固定”是。因此,如果您没有理由使用Python 2,则应该升级。;) 在Python 3中,如果仍然

  • 问题内容: 我以为我可以在Python 2的函数调用中在变长位置参数之后使用命名参数,但是在导入python类时得到了提示。例如,我正在使用以下“ get”方法编写: 错误看起来像: 我希望能够以几种方式调用该方法: 等等 问题答案: 它确实有效,但仅在Python 3中有效。请参阅PEP 3102。浏览“新功能”文档后,似乎没有2.x向后移植,因此您很不走运。您必须接受任何关键字参数()并手动对

  • 谁能解释一下吗?

  • 问题内容: 在 Python的2.x的 (我用2.7),这是使用默认参数用正确的方式和? 我已经找到了与此主题相关的SO问题,但这是针对 Python 3的 : 使用 args,* kwargs和可选的/默认参数调用Python函数 在那里,他们说此方法有效: 在2.7中,结果为。有没有推荐的方法来定义这种功能? 我以这种方式工作,但我猜有更好的解决方案。 问题答案: 只需将默认参数放在: 现在,