有时,有一个空列表的默认参数似乎很自然。然而Python produces unexpected behavior in these situations。
例如,如果我有一个功能:
def my_func(working_list=[]):
working_list.append("a")
print(working_list)
第一次调用时,默认值将起作用,但之后的调用将更新现有列表(每次调用一个 "a"
)并打印更新的版本。
那么,获得我想要的行为的 Pythonic 方法是什么(每次调用都有一个新列表)?
def my_func(working_list=None):
if working_list is None:
working_list = []
# alternative:
# working_list = [] if working_list is None else working_list
working_list.append("a")
print(working_list)
The docs 表示您应该使用 None
作为默认值并在函数主体中明确使用 test for it。
其他答案已经提供了所要求的直接解决方案,但是,由于这对于新的 Python 程序员来说是一个非常常见的陷阱,因此值得添加解释 Python 为何如此行事的解释,这在 The Hitchhikers Guide to Python 中的 {2 下得到了很好的总结}:
Python 的默认参数在定义函数时计算一次,而不是每次调用函数时(就像在 Ruby 中一样)。这意味着,如果您使用可变的默认参数并对其进行变异,您将并且已经对该对象进行了变异,以便将来调用该函数。
在这种情况下并不重要,但您可以使用对象标识来测试无:
if working_list is None: working_list = []
您还可以利用布尔运算符或在 python 中定义的方式:
working_list = working_list or []
尽管如果调用者给你一个空列表(算作假)作为 working_list 并期望你的函数修改他给它的列表,这将出现意外行为。
or
建议看起来不错,但与 0
vs. 1
或 True
vs. False
一起提供时,它的表现令人惊讶。
如果函数的意图是修改作为 working_list
传递的参数,请参阅 HenryR 的答案(=None,检查 None 内部)。
但是,如果您不打算改变参数,只需将其用作列表的起点,您可以简单地复制它:
def myFunc(starting_list = []):
starting_list = list(starting_list)
starting_list.append("a")
print starting_list
(或者在这个简单的情况下只是 print starting_list + ["a"]
但我想这只是一个玩具示例)
一般来说,改变你的论点在 Python 中是不好的风格。唯一完全期望改变对象的函数是对象的方法。改变可选参数甚至更罕见——仅在某些调用中发生的副作用真的是最好的接口吗?
如果你是按照 C 的“输出参数”习惯来做的,那是完全没有必要的——你总是可以将多个值作为一个元组返回。
如果您这样做是为了有效地构建一长串结果而不构建中间列表,请考虑将其编写为生成器并在调用它时使用 result_list.extend(myFunc())。这样,您的调用约定仍然非常干净。
经常更改可选参数的一种模式是递归函数中隐藏的“备忘录”参数:
def depth_first_walk_graph(graph, node, _visited=None):
if _visited is None:
_visited = set() # create memo once in top-level call
if node in _visited:
return
_visited.add(node)
for neighbour in graph[node]:
depth_first_walk_graph(graph, neighbour, _visited)
我可能跑题了,但请记住,如果您只想传递可变数量的参数,pythonic 方法是传递元组 *args
或字典 **kargs
。这些是可选的并且比语法 myFunc([1, 2, 3])
更好。
如果你想传递一个元组:
def myFunc(arg1, *args):
print args
w = []
w += args
print w
>>>myFunc(1, 2, 3, 4, 5, 6, 7)
(2, 3, 4, 5, 6, 7)
[2, 3, 4, 5, 6, 7]
如果要传递字典:
def myFunc(arg1, **kargs):
print kargs
>>>myFunc(1, option1=2, option2=3)
{'option2' : 2, 'option1' : 3}
来自https://docs.python.org/3/reference/compound_stmts.html#function-definitions的报价
执行函数定义时,从左到右计算默认参数值。这意味着表达式在定义函数时被计算一次,并且每次调用都使用相同的“预计算”值。当默认参数是可变对象(例如列表或字典)时,这一点尤其重要:如果函数修改了对象(例如,通过将项目附加到列表中),则默认值实际上已被修改。这通常不是预期的。解决此问题的一种方法是使用 None 作为默认值,并在函数体中显式测试它,例如:
def whats_on_the_telly(penguin=None):
if penguin is None:
penguin = []
penguin.append("property of the zoo")
return penguin
已经提供了好的和正确的答案。我只是想提供另一种语法来编写您想做的事情,例如,当您想要创建一个具有默认空列表的类时,我发现它更漂亮:
class Node(object):
def __init__(self, _id, val, parents=None, children=None):
self.id = _id
self.val = val
self.parents = parents if parents is not None else []
self.children = children if children is not None else []
此代码段使用 if else 运算符语法。我特别喜欢它,因为它是一个简洁的小单行字,没有冒号等。它读起来几乎就像一个正常的英文句子。 :)
在你的情况下,你可以写
def myFunc(working_list=None):
working_list = [] if working_list is None else working_list
working_list.append("a")
print working_list
也许最简单的事情就是在脚本中创建列表或元组的副本。这避免了检查的需要。例如,
def my_funct(params, lst = []):
liste = lst.copy()
. .
我参加了 UCSC 扩展课程 Python for programmer
这是真的: def Fn(data = []):
a) 是一个好主意,这样您的数据列表在每次调用时都是空的。 b) 是一个好主意,这样所有在调用中不提供任何参数的函数调用都将获得空列表作为数据。 c) 是一个合理的想法,只要您的数据是字符串列表。 d) 是个坏主意,因为默认的 [] 会累积数据,而默认的 [] 会随着后续调用而改变。
回答:
d) 是个坏主意,因为默认的 [] 会累积数据,而默认的 [] 会随着后续调用而改变。
不定期副业成功案例分享
f()
,则必须调用f(*l)
,这很糟糕。更糟糕的是,实施mate(['larch', 'finch', 'robin'], ['bumble', 'honey', 'queen'])
会使用可变参数。如果它是def mate(birds=[], bees=[]):
,那就更好了。