PEP 363 – 动态属性访问的语法
- 作者:
- Ben North <ben at redfrontdoor.org>
- 状态:
- 已拒绝
- 类型:
- 标准追踪
- 创建:
- 2007-01-29
- 发布历史:
- 2007-02-12
摘要
目前可以通过“getattr”和“setattr”内置函数实现动态属性访问。本 PEP 建议一种新的语法来简化此类访问,例如允许程序员编写
x.('foo_%d' % n) += 1
z = y.('foo_%d' % n).('bar_%s' % s)
而不是
attr_name = 'foo_%d' % n
setattr(x, attr_name, getattr(x, attr_name) + 1)
z = getattr(getattr(y, 'foo_%d' % n), 'bar_%s' % s)
基本原理
字典访问和索引都有一个友好的调用语法:与 x.__getitem__(12)
相比,程序员可以编写 x[12]
。这也允许在增量赋值中使用带下标的元素,例如“x[12] += 1”。本提案将此易用性扩展到动态属性访问。
属性访问目前可以通过两种方式实现
- 当属性名称在代码编写时已知时,可以使用“.”NAME 后缀,例如
x.foo = 42 y.bar += 100
- 当属性名称在运行时动态计算时,必须使用“getattr”和“setattr”内置函数
x = getattr(y, 'foo_%d' % n) setattr(z, 'bar_%s' % s, 99)
“getattr”内置函数还允许程序员指定一个默认值,如果对象没有给定名称的属性,则返回该默认值
x = getattr(y, 'foo_%d' % n, 0)
本 PEP 描述了一种新的动态属性访问语法——“x.(expr)”——上面的摘要中给出了示例。
(新语法也可以允许在“get”情况下提供默认值,例如
x = y.('foo_%d' % n, None)
这种 2 个参数形式的动态属性访问将不允许作为(增量或普通)赋值的目标。“讨论”部分包括针对 2 个参数扩展的具体意见。)
最后,新语法可以与“del”语句一起使用,例如
del x.(attr_name)
对现有代码的影响
所提出的新语法目前无效,因此本提案不会改变任何现有的格式良好的程序的含义。
在 2.5 版本发行版中,所有“*.py”文件大约有 600 个“getattr”、“setattr”或“delattr”的使用。它们细分为以下几种(数字存在一定的误差,因为它们是通过部分手动检查得出的)
c.300 uses of plain "getattr(x, attr_name)", which could be
replaced with the new syntax;
c.150 uses of the 3-argument form, i.e., with the default
value; these could be replaced with the 2-argument form
of the new syntax (the cases break down into c.125 cases
where the attribute name is a literal string, and c.25
where it's only known at run-time);
c.5 uses of the 2-argument form with a literal string
attribute name, which I think could be replaced with the
standard "x.attribute" syntax;
c.120 uses of setattr, of which 15 use getattr to find the
new value; all could be replaced with the new syntax,
the 15 where getattr is also involved would show a
particular increase in clarity;
c.5 uses which would have to stay as "getattr" because they
are calls of a variable named "getattr" whose default
value is the builtin "getattr";
c.5 uses of the 2-argument form, inside a try/except block
which catches AttributeError and uses a default value
instead; these could use 2-argument form of the new
syntax;
c.10 uses of "delattr", which could use the new syntax.
例如,行
setattr(self, attr, change_root(self.root, getattr(self, attr)))
来自 Lib/distutils/command/install.py 可以改写为
self.(attr) = change_root(self.root, self.(attr))
以及行
setattr(self, method_name, getattr(self.metadata, method_name))
来自 Lib/distutils/dist.py 可以改写为
self.(method_name) = self.metadata.(method_name)
性能影响
最初的 pystone 测量结果尚无定论,但表明打补丁后的版本在 pystones 分数中可能存在约 1% 的性能下降。一种说法是,ceval.c 中的较长主循环会影响缓存行为,但尚未得到证实。
另一方面,测量结果表明动态属性访问速度提升了约 40-45%。
错误情况
只允许字符串作为属性名称,因此例如会产生以下错误
>>> x.(99) = 8
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: attribute name must be string, not 'int'
这由现有的 PyObject_GetAttr
函数处理。
草案实现
草案实现是在 Grammar/Grammar 中为“trailer”子句添加了一种新的替代方案;在 Python.asdl 中添加了一种新的 AST 类型“DynamicAttribute”,并对 symtable.c、ast.c 和 compile.c 进行了相应的更改,以及三个新的操作码(load/store/del),并对 opcode.h 和 ceval.c 进行了相应的更改。该补丁在核心代码中新增约 180 行,在测试中新增约 100 行。它作为 sourceforge 补丁 #1657573 [1] 可用。
邮件列表讨论
此 PEP 的草案形式在 2007 年 2 月 9 日发布到 python-ideas [2],并得到了普遍的积极反响。该 PEP 随后在 2007 年 2 月 12 日发布到 python-dev [3],并引发了有趣的讨论。简要总结一下
最初,人们对这个想法有合理的(但并非一致的)支持,尽管对具体语法的选择意见不一。一些人认为“.”很容易被忽略,导致语法可能与方法/函数调用混淆。一些人建议使用其他语法
obj.(foo)
obj.[foo]
obj.{foo}
obj{foo}
obj.*foo
obj->foo
obj<-foo
obj@[foo]
obj.[[foo]]
其中,“obj.[foo]”脱颖而出成为首选。在最初的讨论中,普遍不赞成使用两个参数的形式,因此将其从 PEP 中删除。
讨论随后回归到一个问题,即此特定功能是否提供了足够的益处来证明引入新语法。除了要求程序员熟悉新语法之外,还会出现向后兼容性问题——使用新语法的代码无法在旧版本的 Python 上运行。
Martin von Löwis 提出了一个新的“包装类”来替代新语法,并建议了以下规范/概念实现
class attrs:
def __init__(self, obj):
self.obj = obj
def __getitem__(self, name):
return getattr(self.obj, name)
def __setitem__(self, name, value):
return setattr(self.obj, name, value)
def __delitem__(self, name):
return delattr(self, name)
def __contains__(self, name):
return hasattr(self, name)
这被认为是解决原始问题的更简洁、更优雅的方案。(另一个建议是使用一个 mixin 类来提供对对象属性的字典式访问。)
最终决定,本 PEP 并没有达到引入新语法的证明责任,而这种观点从讨论一开始就有一些人提出。包装类理念作为未来 PEP 的可能性保留了下来。
参考文献
版权
本文件已置于公有领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0363.rst
最后修改时间:2024-04-14 20:08:31 GMT