在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的增长,人们可能会在spacingcollapse之间添加参数,这意味着错误的值可能会变成未命名的参数。 另外,有时候并不总是很清楚需要进入哪些内容。我们正在迫使人们指定某些参数 – 不仅仅是编码标准,而是理想的标志或者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所说的。


(…)人们可能会添加spacingcollapse之间的参数(…)

一般来说,在改变职能时,新的观点应该总是走到最后。 否则它会破坏代码。 应该是明显的。
如果有人改变了这个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) ),那么它们应该是以什么顺序定义的,所以你最好把新的参数放在最后。

如果你确实希望命令重要,那么如果你改变它,不能指望任何工作!