Python 强制性任意参数列表 *args

-2 投票
3 回答
2314 浏览
提问于 2025-04-16 22:37

有没有办法在一个类的方法中定义一个必须要传的 *args(任意参数)呢?

class foo():
 def __init__(self,*args):
      ....

根据这个,你可以创建一个 foo 类的对象,而不需要传任何参数,比如 x=foo(),也就是说这个参数是可选的。有没有什么办法可以把它改成一个非可选的,或者说“至少给我一个参数”的形式呢?

另一个问题是关于列表解包的,像 x=foo(*list) 这样——有没有办法让这个列表被识别为列表,并且自动解包,这样在调用函数或方法的时候就不需要使用 * 了?

谢谢

3 个回答

0

对于“至少给我一个参数”,你只需要检查一下收到的元组的长度,如果没有至少一个,就抛出一个异常。在这里,我利用了空元组被认为是“假”的这个特性,来隐式地做到这一点:

def __init__(self, *args):
    if not args:
       raise TypeError("__init__ takes at least 2 arguments (1 given)")

至于“自动解包”,你也需要自己进行测试并实现这个功能。一种方法可能是:

if len(args) == 1 and not isinstance(args[0], basestring) and iter(args[0]):
    args = args[0]

使用iter()时,它总是会返回真,但如果你传入的东西不是可迭代的,就会抛出一个异常。如果你想提供一个更友好的错误信息,可以捕捉这个异常,然后抛出其他的异常。

另一种选择是写一个方法,让它递归地遍历args中的所有元素以及其中的所有子容器;这样就没关系了。

或者,你也可以让调用者一开始就传入一个可迭代的对象,这样就不用去处理*args了。如果调用者想传入一个单独的项目,可以使用简单的语法把它变成一个列表:foo([item])

2

要么检查一下得到的元组的长度,要么在它前面加一个或多个普通的参数。

不。

4

*args 是用来接收所有那些没有被普通参数占用的参数。

通常,如果你想要一个参数是必须的,你只需要用一个普通参数就可以了:

>>> def foo(arg, *rest):
...     print arg, rest
... 
>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes at least 1 argument (0 given)

如果你觉得把所有参数放在一个元组里更优雅,那你就得自己处理错误的情况:

>>> def foo(*args):
...     if len(args) < 1:
...         raise TypeError('foo() takes at least 1 argument (%i given)' % len(args))
... 
>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in foo
TypeError: foo() takes at least 1 argument (0 given)

但是从这个函数的定义来看,使用这个函数的人并不清楚有多少个参数是必须的。你应该尽量避免这种情况,或者至少要把它写得非常清楚。

还有其他问题:如果你给 foo() 传一个可迭代的参数(比如字符串),你可能得不到想要的结果。


回应你下面的评论,你的第一种方法是对的:使用一个列表。

def scrape(urls):
   for url in urls:
       do_something(url)

调用者只需要传一个只有一个元素的列表:scrape(['localhost'])

更好的做法可能是只接受一个 URL,让调用者自己遍历列表。这样的话,调用者如果想的话,可以并行处理这些操作。


至于你的第二个问题1:你的函数要么接受一个列表作为参数,要么不接受。要么在你的程序中传递列表是有意义的,要么没有意义。

我想,我不太确定你在问什么,但你的整个问题听起来像是你发现了一个新玩意儿,现在想到处用它,不管它是否合适。


1 请不要一次问多个问题!

撰写回答