python用什么方法避免默认参数为空列表?

有时,默认参数为空列表似乎很自然。然而Python在这些情况下会产生意想不到的行为

例如,我有一个函数:

def my_func(working_list=[]):
working_list.append("a")
print(working_list)

第一次调用时,默认值将有效,但之后的调用将更新现有列表(每次调用一个"a")并打印更新后的版本。

那么,python的方法是什么来获得我想要的行为(每次调用都有一个新的列表)?

97515 次浏览
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)

的文档表示你应该使用None作为默认值,并在函数体中显式地使用测试它

在这种情况下,这并不重要,但你可以使用对象标识来测试None:

if working_list is None: working_list = []

你也可以利用python中布尔运算符or的定义:

working_list = working_list or []

但是,如果调用者给你一个空列表(算作false)作为working_list,并期望你的函数修改他给它的列表,这将出乎意料。

我可能跑题了,但请记住,如果你只是想传递一个变量数量的参数,python的方法是传递一个元组*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}

如果函数的目的是修改作为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())。这样你的调用约定就会非常干净。

一种经常改变可选参数的模式是递归函数中隐藏的“memo”参数:

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)

其他答案已经提供了所要求的直接解决方案,然而,由于这是新Python程序员的一个非常常见的陷阱,因此值得添加对Python为何会出现这种行为的解释,这在可变默认参数下的< em >搭车Python指南< / em >中得到了很好的总结:

Python的默认实参在定义函数时求值一次,而不是每次调用函数时求值(就像在Ruby中那样)。这意味着如果你使用一个可变的默认参数并改变它,你并已经改变了该对象,用于所有未来对函数的调用。

已经提供了正确的答案。我只是想给出另一种语法来写你想做的事情,当你想创建一个默认空列表的类时,我发现它更漂亮:

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

我选择UCSC扩展类Python for programmer

这是真的:def Fn(data = []):

A)是一个好主意,这样你的数据列表在每次调用时都是空的。

B)是一个好主意,这样所有不提供任何参数的函数调用都将获得空列表作为数据。

C)是一个合理的想法,只要你的数据是一个字符串列表。

D)是一个坏主意,因为默认的[]会积累数据,并且默认的[]会随着后续的调用而改变。

答:

D)是一个坏主意,因为默认的[]会积累数据,并且默认的[]会随着后续的调用而改变。

引用自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

也许最简单的事情就是在脚本中创建列表或元组的副本。这样就避免了检查的需要。例如,

    def my_funct(params, lst = []):
liste = lst.copy()
. .