设置应该是 Python 列表的参数的默认值的最佳实践?

Posted

技术标签:

【中文标题】设置应该是 Python 列表的参数的默认值的最佳实践?【英文标题】:Best practice for setting the default value of a parameter that's supposed to be a list in Python? 【发布时间】:2012-03-20 13:16:19 【问题描述】:

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

def func(items=[]):
    print items

Pylint 会告诉我“危险的默认值 [] 作为参数”。所以我想知道这里的最佳做法是什么?

【问题讨论】:

这是每个 python 新手都会碰到一两次的事情,pylint 阻止你写一个可怕的错误真是太酷了! 另一个非常简单的技巧是使用一个空元组:def func(items=())。元组不可变,因此 pylint 关闭,但问题要求提供一个列表,因此这并不总是相关或可能的。 【参考方案1】:

使用None作为默认值:

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

可变默认参数的问题在于它将在函数的所有调用之间共享——请参阅relevant section of the Python tutorial 中的“重要警告”。

【讨论】:

啊哇,我很惊讶人们知道这件事。 =) 我非常讨厌 python 的这个特性,所以我编写了自己的装饰器库来允许func(items=new([])) 语法。 谢谢!似乎 Python 认为 None 是与 list 完全不同的类型,所以我想知道在 Python 中将参数的默认值设置为不同的类型(例如 None)通常是否可以接受(我知道 Python 是无类型的语言,但我来自 C++...lol)? @wim:我认为您的意思是“静态”,而不是强烈; python 是强类型但动态类型的。 您不必每次都检查它,只有在您认为它不会是您认为应该的样子时才检查它。这就像在 C++ 中检查指针/引用是否为 null .. 你不会每次都检查它,但在许多情况下,防范它确实是有意义的。 如果我期望items 是一个可迭代类型,我通常会写items = items or []【参考方案2】:

我第一次遇到这个,我的直接想法是“好吧,我不想改变列表,所以我真正想要的是默认为不可变列表,所以 Python 会给我一个错误如果我不小心变异了它。”不可变列表只是一个元组。所以:

定义函数(项目=()): 打印项目

当然,如果你将它传递给确实需要列表的东西(例如 isinstance(items, list)),那么这会给你带来麻烦。但无论如何,这是一种代码味道。

【讨论】:

如果您需要在函数内部进行复制,请使用my_copy = list(items)。您为一个常见问题想出了一个简单且非常聪明的解决方案。【参考方案3】:

对于可变对象作为函数和方法声明中的默认参数,问题在于,评估和创建发生在完全相同的时刻。 python-parser 读取函数头并同时对其进行评估。

大多数初学者都认为每次调用都会创建一个新对象,但这是不正确的!一个对象(在您的示例中为列表)是在声明时创建的,而不是在您调用方法时按需创建的。

对于不可变对象这不是问题,因为即使所有调用共享同一个对象,它也是不可变的,因此它的属性保持不变。

作为惯例,您使用None 对象作为默认值来指示使用默认初始化,现在可以在函数体中进行,这自然会在调用时进行评估。

【讨论】:

【参考方案4】:

另外也是为了更好的理解python是什么,这里我的小主题sn-p:

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]

【讨论】:

以上是关于设置应该是 Python 列表的参数的默认值的最佳实践?的主要内容,如果未能解决你的问题,请参考以下文章

Python中具有默认值的多列表迭代

Vue:为数组属性设置默认值的最佳方法?

Python笔记 · 函数参数

Python列表查找具有接近值的元素

python 的 NamedTuple 返回结构是少数应该使用可变默认值的地方之一吗?

Python 默认参数值