6

在 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 版本之间有所不同)吗?

我不确定官方是否支持使用非标识符作为关键字参数:事实上,文档中写道:

如果语法 **expression 出现在函数调用中,则 expression 必须计算为映射,其内容被视为附加关键字参数。

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

关键字参数 ::= 关键字项 ("," 关键字项)*

关键字项目 ::= 标识符“=”表达式

其中标识符被限制在他们可以使用的字符中(-例如被禁止):

标识符 ::= (字母|"_") (字母 | 数字 | "_")*

因此,文档表明**在函数调用中给出的映射应该只包含有效的标识符作为键,但 CPython 2.7 接受更通用的键(forformat()和带**参数的函数,它们不会将值放入变量中)。这是一个可靠的功能吗?

4

1 回答 1

5

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

我会说keyword_arguments语法仅适用于源代码的解析器,并且不能被视为对**expression结果内容的功能限制。下面的功能描述并没有明确限制字典的键,函数定义文档也没有。

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

因此,在我看来,功能规范必须明确限制**expression字典中的键以禁止您正在执行的操作,因为语法肯定不会。更改这将是一个巨大的向后不兼容的更改,并且不太可能被添加。

请注意,即使规范没有提到对字典键的任何限制,CPython 也会:

>>> 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

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

/* Speed hack: do raw pointer compares. As names are
   normally interned this should almost always hit. */

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

于 2013-06-06T08:23:07.317 回答