本文介绍了__getattribute__方法和描述符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据有关python描述符的本指南 https://docs.python.org/howto/descriptor.html

according to this guide on python descriptorshttps://docs.python.org/howto/descriptor.html

新样式类中的方法对象是使用描述符实现的,以避免在属性查找中使用特殊的大小写.

method objects in new style classes are implemented using descriptors in order to avoid special casing them in attribute lookup.

我的理解方式是,有一个方法对象类型实现 __ get __ 并在实例调用时返回绑定的方法对象,而在没有实例且仅调用一个实例时返回未绑定的方法对象班级.文章还指出,此逻辑是在 object .__ getattribute __ 方法中实现的.像这样:

the way I understand this is that there is a method object type that implements __get__ and returns a bound method object when called with an instance and an unbound method object when called with no instance and only a class. the article also states that this logic is implemented in the object.__getattribute__ method. like so:

def __getattribute__(self, key):
    "Emulate type_getattro() in Objects/typeobject.c"
    v = object.__getattribute__(self, key)
    if hasattr(v, '__get__'):
       return v.__get__(None, self)
    return v

但是 object .__ getattribute __ 本身就是一种方法!那么它如何绑定到对象(无穷递归)?如果在属性查找中使用了特殊情况,这是否不会破坏删除旧样式特殊大小写的目的?

however object.__getattribute__ is itself a method! so how is it bound to an object (without infinite recursion)? if it is special cased in the attribute lookup does that not defeat the purpose of removing the old style special casing?

推荐答案

实际上,在CPython中,默认的 __ getattribute __ 实现不是 Python 方法,而是在C.它可以直接访问对象插槽(代表Python对象的C结构中的条目),而无需费心通过讨厌的属性访问例程.

Actually, in CPython the default __getattribute__ implementation is not a Python method, but is instead implemented in C. It can access object slots (entries in the C structure representing Python objects) directly, without bothering to go through the pesky attribute access routine.

仅仅因为您的 Python 代码必须执行此操作,并不意味着C代码必须这样做.:-)

Just because your Python code has to do this, doesn't mean the C code has to. :-)

如果您确实实现了Python __ getattribute __ 方法,则只需使用 object .__ getattribute __(self,attrname),或者最好使用 super().__ getattribute __(attrname)来访问 self 上的属性.这样一来,您也不会遇到递归.

If you do implement a Python __getattribute__ method, just use object.__getattribute__(self, attrname), or better still, super().__getattribute__(attrname) to access attributes on self. That way you won't hit recursion either.

在CPython实现中,属性访问实际上由 C类型对象中的 tp_getattro 插槽,后退到 tp_getattr 插槽.

In the CPython implementation, the attribute access is actually handled by the tp_getattro slot in the C type object, with a fallback to the tp_getattr slot.

当您在实例上使用属性访问时,为详尽无遗并充分展示C代码的作用,以下是称为的完整函数集:

To be exhaustive and to fully expose what the C code does, when you use attribute access on an instance, here is the full set of functions called:

  • Python translates attribute access to a call to the PyObject_GetAttr() C function. The implementation for that function looks up the tp_getattro or tp_getattr slot for your class.

object 类型具有 PyObject_GenericGetAttr 函数,它将调用委派给 _PyObject_GenericGetAttrWithDict (将 * dict 指针设置为 NULL 并将 suppress 参数设置为 0 ).此函数是您的 object .__ getattribute __ 方法(特殊表在名称和广告位之间映射.

The object type has filled the tp_getattro slot with the PyObject_GenericGetAttr function, which delegates the call to _PyObject_GenericGetAttrWithDict (with the *dict pointer set to NULL and the suppress argument set to 0). This function is your object.__getattribute__ method (a special table maps between the name and the slots).

_PyObject_GenericGetAttrWithDict 函数可以通过 tp_dict 插槽,但对于描述符(包括方法), _PyType_Lookup 函数已使用.

This _PyObject_GenericGetAttrWithDict function can access the instance __dict__ object through the tp_dict slot, but for descriptors (including methods), the _PyType_Lookup function is used.

_PyType_Lookup 处理缓存并将其委托给 find_name_in_mro ;后者在类(和超类)上查找属性.该代码使用指向MRO中每个类上的 tp_dict 插槽的直接指针来引用类属性.

_PyType_Lookup handles caching and delegates to find_name_in_mro on cache misses; the latter looks up attributes on the class (and superclasses). The code uses direct pointers to the tp_dict slot on each class in the MRO to reference class attributes.

如果通过 _PyType_Lookup 找到了描述符,则将其返回到 _PyObject_GenericGetAttrWithDict ,并在该对象上调用 tp_descr_get 函数( __ get __ 挂钩).

If a descriptor is found by _PyType_Lookup it is returned to _PyObject_GenericGetAttrWithDict and it calls the tp_descr_get function on that object (the __get__ hook).

当您访问类本身上的属性时,而不是 _PyObject_GenericGetAttrWithDict 而不是 type-&t; tp_getattro 插槽将由 type_getattro()函数,它也考虑了元类.此版本也调用 __ get __ ,但将实例参数设置为 None .

When you access an attribute on the class itself, instead of _PyObject_GenericGetAttrWithDict, the type->tp_getattro slot is instead serviced by the type_getattro() function, which takes metaclasses into account too. This version calls __get__ too, but leaves the instance parameter set to None.

此代码无需在任何地方递归调用 __ getattribute __ 即可访问 __ dict __ 属性,因为它可以直接直接进入C结构.

Nowhere does this code have to recursively call __getattribute__ to access the __dict__ attribute, as it can simply reach into the C structures directly.

这篇关于__getattribute__方法和描述符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 04:15