What is the pythonic way to avoid default parameters that are empty lists?
有时,拥有一个空列表的默认参数似乎很自然。然而,在这些情况下,python提供了意外的行为。
例如,我有一个函数:
1 2 3 | def my_func(working_list = []): working_list.append("a") print(working_list) |
第一次调用它时,默认值将起作用,但之后的调用将更新现有列表(每次调用一个"a"),并打印更新的版本。
那么,要获得我想要的行为(每一个电话都有一个新的列表)的Python式的方法是什么?
1 2 3 4 5 6 | def my_func(working_list=None): if working_list is None: working_list = [] working_list.append("a") print(working_list) |
文档说您应该使用
现有答案已按要求提供了直接解决方案。但是,由于这对于新的Python程序员来说是一个非常常见的陷阱,因此值得添加一个解释,解释为什么Python的行为是这样的,这在"Python的Hitchakers指南"中被很好地概括为"可变的默认参数":http://docs.python-guide.org/en/latest/writing/gotchass./
引号:"定义函数时,不会每次调用函数时(就像say中的ruby一样),对python的默认参数进行一次评估。"这意味着,如果使用可变的默认参数并对其进行变异,那么将来对函数的所有调用都将对该对象进行变异。"
实现它的示例代码:
1 2 3 4 5 | def foo(element, to=None): if to is None: to = [] to.append(element) return to |
在这种情况下并不重要,但是您可以使用对象标识来测试无:
1 | if working_list is None: working_list = [] |
号
您还可以利用Boolean运算符或在python中定义的方式:
1 | working_list = working_list or [] |
但是,如果调用者将空列表(计为假)作为工作列表提供给您,并期望您的函数修改他提供的列表,则会发生意外的情况。
如果函数的目的是修改作为
但是,如果您不打算改变参数,只需使用它作为列表的起点,就可以简单地复制它:
1 2 3 4 | def myFunc(starting_list = []): starting_list = list(starting_list) starting_list.append("a") print starting_list |
。
(或者,在这个简单的例子中,只是
通常,在Python中改变参数是不好的风格。唯一完全期望改变对象的函数是对象的方法。改变一个可选参数更为罕见——只有在某些调用中才会发生的副作用真的是最佳接口吗?
如果您是从"输出参数"的C习惯中完成的,那么这是完全不必要的——您总是可以将多个值作为一个元组返回。
如果您这样做是为了在不构建中间列表的情况下高效地构建一个长的结果列表,那么可以考虑将其编写为生成器,并在调用时使用
result_list.extend(myFunc()) 。这样,您的呼叫约定保持非常干净。
一种经常改变可选参数的模式是递归函数中隐藏的"memo"参数:
1 2 3 4 5 6 7 8 9 | 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方法就是传递一个tuple
如果要传递元组:
1 2 3 4 5 6 7 8 | 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] |
。
如果要传递字典:
1 2 3 4 | def myFunc(arg1, **kargs): print kargs >>>myFunc(1, option1=2, option2=3) {'option2' : 2, 'option1' : 3} |
已经提供了正确的答案。我只想给出另一种语法来写你想做的事情,当你想创建一个带有默认空列表的类时,我会发现它更漂亮:
1 2 3 4 5 6 | 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运算符语法。我特别喜欢它,因为它是一个整洁的一行,没有冒号,等等,它几乎读起来像一个正常的英语句子。:)
在你的情况下,你可以写
1 2 3 4 | def myFunc(working_list=None): working_list = [] if working_list is None else working_list working_list.append("a") print working_list |