在Python中,您可能有一个函数定义:
def info(object, spacing=10, collapse=1)
可以通过以下任何一种方式调用:
info(odbchelper)
info(odbchelper, 12)
info(odbchelper, collapse=0)
info(spacing=15, object=odbchelper)
感谢Python允许任意顺序参数,只要它们被命名。
我们遇到的问题是,随着我们的一些较大功能的增长,人们可能会在spacing
和collapse
之间添加参数,这意味着错误的值可能会转到非{命名。此外,有时候并不总是清楚需要进行什么。我们正在采用一种方法强迫人们命名某些参数 - 不仅仅是编码标准,而且理想情况下是标志或pydev插件?
所以在上面的4个例子中,只有最后一个会通过检查,因为所有参数都被命名。
我们只会针对某些功能启用它,但是有关如何实现这一点的任何建议 - 或者如果可能的话,我们将不胜感激。
答案 0 :(得分:153)
在Python 3中 - 是的,您可以在参数列表中指定*
。
来自docs:
“*”或“* identifier”之后的参数是仅限关键字的参数和 可能只传递使用过的关键字参数。
>>> def foo(pos, *, forcenamed):
... print(pos, forcenamed)
...
>>> foo(pos=10, forcenamed=20)
10 20
>>> foo(10, forcenamed=20)
10 20
>>> foo(10, 20)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 1 positional argument (2 given)
这也可以与**kwargs
:
def foo(pos, *, forcenamed, **kwargs):
答案 1 :(得分:24)
您可以通过以下方式定义函数来强制人们在Python3中使用关键字参数。
def foo(*, arg0="default0", arg1="default1", arg2="default2"):
pass
通过使第一个参数成为一个没有名称的位置参数,你强制所有调用该函数的人都使用关键字参数,这是我认为你所问的问题。在Python2中,唯一的方法是定义一个像这样的函数
def foo(**kwargs):
pass
这会强制调用者使用kwargs,但这不是一个很好的解决方案,因为你必须检查只接受你需要的参数。
答案 2 :(得分:9)
是的,大多数编程语言使参数顺序成为函数调用契约的一部分,但这不是需要。为什么会这样?我对这个问题的理解是,如果Python在这方面与其他编程语言有任何不同。除了Python 2的其他好答案外,请考虑以下内容:
__named_only_start = object()
def info(param1,param2,param3,_p=__named_only_start,spacing=10,collapse=1):
if _p is not __named_only_start:
raise TypeError("info() takes at most 3 positional arguments")
return str(param1+param2+param3) +"-"+ str(spacing) +"-"+ str(collapse)
调用者能够在位置(无异常)提供参数spacing
和collapse
的唯一方法是:
info(arg1, arg2, arg3, module.__named_only_start, 11, 2)
不使用属于其他模块的私有元素的惯例已经在Python中非常基础。与Python本身一样,这种参数约定只能半强制执行。
否则,呼叫需要采用以下形式:
info(arg1, arg2, arg3, spacing=11, collapse=2)
致电
info(arg1, arg2, arg3, 11, 2)
会将值11赋给参数_p
,并且函数的第一条指令会增加异常。
特性:
_p=__named_only_start
之前的参数在位置(或按名称)被接纳。_p=__named_only_start
之后的参数必须仅按名称提供(除非获取并使用了有关特殊标记对象__named_only_start
的知识)。优点:
__named_only_start
切换到位置模式。缺点:
__named_only_start
切换到位置模式。是的,这也可以看作是专业人士。请记住,这个答案仅对Python 2有效.Python 3实现了其他答案中描述的类似但非常优雅的语言支持机制。
我发现当我打开思绪并思考它时,没有任何问题或其他决定似乎是愚蠢,愚蠢或者只是愚蠢。恰恰相反:我通常会学到很多东西。
答案 3 :(得分:7)
你可以用在Python 2和Python 3中工作,通过制作一个&#34;伪造的&#34;第一个关键字参数,默认值不会发生&#34;自然&#34;。该关键字参数可以在一个或多个没有值的参数之前:
_dummy = object()
def info(object, _kw=_dummy, spacing=10, collapse=1):
if _kw is not _dummy:
raise TypeError("info() takes 1 positional argument but at least 2 were given")
这将允许:
info(odbchelper)
info(odbchelper, collapse=0)
info(spacing=15, object=odbchelper)
但不是:
info(odbchelper, 12)
如果您将功能更改为:
def info(_kw=_dummy, spacing=10, collapse=1):
然后所有参数都必须包含关键字,而info(odbchelper)
将不再有效。
这将允许您在_kw
之后的任何位置放置其他关键字参数,而不必强迫您在最后一个条目之后放置它们。这通常是有意义的,例如按逻辑分组或按字母顺序排列关键字有助于维护和开发。
因此无需恢复使用def(**kwargs)
并丢失智能编辑器中的签名信息。您的社交合同是通过强制(其中一些)要求关键字来提供某些信息,这些信息的提供顺序已经变得无关紧要。
答案 4 :(得分:2)
<强>更新强>
我意识到使用**kwargs
无法解决问题。如果程序员按照自己的意愿更改函数参数,可以将函数更改为:
def info(foo, **kwargs):
并且旧代码会再次中断(因为现在每个函数调用都必须包含第一个参数)。
这真的归结为布莱恩所说的。
(...)人们可能会在
spacing
和collapse
之间添加参数(...)
通常,在更改函数时,新参数应始终结束。否则会破坏代码。应该是显而易见的 如果有人更改了该功能以使代码中断,则必须拒绝此更改 (正如布莱恩所说,这就像一份合同)
(...)有时候并不总是清楚需要了解什么。
通过查看函数的签名(即def info(object, spacing=10, collapse=1)
),应立即看到每个不默认值的参数都是必需的。
该参数的 应该进入docstring。
旧答案(保持完整性):
<击> 这可能不是一个好的解决方案:
您可以这样定义功能:
def info(**kwargs):
''' Some docstring here describing possible and mandatory arguments. '''
spacing = kwargs.get('spacing', 15)
obj = kwargs.get('object', None)
if not obj:
raise ValueError('object is needed')
kwargs
是一个包含任何关键字参数的字典。您可以检查是否存在强制参数,如果不存在,则引发异常。
缺点是,它可能不再那么明显,哪些参数是可能的,但是如果有正确的文档字符串,它应该没问题。
击><击> 撞击>
答案 5 :(得分:1)
您可以将您的功能声明为仅接收**args
。这将强制关键字参数,但你需要做一些额外的工作,以确保只传入有效的名称。
def foo(**args):
print args
foo(1,2) # Raises TypeError: foo() takes exactly 0 arguments (2 given)
foo(hello = 1, goodbye = 2) # Works fine.
答案 6 :(得分:1)
可以使用*
在python2.x中模拟仅python3关键字参数(**kwargs
考虑以下python3代码:
def f(pos_arg, *, no_default, has_default='default'):
print(pos_arg, no_default, has_default)
及其行为:
>>> f(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() takes 1 positional argument but 3 were given
>>> f(1, no_default='hi')
1 hi default
>>> f(1, no_default='hi', has_default='hello')
1 hi hello
>>> f(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() missing 1 required keyword-only argument: 'no_default'
>>> f(1, no_default=1, wat='wat')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() got an unexpected keyword argument 'wat'
这可以使用以下方法模拟,请注意,在“必填命名实参”的情况下,我可以自由地将TypeError
切换为KeyError
,这样做不会做太多工作同样的异常类型
def f(pos_arg, **kwargs):
no_default = kwargs.pop('no_default')
has_default = kwargs.pop('has_default', 'default')
if kwargs:
raise TypeError('unexpected keyword argument(s) {}'.format(', '.join(sorted(kwargs))))
print(pos_arg, no_default, has_default)
行为:
>>> f(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() takes exactly 1 argument (3 given)
>>> f(1, no_default='hi')
(1, 'hi', 'default')
>>> f(1, no_default='hi', has_default='hello')
(1, 'hi', 'hello')
>>> f(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
KeyError: 'no_default'
>>> f(1, no_default=1, wat='wat')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in f
TypeError: unexpected keyword argument(s) wat
该食谱在python3.x中同样有效,但是如果您仅使用python3.x,则应避免使用该食谱
答案 7 :(得分:0)
您可以使用**
运算符:
def info(**kwargs):
这样人们就不得不使用命名参数。
答案 8 :(得分:0)
def cheeseshop(kind, *arguments, **keywords):
在python中如果使用* args意味着你可以为这个参数传递n个参数 - 这将是一个列表内的函数来访问
如果使用** kw表示其关键字参数,可以作为dict访问 - 您可以传递n个数量的kw args,如果要限制该用户必须按顺序输入序列和参数,那么请使用*和** - (它的pythonic方式为大型架构提供通用解决方案......)
如果你想用默认值限制你的功能,那么你可以在里面检查
def info(object, spacing, collapse)
spacing = spacing or 10
collapse = collapse or 1
答案 9 :(得分:0)
正如其他答案所说,改变功能签名是一个坏主意。可以在末尾添加新参数,也可以在插入参数时修复每个调用者。
如果您仍想这样做,请使用function decorator和inspect.getargspec功能。它会用到这样的东西:
@require_named_args
def info(object, spacing=10, collapse=1):
....
require_named_args
的实施留给读者练习。
我不会打扰。每次调用函数时都会很慢,并且通过更仔细地编写代码可以获得更好的结果。
答案 10 :(得分:-2)
我不明白为什么程序员首先会在两个其他人之间添加一个参数。
如果您希望函数参数与名称一起使用(例如info(spacing=15, object=odbchelper)
),那么定义它们的顺序无关紧要,因此您最好将新参数放在最后。
如果您确实希望订单变得重要,那么如果您更改订单,则无法期待任何工作!