我在Python Cookbooks中阅读了一个解决方案,用于创建仅允许名称参数的函数。我编写了自己的代码来试试:
class Reporter(object):
def __init__(self, *, testline=None, sw_ver= None, directory=None):
pass
if __name__ == "__main__"
r = Reporter()
但解释器显示此错误:
File "Reporter.py", line 6
def __init__(self, *, testline=None, sw_ver= None, directory=None):
^
SyntaxError: invalid syntax
为什么会显示这个?
答案 0 :(得分:7)
您使用的代码是有效的语法,但对于 python3 ,所以本书必须使用python3语法,它只允许关键字参数,pep-3102:< / p>
您还可以在参数列表中使用bare *来表示您不接受可变长度参数列表,但是您确实有关键字参数。
使用您的代码并在python 3中传递非关键字会出错,但原因不同:
TypeError Traceback (most recent call last)
<ipython-input-2-b4df44fa1e0c> in <module>()
1 if __name__ == "__main__":
----> 2 r = Reporter(4)
3
TypeError: __init__() takes 1 positional argument but 2 were given
使用关键字后,工作正常:
In [4]: if __name__ == "__main__":
r = Reporter(testline=4)
...:
使用具有相同语法的函数可能会产生更明显的错误:
def f(*, foo=None, bar=None):
return foo, bar
In [6]: f(4)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-a122d87dbe99> in <module>()
----> 1 f(4)
TypeError: f() takes 0 positional arguments but 1 was given
如果您想允许某些位置参数并且传递了可选的关键字args但仅通过名称,那么它也很有用:
def f(a,b, *, foo=None, bar=None):
return a, b, foo, bar
然后传递3个位置参数将会出错:
In [8]: f(1,2,3)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-8-b61741968103> in <module>()
----> 1 f(1,2,3)
TypeError: f() takes 2 positional arguments but 3 were given
答案 1 :(得分:5)
我最近收到了关于这个答案的评论 →请注意它的目标是python2,因为OP用python2.7明确地标记了他的问题。对于python3,请参阅Padraic Cunningham的回答。
[原始回答]
您不能单独使用*
。在函数声明中,它表示“解压缩此变量中的任何其他未命名参数”,因此您必须为其指定变量名称。
你可以通过给它一个名字来实现你想要的东西,然后检查它是否为空,如下所示:
class Reporter(object):
def __init__(self, *args):
assert not args, "Reporter.__ini__ only accepts named arguments"
然后你想要添加你可以允许的参数,如下所示:
# won't work
def __init__(self, *args, testline=None, sw_ver= None, directory=None):
...除了* args需要在最后。但是如果你把它们放在最后,你会发现你仍然可以先传递未命名的其他参数。
你必须扭转逻辑,只采取kwargs。
class Reporter(object):
def __init__(self, **kwargs):
testline = kwargs.pop('testline', None)
sw_ver = kwargs.pop('sw_ver', None)
directory = kwargs.pop('directory', None)
assert not kwargs, 'Unknown arguments: %r' % kwargs
现在,如果有人试图提供未命名的辩论,他们将被拒绝。
答案 2 :(得分:1)
星号运算符(*
)用于unpacking。你不能用它作为论据。
您可能需要阅读Variable-Length Argument Tuples:
函数可以使用可变数量的参数。参数名称 以*开始将参数收集到元组中开头。例如, printall接受任意数量的参数并打印出来:
def printall(*args):
print args
gather参数可以有你喜欢的任何名字,但是args是 常规。以下是该功能的工作原理:
>>> printall(1, 2.0, '3') (1, 2.0, '3')
答案 3 :(得分:1)
这是我在正在研究的Python 2项目的切线上写的一些实用程序装饰器。引发的异常尽可能地反映了Python 3中使用仅关键字参数语法的函数所引发的异常。
它们不会禁止位置参数,但它们可以要求/限制关键字参数。你可以创建另一个不允许位置参数的装饰器。
import functools
def original_wrapped_function(f):
try:
while True:
f = f.__wrapped__
except AttributeError:
return f
def restrict_kwargs(*allowed_keywords):
def restrict_kwargs_decorator(func):
@functools.wraps(original_wrapped_function(func))
def restrict_wrapper(*args, **kwargs):
for keyword in kwargs:
if keyword not in allowed_keywords:
msg = "%s() got an unexpected keyword argument '%s'"
raise TypeError(msg % (func.__name__, keyword))
return func(*args, **kwargs)
restrict_wrapper.__wrapped__ = func
return restrict_wrapper
return restrict_kwargs_decorator
def require_kwargs(*required_keywords):
def require_kwargs_decorator(func):
@functools.wraps(original_wrapped_function(func))
def require_wrapper(*args, **kwargs):
missing_keywords = []
for keyword in required_keywords:
if keyword not in kwargs:
missing_keywords.append(keyword)
if missing_keywords:
func_name = func.__name__
count = len(missing_keywords)
if count == 1:
arg_word = 'argument'
missing_keywords_str = "'%s'" % missing_keywords[0]
else:
arg_word = 'arguments'
and_join_str = ' and ' if count == 2 else ', and '
missing_keywords_str = ', '.join(
("'%s'" % mk) for mk in missing_keywords[:-1])
missing_keywords_str = and_join_str.join((
missing_keywords_str, ("'%s'" % missing_keywords[-1])))
msg = "%s() missing %d required keyword-only %s: %s"
raise TypeError(msg % (func_name, count, arg_word,
missing_keywords_str))
return func(*args, **kwargs)
require_wrapper.__wrapped__ = func
return require_wrapper
return require_kwargs_decorator
def exact_kwargs(*exact_keywords):
def exact_kwargs_decorator(func):
@restrict_kwargs(*exact_keywords)
@require_kwargs(*exact_keywords)
@functools.wraps(original_wrapped_function(func))
def exact_wrapper(*args, **kwargs):
return func(*args, **kwargs)
exact_wrapper.__wrapped__ = func
return exact_wrapper
return exact_kwargs_decorator
一些例子:
>>> @restrict_kwargs('five', 'six')
... def test_restrict_kwargs(arg1, arg2, *moreargs, **kwargs):
... return (arg1, arg2, moreargs, kwargs)
...
>>>
>>> @require_kwargs('five', 'six')
... def test_require_kwargs(arg1, arg2, *moreargs, **kwargs):
... return (arg1, arg2, moreargs, kwargs)
...
>>>
>>> @exact_kwargs('five', 'six')
... def test_exact_kwargs(arg1, arg2, *moreargs, **kwargs):
... return (arg1, arg2, moreargs, kwargs)
...
>>>
>>>
>>>
>>> test_restrict_kwargs(1, 2, 3, 4, five=5)
(1, 2, (3, 4), {'five': 5})
>>>
>>> test_restrict_kwargs(1, 2, 3, 4, five=5, six=6)
(1, 2, (3, 4), {'six': 6, 'five': 5})
>>>
>>> test_restrict_kwargs(1, 2, 3, 4, five=5, six=6, seven=7)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "SO_31939890.py", line 19, in restrict_wrapper
raise TypeError(msg % (func.__name__, keyword))
TypeError: test_restrict_kwargs() got an unexpected keyword argument 'seven'
>>>
>>>
>>>
>>> test_require_kwargs(1, 2, 3, 4, five=5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "SO_31939890.py", line 49, in require_wrapper
missing_keywords_str))
TypeError: test_require_kwargs() missing 1 required keyword-only argument: 'six'
>>>
>>> test_require_kwargs(1, 2, 3, 4, five=5, six=6)
(1, 2, (3, 4), {'six': 6, 'five': 5})
>>>
>>> test_require_kwargs(1, 2, 3, 4, five=5, six=6, seven=7)
(1, 2, (3, 4), {'seven': 7, 'six': 6, 'five': 5})
>>>
>>>
>>>
>>> test_exact_kwargs(1, 2, 3, 4, five=5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "SO_31939890.py", line 20, in restrict_wrapper
return func(*args, **kwargs)
File "SO_31939890.py", line 49, in require_wrapper
missing_keywords_str))
TypeError: test_exact_kwargs() missing 1 required keyword-only argument: 'six'
>>>
>>> test_exact_kwargs(1, 2, 3, 4, five=5, six=6)
(1, 2, (3, 4), {'six': 6, 'five': 5})
>>>
>>> test_exact_kwargs(1, 2, 3, 4, five=5, six=6, seven=7)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "SO_31939890.py", line 19, in restrict_wrapper
raise TypeError(msg % (func.__name__, keyword))
TypeError: test_exact_kwargs() got an unexpected keyword argument 'seven'
>>>
答案 4 :(得分:0)
星号(*
)不是有效的Python 2.7标识符,它是一个运算符。我认为你在复制食谱中的代码时犯了一个错误。
然而,它是Python 3中的有效代码,如Padraic Cunningham answered。
答案 5 :(得分:0)
在python 2中。*:
参数名称派生为任何编程语言中的变量名称定律。但在python 2. * STAR 符号用于determining special situation的参数名称和单个 STAR 符号作为参数名称引发错误。
但是在python 3. *:
等于 STAR 的参数不能为此分配任何值,下一个位置参数不能按位置给出值,只能按参数名称值。