2010-06-03 54 views
42

在Python中,你可以有一个函数的定义:强制的参数命名Python中

def info(object, spacing=10, collapse=1) 

这可能在以下任一方式被称为:

info(odbchelper)      
info(odbchelper, 12)     
info(odbchelper, collapse=0)   
info(spacing=15, object=odbchelper) 

感谢Python的允许任何─命令参数,只要它们被命名。

我们遇到的问题是随着我们一些较大的功能增加,人们可能会在spacingcollapse之间添加参数,这意味着错误的值可能会转到未命名的参数。此外,有时候并不总是清楚需要进入哪些内容。我们正在迫使人们指定某些参数 - 不仅仅是编码标准,而是理想的标志或pydev插件?

这样在上面的4个例子中,只有最后一个会通过检查,因为所有参数都被命名。

我们只会针对某些功能打开它,但对于如何实现此功能有任何建议 - 或者如果甚至有可能,我们将不胜感激。

回答

94

在Python 3中 - 是的,你可以在参数列表中指定*

“*”或“* identifier”后面的参数仅为关键字参数, 只能传递使用的关键字参数。

示例代码(from 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) 
+4

对于寻找他的引用来源的人来说,它在Python 3文档中:https://docs.python.org/3.5/reference/compound_stmts.html#function-definitions – phoenix 2015-08-10 21:30:38

+0

使用** kwargs:'def foo( pos,*,forcenamed,** kwargs)'。 – Jerther 2017-11-27 19:44:56

0

您可以使用**操作:

def info(**kwargs): 

这样人们被迫使用命名参数。

+0

并且不知道如何在不读取代码的情况下调用您的方法,增加您的消费者的认知负载:( – Dagrooms 2017-12-19 22:39:02

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. 
+0

)您不仅需要添加关键字检查,还要考虑知道他们必须调用方法的消费者'foo(** kwargs)'''''''''''''foo(killme = True,when =“rightnowplease”)' – Dagrooms 2017-12-19 22:40:49

+0

这要看真的,考虑'dict'。 – 2017-12-20 04:43:14

2

更新:

我意识到,使用**kwargs不会解决问题。如果你的程序员修改函数的参数,因为他们希望,人们可以,例如,函数改成这样:

def info(foo, **kwargs): 

和旧代码将再次突破(因为现在每个函数调用必须包括的第一个参数)。

这真的归结为布赖恩所说的。


(......)人们可能spacingcollapse(...)

一般来说之间添加参数,转变职能的时候,新的论据应该经常去年底。否则它会破坏代码。应该很明显。
如果有人更改了该函数以使代码中断,则必须拒绝此更改。
(正如布莱恩说,它就像一个合同)

(...)有时它并不总是清楚哪些需要去。

通过查看函数的签名(即def info(object, spacing=10, collapse=1))应该立即看到每一个具有默认值,参数是强制性的。
什么的论点是,应该进入文档字符串。


老答案(保持完整性)

这可能不是一个很好的解决方案:

您可以定义函数是这样的:

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是一个包含任何关键字参数的字典。您可以检查是否存在强制参数,如果不存在,则引发异常。

缺点是,它可能不再那么明显,哪些参数是可能的,但有了合适的文档字符串,它应该没问题。

+3

我更喜欢你的旧答案。只是为了说明为什么你只接受函数中的kwargs,毕竟任何人都可以在源代码中改变任何东西 - 你需要文档来描述你的决定背后的意图和目的。 – Brandon 2010-06-03 11:57:27

0
def cheeseshop(kind, *arguments, **keywords): 

在Python如果使用* ARGS这意味着你可以通过对这个参数的参数正数 - 这将进来的功能列表访问

,如果使用* * kw表示其关键字参数,可以作为字典访问 - 您可以传递n个kw参数,并且如果要限制该用户必须按顺序输入序列和参数,则不要使用*和** - (它为python提供大型架构的通用解决方案...)

如果要限制使用默认值的功能,那么你可以检查里面

def info(object, spacing, collapse) 
    spacing = spacing or 10 
    collapse = collapse or 1 
0

至于其他的答案说,改变函数签名是一个坏主意。在结尾添加新参数,或者在插入参数时修复每个调用者。

如果您仍想这样做,请使用function decoratorinspect.getargspec函数。它会被用来这样的:

@require_named_args 
def info(object, spacing=10, collapse=1): 
    .... 

require_named_args的实施是作为读者的练习。

我不打扰。每次调用函数时它都会很慢,并且您会更仔细地编写代码以获得更好的结果。

-2

我不明白为什么程序员会首先在两个人之间添加一个参数。

如果您希望将函数参数与名称一起使用(例如info(spacing=15, object=odbchelper)),那么它们应该以什么顺序定义,因此您最好还是将新参数放在最后。

如果您确实希望订单重要,那么如果您更改它,不能指望任何工作!

+0

这并不回答这个问题。不管它是不是一个好主意都是无关紧要的 - 有人可能会这样做 – 2015-05-26 18:53:19

+0

正如格雷姆提到的那样,有人会这样做,而且,如果你正在编写一个供其他人使用的库,强制(仅用于python 3),那么当你需要重构API时,只传递关键字参数会带来额外的灵活性。 – s0undt3ch 2016-12-04 01:18:13

14

通过以下方式定义函数,可以强制用户在Python3中使用关键字参数。

def foo(*, arg0="default0", arg1="default1", arg2="default2"): 
    pass 

通过将第一个参数一个位置参数没有名字你强迫大家谁调用使用关键字参数的函数,它是什么,我认为你是问。在Python2做到这一点的唯一方法是这样定义

def foo(**kwargs): 
    pass 

一个函数,会迫使呼叫者使用kwargs但是这并没有很大的一个解决方案为您要那么必须把检查只接受你需要的参数。

7

真,大多数编程语言使函数调用合同参数顺序的一部分,但这并不需要如此。为什么呢?那么我对这个问题的理解是,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) 

的唯一途径呼叫者将能够提供论据spacingcollapse位置上(无例外)将是:

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的知识)。

优点:

  • 参数在数量和意义(后来的,如果好名字也选择了,当然)明确。
  • 如果将sentinel指定为第一个参数,则所有参数都需要通过名称指定。
  • 调用该功能时,可以通过在相应的位置使用标记对象__named_only_start来切换到位置模式。
  • 可以预期比其他替代方案更好的性能。

缺点:

  • 检查在运行时发生,而不是编译时。
  • 使用额外的参数(尽管不是参数)和额外的检查。对常规功能的性能下降很小。
  • 功能性是没有语言直接支持的黑客(见下面的注释)。
  • 调用该功能时,可以通过在正确位置使用哨兵对象__named_only_start来切换到位置模式。是的,这也可以看作是一个专业人士。

请记住,这个答案只适用于Python 2. Python 3实现了其他答案中描述的类似但非常优雅的语言支持机制。

我发现,当我打开思绪想一想,没有问题或其他的决定似乎愚蠢,愚蠢或愚蠢。恰恰相反:我通常学到很多东西。

+0

_“检查发生在运行时,而不是编译时。” - 我认为所有函数参数检查都是如此。在你实际执行函数调用的行之前,你并不总是知道正在执行哪个函数。另外,** + 1 ** - 这很聪明。 – Eric 2013-07-23 18:24:30

+0

@Eric:这只是我首选静态检查。但你是对的:那根本不是Python。虽然不是决定性的一点,但Python 3的“*”构造也是动态检查的。谢谢你的评论。 – 2013-07-27 05:42:31

+0

另外,如果您命名模块变量'_named_only_start',则无法从外部模块中引用该模块,该外部模块会提取pro和con。 (模块范围内的单引号下划线是私人的,IIRC) – Eric 2013-07-27 23:54:47

2

通过使用默认值“bogus”第一个关键字参数“自然”不会出现的默认值,您可以以的方式在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)     

如果更改的功能:

def info(_kw=_dummy, spacing=10, collapse=1): 

,那么所有参数必须有关键字,info(odbchelper)将不再起作用。

这将允许您在_kw之后的任何位置放置其他关键字参数,而不会强制您在最后一次输入之后放置它们。这通常是有意义的,例如按逻辑分组或按字母顺序排列关键字可以帮助维护和开发。

所以没有必要恢复使用def(**kwargs)并丢失智能编辑器中的签名信息。你的社会契约是提供某些信息,通过迫使(其中一些)要求关键字,这些信息的呈现顺序变得无关紧要。