Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 363 – 动态属性访问的语法

作者:
Ben North <ben at redfrontdoor.org>
状态:
已拒绝
类型:
标准追踪
创建:
2007-01-29
发布历史:
2007-02-12

目录

警告

此 PEP 已被拒绝。

×

更多信息请参见 邮件列表讨论

摘要

目前可以通过“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