惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
博客园 - 司徒正美
I
InfoQ
宝玉的分享
宝玉的分享
G
Google Developers Blog
J
Java Code Geeks
Martin Fowler
Martin Fowler
The GitHub Blog
The GitHub Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
罗磊的独立博客
腾讯CDC
F
Fortinet All Blogs
A
About on SuperTechFans
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Recent Announcements
Recent Announcements
Last Week in AI
Last Week in AI
B
Blog RSS Feed
博客园 - 聂微东
D
DataBreaches.Net
Hugging Face - Blog
Hugging Face - Blog
The Cloudflare Blog
L
LangChain Blog
Microsoft Azure Blog
Microsoft Azure Blog
aimingoo的专栏
aimingoo的专栏

Jiajun的技术笔记

你好,2026! TiDB 源码阅读(六):TiDB Coprocessor 源码解析 性能优化的核心思想 TiDB 源码阅读(五):索引 TiDB 源码阅读(四):AST、逻辑计划、物理计划 CockroachDB Serverless Architecture podman 无故退出 Cursor Control-L (CTRL-L) Keyboard Shortcuts in Terminal Replace docker with podman Using xmonad with xfce4 A RC script for freebsd frpc 自己动手写一个k8s controller AI 会取代你的(编程)岗位吗? 自建DERP服务器提升Tailscale连接速度(使用Nginx转发) 自动升级Docker容器 再读《程序员修炼之道-从小工到专家》 让浏览器下载文件 再读《软件随想录》/《黑客与画家》/《软技能》 HTTP 压力测试中的 Coordinated Omission 2的补码 编程语言中的 context 是什么? flutter macOS 构建出错 Flatpak 使用小记 Golang CAS 操作是怎么实现的 PostgreSQL 当MQ来使用 Clash 结合 工作VPN 的网络设计 使用 PostgreSQL 搭建 JuiceFS PostgreSQL 配置优化和日志分析 有GitHub Copilot?那就可以搭建你的ChatGPT4服务 窗口函数的使用(以PG为例)
从源码看Python的descriptor
2017-04-19 · via Jiajun的技术笔记

descriptor是Python初学者比较不好懂的概念之一,再加上官网文档说的也不是很清楚, 就更容易让人误解了。但是源码总不会说不清楚,所以我们从源码看清楚descriptor到底 在干啥。

PyObject *
_PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
{
    /* Make sure the logic of _PyObject_GetMethod is in sync with
       this method.
    */

    PyTypeObject *tp = Py_TYPE(obj);
    PyObject *descr = NULL;
    PyObject *res = NULL;
    descrgetfunc f;
    Py_ssize_t dictoffset;
    PyObject **dictptr;

    if (!PyUnicode_Check(name)){
        PyErr_Format(PyExc_TypeError,
                     "attribute name must be string, not '%.200s'",
                     name->ob_type->tp_name);
        return NULL;
    }
    Py_INCREF(name);

    if (tp->tp_dict == NULL) {
        if (PyType_Ready(tp) < 0)
            goto done;
    }

    descr = _PyType_Lookup(tp, name);  // 从自身包括继承关系中找descriptor

    f = NULL;
    if (descr != NULL) {
        Py_INCREF(descr);
        f = descr->ob_type->tp_descr_get;
        if (f != NULL && PyDescr_IsData(descr)) {
        // 如果有descriptor并且是data descriptor,那么就调用并返回
            res = f(descr, obj, (PyObject *)obj->ob_type);
            goto done;
        }
    }

    if (dict == NULL) {
        /* Inline _PyObject_GetDictPtr */
        dictoffset = tp->tp_dictoffset;
        if (dictoffset != 0) {
            if (dictoffset < 0) {
                Py_ssize_t tsize;
                size_t size;

                tsize = ((PyVarObject *)obj)->ob_size;
                if (tsize < 0)
                    tsize = -tsize;
                size = _PyObject_VAR_SIZE(tp, tsize);
                assert(size <= PY_SSIZE_T_MAX);

                dictoffset += (Py_ssize_t)size;
                assert(dictoffset > 0);
                assert(dictoffset % SIZEOF_VOID_P == 0);
            }
            dictptr = (PyObject **) ((char *)obj + dictoffset);
            dict = *dictptr;
        }
    }
    if (dict != NULL) {
        Py_INCREF(dict);
        // 从__dict__里查找属性并返回
        res = PyDict_GetItem(dict, name);
        if (res != NULL) {
            Py_INCREF(res);
            Py_DECREF(dict);
            goto done;
        }
        Py_DECREF(dict);
    }

    if (f != NULL) {
        // 非data descriptor,返回
        res = f(descr, obj, (PyObject *)Py_TYPE(obj));
        goto done;
    }

    if (descr != NULL) {
        res = descr;
        descr = NULL;
        goto done;
    }

    PyErr_Format(PyExc_AttributeError,
                 "'%.50s' object has no attribute '%U'",
                 tp->tp_name, name);
  done:
    Py_XDECREF(descr);
    Py_DECREF(name);
    return res;
}

PyObject *
PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
{
    // 调用此函数,往上看
    return _PyObject_GenericGetAttrWithDict(obj, name, NULL);
}

所以可以得知:

- descriptor是作为属性时才会生效,如下demo中,Foo是作为属性时,才会生效
- 属性查找顺序为 data descriptor -> __dict__ -> descriptor
In [1]: class Foo:
   ...:     def __get__(self, obj, type):
   ...:         print("__get__")
   ...:     def __set__(self, obj, val):
   ...:         print("__set__")
   ...:

In [2]: class Test:
   ...:     bar = Foo()
   ...:

In [3]: t = Test()

In [4]: t.bar
__get__

In [5]: foo = Foo()

In [6]: foo.bar
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-673afadb7b8e> in <module>()
----> 1 foo.bar

AttributeError: 'Foo' object has no attribute 'bar'