在Python中,设置默认值为列表的参数的最佳实践是什么?

73

我有一个Python函数,它接受一个列表作为参数。如果我将参数的默认值设置为空列表,像这样:

def func(items=[]):
    print items

Pylint会提示我"危险的默认值[]作为参数"。所以我想知道这里的最佳实践是什么?


6
这是每个 Python 新手都会踩过一两次的坑,很酷的是 Pylint 防止了你写出一个可怕的 bug! - wim
另一个非常简单的技巧是使用空元组:def func(items=())。元组是不可变的,因此pylint会保持安静,但问题要求列表,因此这并不总是相关或可能的。 - Ant
4个回答

107

使用None作为默认值:

def func(items=None):
    if items is None:
        items = []
    print items

可变默认参数的问题在于它将被所有函数调用共享 -- 请参阅Python教程中相关部分的"重要警告"。

3
哇,我很惊讶人们知道这个。=)我非常讨厌 Python 的这个特性,所以我写了自己的装饰器库,允许使用func(items=new([]))的语法。 - ninjagecko
1
谢谢!看起来Python认为None和list是完全不同的类型,所以我想知道在Python中将参数的默认值设置为不同类型(比如None)是否通常可接受(我知道Python是一种无类型语言,但我来自C++...哈哈)? - Jack Z
3
我认为你的意思是“静态类型”,而不是“强类型”。Python 是强制但动态类型。 - DSM
1
你不必每次都检查它,只有在你预计它不会是你认为的那样时才需要检查。这就像在C++中检查指针/引用是否为空一样..你不需要每次都检查,但在许多情况下,防范起见是有意义的。 - cwa
7
如果我希望 items 是一个可迭代类型,我通常会写成 items = items or [] - Neil G
显示剩余2条评论

10

我第一次遇到这个问题,我的第一反应是“好吧,我根本不想改变列表,所以我真正想要的是默认为一个不可变列表,这样如果我不小心更改它,Python会给我报错。”不可变列表就是元组。所以:

  def func(items=()):
      print items

当然,如果你将它传递给确实需要列表的某些东西(例如 isinstance(items, list)),那么这可能会让你陷入麻烦。但是这本身就是一种代码异味。


3
如果函数内需要进行复制操作,使用my_copy = list(items)。你针对一个常见问题提出了简单而巧妙的解决方案。 - Mark Ransom

3
作为函数和方法声明中默认参数的可变对象,问题在于评估和创建发生在正好同一时刻。Python解析器在读取函数头并同时对其进行评估。
大多数初学者会认为每次调用都会创建一个新对象,但这是不正确的!一个对象(例如一个列表)在声明时被创建,而不是在调用方法时才按需创建。
对于不可变对象,这不是问题,因为即使所有调用都共享相同的对象,它是不可变的,因此其属性保持不变。
作为惯例,您使用None对象来表示使用默认初始化,现在可以在函数体中执行,自然会在调用时进行评估。

1
此外,为了更好地理解Python是什么,这里有一个我准备的小主题片段:
from functools import wraps
def defaultFactories(func):
    'wraps function to use factories instead of values for defaults in call'
    defaults = func.func_defaults
    @wraps(func)
    def wrapped(*args,**kwargs):
        func.func_defaults = tuple(default() for default in defaults)
        return func(*args,**kwargs)
    return wrapped

def f1(n,b = []):
    b.append(n)
    if n == 1: return b
    else: return f1(n-1) + b

@defaultFactories
def f2(n,b = list):
    b.append(n)
    if n == 1: return b
    else: return f2(n-1) + b

>>> f1(6)
[6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1]
>>> f2(6)
[1, 2, 3, 4, 5, 6]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接