Idiomatic way to default mutable arguments
在python中,如果直接将可变类型设为默认参数,则会出现众所周知的边缘情况:
1 2 3 4
| def foo(x=[]): return x
y = foo()
y.append(1)
print foo() |
通常的解决方法是将参数默认为None,然后将其设置在正文中。但是,有3种不同的方法可以做到这一点,其中2种基本上是相同的,但第三种方法是完全不同的。
1 2 3 4
| def foo(x=None):
if x is None:
x = []
return x |
这是我通常看到的。
1 2 3
| def foo(x=None):
x = [] if x is None else x
return x |
语义相同。一行较短,但有些人抱怨python的三元是不自然的,因为它不是从条件开始的,并且建议避免它。
1 2
| def foo(x=None):
x = x or [] |
这是最短的。我今天才知道这种疯狂。我知道Lisp,所以这对我来说可能比一些Python程序员更不令人惊讶,但我从未想过这会在Python中起作用。此行为不同;如果传递的内容不是None,但计算结果为false(如False),则不会覆盖默认值。如果默认值不为false,则不能使用它,因此如果您有一个非空列表或dict默认值,则不能使用它。但根据我的经验,空名单/口述是99%的相关案例。
有没有想过哪一个最像Python?我意识到这里有一种观点,但我希望有人能给出一个很好的例子或理由来说明什么是最惯用的。与大多数社区相比,python倾向于强烈地鼓励人们以某种方式做事,所以我希望这个问题及其答案会有用,即使它不是完全黑白相间的。
- 一般来说,使用x=A or B在python中创建三元语句是不受欢迎的,并且被认为是不好的做法…那就是说去吧…但我认为python三元语句(选项b)是最具Python味的…但我很肯定这个问题会被解决,因为它不太适合这样做(所以讨厌"意见",更喜欢"事实")。
- 另外,如果您希望有一个可变的对象(例如list),为什么不希望x = x if x else []?它稍微短一点,但如果x是True布尔值,则会出现"角"情况。
- "但是第三个身份证"。是?
- 显式检查无>>检查是否有任何虚假值
- 我还有一个给你:以东十一〔五〕号。
- 同样值得注意的是,第三个选项在某些方面的行为有所不同;如果您传入x=[],第三个选项实际上将创建一个新的列表并返回该列表,而其他选项将返回您传入的列表。如果您想在多个地方使用同一个列表对象,共享其内容,这很重要。
- @马塞尔姆,我不明白你说的其他人是什么意思。你是说另一个边缘案例,还是另一种解决问题的方法?我认为是后者。但是**kwargs的行为与调用者完全不同,它允许传递随机参数。很明显,除非您要转发到另一个函数或真正打算使用所有函数,否则不应使用**kwargs。多谢你的便条明确地传来了x=[],我没想到。
- @尼弗里德曼是的,我指的是另一种解决问题的方法。我不同意它的行为"完全不同"。是的,有区别,但这也适用于您的第三个选项(例如,通过0)。我也不同意必须消费所有的kwargs。如果要检查意外的关键字参数,可以将.get()更改为.pop(),并检查kwargs中是否有任何内容。
- 如果你愿意使用单行版本的第一行,那么第二行甚至不是"短一行",使用if x is None: x = []。
我同意1,因为它比较简单;它的"else"分支是隐含的。很难曲解。
在这种情况下,我不同意第3种说法:bool(x)对None、[]、{}、()、0和其他一些东西同样是错误的。如果我错误地将一个0传递到一个期望列表的函数中,那么最好是该函数快速失败,而不是将零误认为空列表!
在其他情况下,c and x else y可以是一个方便的三元运算符,但您必须控制c的类型;当它是局部变量而不是函数参数时更容易控制。
如果你经常发现自己用一个值来代替None,那么用一个函数来代替它。考虑一下像x = replace_none(x, [])这样的问题。
我认为第一种方法在一般情况下是最好的。第一种和第二种方法在功能上是等效的,但是第一种方法对于新来者来说更容易阅读。
1 2 3 4
| def foo(x=None):
if x is None:
x = []
return x |
x or []技巧只能在以下情况下使用:
- 参数不会发生变化(因为您将空集合替换为新集合)。
- 你不在乎论点的不同错误价值(因为你失去了[]、{}、None、my-special-class-with-__bool__之间的任何差异)。
- 你认为任何阅读你的代码的人都知道它是如何工作的(或者想弄清楚它)。
作为旁注,如果默认值为真,则可以使用or技巧:如果x不可靠,x or [1]仍将是[1]。但是你不能用[]作为论据,因为它将被[1]取代。
- "or技巧可以用,如果违约evaluates到真正的x or [1]:将仍然是[1]如果x也falsy"这个断裂,当你通过在一个[]argument,x将falsely改变到[1]。我想那就是意味着运算。
- @ flornquake吗是的,你是的先生,也许我应该有爱拼写出来。
- 我说的是不相关的违约可真x or [];或x or [1]都replace 0by的名单,这是undesirable也是平等的。但我明白你的intention现在。
- 马克@它也在相关的意义,特别是在你以的usually Python的接口。usually经过一个integer为列表会坏regardless。所以,如果违约的dict,让我们说在普通的情况下,我们restrict到自己喜欢dict类型,这是在实践表现的很similarly到dicts(如违约和ordered dicts)。但现在truthy类型在Python的唯一有价值的,是虚假的。如果这是你的违约,然后你不让一个问题的时候,经过正确的类型。但是,如果在真正的价值(如非空dict)违约,你不能通过空dict explicitly,这是一个问题。
- 公平:好的,我已经更新的最后一个比特的海滩,感觉到自由的做出进一步的edits