我什么时候应该在 Python 中使用 *args?

When should I use *args in Python?

在编写 Python 3.x 函数时,我经常偶然发现编写接受单个值和多个值的代码的问题。 采用这个简单的函数:

def capitalize(word):
    return word.capitalize()

此函数适用于将单个字符串作为输入,但如果提供多个​​字符串或字符串列表,则会失败。

我们可以将函数转换为接受字符串列表,这样,如果同时提供一个或多个值,它就会工作。然而,即使我传递了单个值,它也需要包含在一个列表中,并且 return 值也将是一个列表,即使包含一个元素也是如此。

def capitalize(words):
    return [word.capitalize() for word in words]

我最近阅读了有关 *args 的内容,并认为我可以通过将其重写为如下所示来解决必须将包含在列表中的单个字符串作为参数传递给上述函数的尴尬:

def capitalize(*words):
    return [word.capitalize() for word in words]

在我看来,这是对 *args 的合法使用。但是,我发现了一个 pyCon 2015 演讲,演讲者声称 "variable positional arguments are good for removing noise and make your code readable, not to turn a single call api into an N api"

https://www.youtube.com/watch?v=WjJUPxKB164 在 5:30 标记处。

这是对 *args 的合理使用吗?如果这不是 *args 应该使用的方式,是否有处理单参数和多参数函数的既定方法?

*args 将额外的位置参数组合成一个元组,以便您可以在函数中处理它们。这是可选。函数中的必需参数最好是位置参数,在这种情况下,如果不使用这些参数调用它,您的 IDE 会出错。

*args 可以用作扩展,并在不破坏旧实现的情况下用于未来的开发。

# required args
def test_req(arg1, arg2, arg3):
    print arg1, arg2, arg3


# not required
def test_args(*args):
    if args:
        print args, type(args)
        for arg in args:
            print arg

test_req(1, 2, 3)
test_args(1, 2, 3)

输出

1 2 3
(1, 2, 3) <type 'tuple'>
1
2
3

是的,在这里使用 *args 没问题。

如果 *args 比参数列表 (def foo(a,b,c,d,e...)) 更容易阅读,并且不会使了解哪个参数用于什么变得更难,那么您应该使用 *args

您的示例是很好地使用 *args 的完美示例,args 中的所有元素都将以完全相同的方式处理。

当你需要对每个参数执行不同的计算时,你应该避免它,比如:

def foo(*args):
     do_one_stuff(args[1], args[2])
     if args[3]:
         do_another_stuff(args[3], args[4])
     if args[6] != args[7]:
         return args[0]
     else:
         return None

这个例子应该让你感受到如果你使用不当 *args.

函数会是什么样子

简短的回答是:视情况而定。

在python中,*args用于向函数传递未知个参数。与在接受未知数量参数的 C 中调用 printf() 非常相似。

因此,如果您事先不知道用户想要处理多少参数,使用 *args 是一个解决方案。我认为演讲者的意图是不使用此功能除非我们不知道参数的数量。因此,如果您想比较两个参数,您可以使用:

def names_are_equal(name1, name2):
    return name1 == name2

而不是

def names_are_equal(*names):
    return names[0] == names[1]

另一方面,如果您想将未知数量的名字大写,您会使用:

def capitalize_names(*names):
    return [name.upper() for name in names]

请注意,使用 * 在其他情况下会很方便,例如解包参数:

>>> l = [1, 2, 3, 4, 5]
>>> a, *b, c = l
>>> a
1
>>> b
[2, 3, 4]
>>> c
5

除了接受的答案(提出了一些好的观点)之外,您还必须考虑您的函数将如何使用。以 os.path.join 为例,此函数接受 *args 是有意义的,因为人们通常会处理一组路径元素,其中每个元素都可能有自己的名称。例如:

dir_name = os.getcwd()
temp_folder = 'temp'
base_name = 'base_file.txt'
full_path = os.path.join(dir_name, temp_folder, base_name)

另一方面,当您处理自然会被分组到容器对象中的难以区分的元素时,使用 args 更有意义。考虑 str.join.

的以下用法
sentence = 'this is an example sentence'
words = sentence.split()
dash_separated = '-'.join(words)