在Python中强制命名参数
在Python中,你可能有一个函数定义:
def info(object, spacing=10, collapse=1)
可以通过以下任何方式调用:
info(odbchelper) info(odbchelper, 12) info(odbchelper, collapse=0) info(spacing=15, object=odbchelper)
感谢Python允许任何顺序的参数,只要它们被命名。
我们遇到的问题是随着我们的一些更大的function的增长,人们可能会在spacing
和collapse
之间添加参数,这意味着错误的值可能会变成未命名的参数。 另外,有时候并不总是很清楚需要进入哪些内容。我们正在迫使人们指定某些参数 – 不仅仅是编码标准,而是理想的标志或者pydev插件。
所以在上面的4个例子中,只有最后一个会通过检查,因为所有的参数都被命名了。
几率是我们只会打开它的某些function,但任何build议如何实现这一点,或者如果甚至可能,将不胜感激。
在Python 3中 – 是的,你可以在参数列表中指定*
。
“*”或“* identifier”后面的参数是关键字参数,只能传递用过的关键字参数。
示例代码( 来自python ref ):
>>> 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)
您可以通过以下方式定义一个函数来强制用户在Python3中使用关键字参数。
def foo(*, arg0="default0", arg1="default1", arg2="default2"): pass
通过使第一个参数成为没有名字的位置参数,可以强制每个调用该函数的人使用关键字参数,这是我想你所问的。 在Python2中,唯一的方法是定义一个像这样的函数
def foo(**kwargs): pass
这将迫使调用者使用kwargs,但这不是一个很好的解决scheme,因为你必须把支票只接受你需要的参数。
诚然, 绝大多数编程语言使得函数调用合约的参数顺序成为一部分,但是这并不是必须的 。 为什么呢? 那么我对这个问题的理解是,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本身一样,这个参数约定也只是半强制的。
否则,调用将需要如下forms:
info(arg1, arg2, arg3, spacing=11, collapse=2)
一个电话
info(arg1, arg2, arg3, 11, 2)
会将值11分配给参数_p
,并由函数的第一条指令提升exception。
特点:
-
_p=__named_only_start
之前的参数在位置上(或按名称)被允许。 -
_p=__named_only_start
之后的参数必须仅由名称提供(除非获得并使用关于特殊哨兵对象__named_only_start
知识)。
优点:
- 参数在数量上和含义上是明确的(当然,如果好的名字也被select的话)。
- 如果哨兵被指定为第一个参数,那么所有的参数都需要用名字来指定。
- 调用函数时,可以通过在相应位置使用
__named_only_start
对象__named_only_start
来切换到位置模式。 - 可以预期比其他替代品更好的性能。
缺点:
-
检查发生在运行时,而不是编译时。 - 使用额外的参数(尽pipe不是参数)和额外的检查。 对常规function的性能下降很小。
- function是一种黑客,没有语言的直接支持(见下面的注释)。
- 调用该函数时,可以通过在正确位置使用
__named_only_start
对象__named_only_start
来切换到位置模式。 是的,这也可以看作是一个亲。
请记住,这个答案只适用于Python 2. Python 3实现了其他答案中描述的类似但非常优雅的语言支持机制。
我发现,当我打开思绪想一想,没有问题或其他的决定似乎是愚蠢的,愚蠢的,或只是愚蠢的。 恰恰相反:我通常学到很多东西。
你可以声明你的函数只接收**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.
更新:
我意识到使用**kwargs
不会解决问题。 如果你的程序员按照自己的意愿更改函数参数,例如,可以将函数改为:
def info(foo, **kwargs):
旧的代码会再次破坏(因为现在每个函数调用都必须包含第一个参数)。
这真的归结于Bryan所说的。
(…)人们可能会添加
spacing
和collapse
之间的参数(…)
一般来说,在改变职能时,新的观点应该总是走到最后。 否则它会破坏代码。 应该是明显的。
如果有人改变了这个function,这样代码就会中断,这个改变必须被拒绝。
(正如布莱恩所说,这就像一份合同)
(…)有时并不总是清楚需要进入什么。
通过查看函数的签名(即def info(object, spacing=10, collapse=1)
),应该立即看到每个没有默认值的参数都是强制的。
这个论点是什么 ,应该进入文档string。
旧的回答(保持完整) :
这可能不是一个好的解决scheme:
你可以这样定义函数:
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
是包含任何关键字参数的字典。 您可以检查是否存在强制性参数,如果不存在,则引发exception。
缺点是,它可能不是那么明显,哪些论点是可能的,但有了适当的文档,应该没问题。
你可以通过在Python 2和Python 3中工作的方式来做到这一点 ,通过使用默认值“自然”不会发生的“伪造”第一关键字参数。 该关键字参数可以前面有一个或多个没有值的参数:
_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)
如果您将该function更改为:
def info(_kw=_dummy, spacing=10, collapse=1):
那么所有的参数必须有关键字和info(odbchelper)
将不再工作。
这将允许您在_kw
之后的任何位置放置其他关键字参数,而不会强制您在最后一项之后放置它们。 这通常是有道理的,例如按照逻辑分组或按字母顺序排列关键字可以帮助维护和开发。
所以没有必要恢复使用def(**kwargs)
并在智能编辑器中丢失签名信息。 你的社会契约是提供某些信息,通过迫使(其中一些)要求关键字,这些呈现的顺序变得无关紧要。
你可以使用**
运算符:
def info(**kwargs):
这样人们被迫使用命名参数。
def cheeseshop(kind, *arguments, **keywords):
在Python中,如果使用*参数,这意味着你可以传递n参数为这个参数 – 这将是一个列表内函数访问
如果使用** kw表示它的关键字参数,可以作为字典访问 – 你可以传递n个kw参数,如果你想限制那个用户必须按顺序input序列和参数,那么不要使用*和** – (pythonic方式为大型架构提供通用解决scheme…)
如果你想用默认值限制你的function,那么你可以在里面检查
def info(object, spacing, collapse) spacing = spacing or 10 collapse = collapse or 1
正如其他答案所说,改变function签名是一个坏主意。 在结尾添加新参数,或者在插入参数的情况下修复每个调用者。
如果您仍想这样做,请使用函数装饰器和inspect.getargspec函数。 这将被用于这样的事情:
@require_named_args def info(object, spacing=10, collapse=1): ....
require_named_args
实现仅供读者参考。
我不会打扰。 每次调用函数时都会变得很慢,并且通过更仔细地编写代码可以获得更好的结果。
我不明白为什么程序员会首先在两个人之间添加一个参数。
如果你想让函数参数和名称一起使用(例如info(spacing=15, object=odbchelper)
),那么它们应该是以什么顺序定义的,所以你最好把新的参数放在最后。
如果你确实希望命令重要,那么如果你改变它,不能指望任何工作!