A Wand Makes Your Ideas Come True

0%

通过rustpython学习rust和Python系列之1--getattribute

先写个算是开篇语的东西吧。从去年起就开始关注rustpython这个项目,也一直想写一系列长篇文章来深入分析这个项目。这样一直拖了差不多一年了,总想着先把整个项目看完再书写一套有体系有章法的文章,但事实证明碎片的时间还是不足以让我把整个项目看完。因此,在今天决定,不管什么体系了,就按照随笔来写吧,看到啥写啥。正好之前因为追求高质量的文章,这个博客之前几个月才更新一次,所以这也算是一个尝试,当然,龟速更新的文章我也会继续写的。如果你看到这系列文章感觉还有点意思,可以把我的网站加收藏哈~ http://blog.ideawand.com 。我还有一个微信公众号叫做【极客幼稚园】,大家也可以关注一下,这样有什么更新大家也可以看到。

本系列随笔的第一篇,基本算是随机挑选的,我们来看看个python中__getattribute__这个魔术方法在rustpython中有关的实现吧。

本次阅读对应的rustpython commit: 1976740d3c0015514db8863584ed7a015c18521f。

PyAttributes类型的定义在RP中是用的Rust标准库的HashMap,代码注释中(pyobject.rs:64)的说明是这样可能会比用dict更快一些。而CPython里毕竟C语言自己没有hashmap,所以用的都是CPython自己实现的dict来做。

  • 但是毕竟逃不掉,还要和Python语言中的字典有些关系,所以在RustPython的Dict实现中,实现了与PyAttributes的互相转换(from_attributes @ dict.rs:377、to_attributes @ dict.rs:452)。

PyType实现SlotGetattro Trait,定义了getattro方法,流程如下:

  • PyType为解释器自己快速查找一个类型的属性开了后门,定义了get_attr方法。先调用这个方法
    • 该方法先尝试直接从自己的attributes属性(上面提到了,他没有有dict实现,而是直接使用了HashMap)里去按名字查找
      • 如果没有找到,再去自己的MRO里面找,也是遍历MRO,直接读它们的attributes属性

Python官方的《Descriptor HowTo Guide》中有如下的描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Instance lookup scans through a chain of namespaces giving data descriptors the highest priority, followed by instance variables, then non-data descriptors, then class variables, and lastly __getattr__() if it is provided.

......

The mechanism for descriptors is embedded in the __getattribute__() methods for object, type, and super().

The important points to remember are:

Descriptors are invoked by the __getattribute__() method.

Classes inherit this machinery from object, type, or super().

Overriding __getattribute__() prevents automatic descriptor calls because all the descriptor logic is in that method.

object.__getattribute__() and type.__getattribute__() make different calls to __get__(). The first includes the instance and may include the class. The second puts in None for the instance and always includes the class.

Data descriptors always override instance dictionaries.

Non-data descriptors may be overridden by instance dictionaries.

我暂时认为可以这样理解,Python默认为objecttypesuper()三种类型实现了默认的__getattribute__方法,默认方法里支持了描述器的行为。我们大多数自定义的结构都继承了这些行为,当然,你也可以自定义一个__getattribute__方法,这样描述器的机制在当前类就不一定有效了。不过由于在编写自定义的__getattribute__方法时,为了避免无限递归调用,不能使用self.xxx的语法,而是显示使用父类的__getattribute__方法,因此描述器的机制在父类中还会起作用。

__set__只在当前类下找,而__get__要按MRO顺序找,为什么呢,存疑,再看看文档

上文提到对于objecttypesuper()三种类型实现了默认的__getattribute__方法,所以在RP的代码中也可以在不同位置看到这些逻辑

  • generic_getattribute_opt @ vm.rs:1244 这个对应实现了CPython中的_PyObject_GenericGetAttrWithDict
  • getattro @ pytype.rs:539
  • 目前的感觉是,vm.rs中的逻辑是对普通对象的,而pytype.rs中的逻辑是对type的,对super的应该是通过某种方式最终使用了上面两个方法

未完待续。。。。

参考资料:

微信公众号:极客幼稚园
关注阅读更多优质技术文章