是否安全使用函数接受不是标识符的kwargs关键字参数?

时间:2013-06-06 07:40:31

标签: python python-3.x identifier python-2.x keyword-argument

在Python中,为函数提供 Python标识符的关键字参数是否安全?这是一个例子:

>>> '{x-y}'.format(**{'x-y': 3})  # The keyword argument is *not* a valid Python identifier
'3'
>>> '{x-y}'.format(x-y=3)
  File "<ipython-input-12-722afdf7cfa3>", line 1
SyntaxError: keyword can't be an expression

我问这个是因为使用包含破折号的名称格式化更方便(因为这些值对应于名称中带有破折号的命令行参数)。但这种行为是否可靠(即它可以在Python版本之间变化)?

我不确定是否正式支持使用非标识符作为关键字参数:事实上,documentation读取:

  

如果语法**表达式出现在函数调用表达式中   必须评估映射,其内容被视为   其他关键字参数。

...其中“关键字参数”被定义为具有标识符的名称:

  

keyword_arguments :: = keyword_item(“,”keyword_item)*

     

keyword_item :: = identifier“=”表达式

identifiers限制了他们可以使用的字符(例如-是禁止的):

  

identifier :: =(letter |“_”)(字母|数字|“_”)*

因此,文档指出函数调用中给**的映射只应包含有效的标识符作为键,但CPython 2.7接受更多通用键(对于format()和函数带有{{ 1}}参数,不将值放在变量中)。这是一个可靠的功能吗?

1 个答案:

答案 0 :(得分:5)

首先:具有非标识符名称的**{...}调用约定仅在被调用函数具有**kw参数的情况下才有效,因为它也无法定义无效的显式关键字参数标识符

我会说keyword_arguments语法只适用于源代码的解析器,并且不能被视为对{{1}的内容的功能限制结果。下面的功能描述不会明确限制字典的键,function definition documentation也不会。

相反,由于语法允许**expression,并且功能规范声明应解析为映射,其内容被视为附加关键字参数,因此很明显(对我来说,除了适用于Python词典的正常键(键必须是不可变的)之外,没有任何限制。您可以传入所有Python关注的元组或数字键。功能规范说明了如何处理内容,而不是内容必须符合某种格式。

因此,在我看来,功能规范必须显式限制expression字典中的键以禁止你正在做的事情,因为语法肯定不会。改变这将是一个巨大的向后不兼容的变化,并且不太可能被添加。

请注意,尽管规范没有提及对字典键的任何限制,但CPython确实:

**expression

这是python解释器在调用代码对象(用户定义的函数)时所做的限制。在引发上述异常的部分之后的the source code中解释了原因:

>>> def f(*args, **kw): print args, kw
... 
>>> f(**{1: 2})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() keywords must be strings

通过将关键字限制为字符串,可以实现速度优化。