6

使用表单的默认参数x={}通常不能实现 Python 中的预期目的,因为默认参数是在定义而不是调用函数时绑定的。

约定似乎是将可变对象设置为默认参数,x=None然后x is None在调用函数时检查以分配正确的默认值。

因此,如果我想强制x转换为默认为空的字典,我将使用如下内容:

def f(x=None):
    x = dict(x) if x is not None else {}

但是,由于dict可以采用任何可迭代的,我也可以编写这个更简洁的版本:

def f(x=()):
    x = dict(x)

以下哪一个是“正确”的方法?

4

3 回答 3

7

惯用风格是不强制转换为dict; 这样人们就可以使用任何实现正确映射方法的对象。

所以,最pythonic的方法是使用:

def f(x=None):
    if x is None:
        x = {}

然后只使用映射方法。

因此,您通常不应该将论点转换为 dict。您声明您的 API改为接受映射对象,并期望调用者进行转换。

同时接受 adict和 iterable 的唯一原因是当您想要支持允许重复键的有序键值对时,例如对于urllib.urlencodefunction。在这种情况下, adict无法保留该信息,并且该方法不会将可迭代对象转换为 dict,而是将 dict 用作​​可迭代对象。

于 2012-12-10T07:06:37.620 回答
1

这里没有正确或错误的答案,但我会说第一个更惯用(这不会使第二个错误,或坏,或更糟等)

于 2012-12-10T06:54:07.987 回答
0

需要提到的是,只有当函数本身改变thisdict时,作为参数的空才是问题。如果只是读取,则不会出现任何问题。dictdict

如果dict预期发生变化,为什么将空dict作为默认参数有意义?由于调用者没有提供 this,因此他们没有对此的引用,并且对 的更改dict在调用后丢失。

这可能有意义的唯一情况是当函数需要做一些需要更改其输入版本的事情时。在这种情况下,该函数可能应该在不直接更改输入的情况下创建更改后的版本,例如:

# bad way:
def f(x={}):
  x['newkey'] = 'newvalue'
  print(x)  # do something with the extended x

# probably better way:
def f(x={}):
  new_dict = {}
  new_dict.update(x)
  new_dict.update({ 'newkey': 'newvalue' })
  print(new_dict)  # do something with the extended x

这样(后一种方式)原件x永远不会改变,所以不会出现问题。

我知道可变默认参数问题是众所周知的并且被认为是一个问题。但我也有一种感觉,遇到它只是它出现的代码结构存在更严重问题的症状,我想在这篇文章中指出这一点。或者换一种说法:在一段结构合理的代码中,可变的默认参数永远不会导致实际问题。

于 2019-02-11T10:10:50.530 回答