Python内置函数源码解析:从abs到sum的底层实现

季宁
季宁
发布时间:2026-10-15| 3人浏览过
原创

本文深入解析Python多个内置函数的C语言底层实现。通过分析abs、all、any、dir、id、locals、globals、hash及sum等函数的源码,揭示其内部调用机制,如PyNumber_Absolute、PyObject_Dir及迭代器处理逻辑。重点讲解sum函数中start参数的作用及类型限制,帮助开发者理解Python内置函数的性能优势与使用注意事项。

前言

Python提供了丰富的内置函数,显著提升了开发效率。本文将选取部分常用内置函数,深入探讨其底层C语言实现机制。这些内置函数的核心代码主要位于Python解释器的bitlinmodule.c文件中。

1. abs

abs函数的主要功能是计算整数的绝对值或复数的模。

static PyObject *
builtin_abs(PyObject *module, PyObject *x)
{
    return PyNumber_Absolute(x);
}

该函数在底层通过调用PyNumber_Absolute来实现具体逻辑。

//Objects/abstract.c
PyObject *
PyNumber_Absolute(PyObject *o)
{
    PyNumberMethods *m;
    if (o == NULL) {
        return null_error();
    }
    //通过类型对象获取操作簇 PyNumberMethods
    m = o->ob_type->tp_as_number;
    //调用 nb_absolute 
    if (m && m->nb_absolute)
        return m->nb_absolute(o);

    return type_error("bad operand type for abs(): '%.200s'", o);
}

以整型为例,其类型对象中的nb_absoulte指针指向long_absolute函数。

//Objects/longobject.c
static PyObject *
long_abs(PyLongObject *v)
{
    if (Py_SIZE(v) < 0)
        //如果 v 小于 0,那么取相反数
        return long_neg(v);
    else
        //否则返回本身
        return long_long((PyObject *)v);
}

由于Python 3中的整数采用数组形式存储,因此不能简单地直接取相反数,还需要进行额外的处理。但从数学角度理解,其核心逻辑即为取相反数。

2. all

all函数接收一个可迭代对象作为参数。如果该对象中的所有元素均为真值,则返回True;只要存在一个元素为假值,则返回False。

static PyObject *
builtin_all(PyObject *module, PyObject *iterable)
{
    PyObject *it, *item;
    PyObject *(*iternext)(PyObject *);
    int cmp;
    //获取可迭代对象的迭代器
    it = PyObject_GetIter(iterable);
    if (it == NULL)
        return NULL;
    //拿到内部的 __next__ 方法
    iternext = *Py_TYPE(it)->tp_iternext;

    for (;;) {
        //迭代元素
        item = iternext(it);
        //返回 NULL,说明出异常了
        //一种是迭代完毕抛出的 StopIteration
        //另一种是迭代过程中出现的异常
        if (item == NULL)
            break;
        //判断 item 的布尔值是否为真
        //cmp > 0 表示为真
        //cmp == 0表示为假
        //cmp < 0 表示解释器调用出错(极少发生)
        cmp = PyObject_IsTrue(item);
        Py_DECREF(item);
        if (cmp < 0) {
            Py_DECREF(it);
            return NULL;
        }
        //只要有一个元素为假,就返回 False
        if (cmp == 0) {
            Py_DECREF(it);
            Py_RETURN_FALSE;
        }
    }
    Py_DECREF(it);
    //PyErr_Occurred() 为真表示出现异常了
    if (PyErr_Occurred()) {
        //判断异常是不是 StopIteration
        if (PyErr_ExceptionMatches(PyExc_StopIteration))
            //如果是,那么表示迭代正常结束
            //PyErr_Clear() 负责将异常清空
            PyErr_Clear();
        else
            return NULL;
    }
    //走到这,说明所有的元素全部为真
    //返回 True,等价于 return Py_True
    Py_RETURN_TRUE;
}

从实现上看,all函数本质上执行了一层for循环。由于该循环由C语言编写,其执行效率远高于Python层面的代码循环。

3. any

any函数同样接收一个可迭代对象。只要其中有一个元素为真值,即返回True;若所有元素均为假值,则返回False。

static PyObject *
builtin_any(PyObject *module, PyObject *iterable)
{    
    //源码和 builtin_all 是类似的
    PyObject *it, *item;
    PyObject *(*iternext)(PyObject *);
    int cmp;
    //获取可迭代对象的迭代器
    it = PyObject_GetIter(iterable);
    if (it == NULL)
        return NULL;
    //拿到内部的 __next__ 方法
    iternext = *Py_TYPE(it)->tp_iternext;

    for (;;) {
        //迭代元素
        item = iternext(it);
        if (item == NULL)
            break;
        cmp = PyObject_IsTrue(item);
        Py_DECREF(item);
        if (cmp < 0) {
            Py_DECREF(it);
            return NULL;
        }
        //只要有一个为真,则返回 True
        if (cmp > 0) {
            Py_DECREF(it);
            Py_RETURN_TRUE;
        }
    }
    Py_DECREF(it);
    if (PyErr_Occurred()) {
        if (PyErr_ExceptionMatches(PyExc_StopIteration))
            PyErr_Clear();
        else
            return NULL;
    }
    //全部为假,则返回 False
    Py_RETURN_FALSE;
}

4. callable

callable函数用于判断一个对象是否可被调用。

static PyObject *
builtin_callable(PyObject *module, PyObject *obj)
{
    return PyBool_FromLong((long)PyCallable_Check(obj));
}
PyBool_FromLong 是将一个整数转成布尔值,所以就看 PyCallable_Check 是返回 0,还是返回非 0。

int
PyCallable_Check(PyObject *x)
{
    if (x == NULL)
        return 0;
    return x->ob_type->tp_call != NULL;
}

其逻辑非常直观:判断一个对象是否可调用,只需检查其类型对象是否实现了__call__方法。

5. dir

当dir函数不接收任何参数时,它返回当前的局部变量空间;如果接收了对象参数,则返回该对象的所有属性名称列表。

static PyObject *
builtin_dir(PyObject *self, PyObject *args)
{
    PyObject *arg = NULL;
    //要么不接收参数,要么接收一个参数
    //如果没有接收参数,那么 arg 就是 NULL,否则就是我们传递的参数
    if (!PyArg_UnpackTuple(args, "dir", 0, 1, &arg))
        return NULL;
    return PyObject_Dir(arg);
}

该函数底层调用了PyObject_Dir函数。

//Objects/object.c
PyObject *
PyObject_Dir(PyObject *obj)
{   
    //当 obj 为 NULL,说明我们没有传参,那么返回 local 空间
    //否则返回对象的所有属性的名称
    return (obj == NULL) ? _dir_locals() : _dir_object(obj);
}

首先来看_dir_locals函数的实现。

//Objects/object.c
static PyObject *
_dir_locals(void)
{
    PyObject *names;
    PyObject *locals;
    //获取当前的 local 空间
    locals = PyEval_GetLocals();
    if (locals == NULL)
        return NULL;
    //拿到所有的 key,注意:PyMapping_Keys 返回的是列表
    names = PyMapping_Keys(locals);
    if (!names)
        return NULL;
    if (!PyList_Check(names)) {
        PyErr_Format(PyExc_TypeError,
            "dir(): expected keys() of locals to be a list, "
            "not '%.200s'", Py_TYPE(names)->tp_name);
        Py_DECREF(names);
        return NULL;
    }
    //排序
    if (PyList_Sort(names)) {
        Py_DECREF(names);
        return NULL;
    }
    //返回
    return names;
}

该部分逻辑较为简单。此外还有_dir_object函数,虽然其代码量较大,但核心逻辑并不复杂,主要是调用对象的dir方法,对获取的列表进行排序后返回。

6. id

id函数用于查看对象的内存地址。尽管Python中一切皆对象,但我们实际操作的是指向对象的指针。例如,id(name)用于查看变量name所指向对象的地址。本质上,这只需将指针转换为整数后返回即可。

static PyObject *
builtin_id(PyModuleDef *self, PyObject *v)

{   
    //将 v 转成整数,返回即可
    PyObject *id = PyLong_FromVoidPtr(v);

    if (id && PySys_Audit("builtins.id", "O", id) < 0) {
        Py_DECREF(id);
        return NULL;
    }

    return id;
}

7. locals 和 globals

这两个函数分别用于查看当前的局部变量空间和全局变量空间。实现上,直接通过栈帧的f_locals和f_globals字段即可获取相应数据。

static PyObject *
builtin_locals_impl(PyObject *module)
{
    PyObject *d;
    //在内部会通过线程状态对象拿到栈帧
    //再通过栈帧的 f_locals 字段拿到 local 空间
    d = PyEval_GetLocals();
    Py_XINCREF(d);
    return d;
}

static PyObject *
builtin_globals_impl(PyObject *module)
{
    PyObject *d;
    //和 PyEval_GetLocals 类似
    d = PyEval_GetGlobals();
    Py_XINCREF(d);
    return d;
}

8. hash

hash函数用于获取对象的哈希值。

static PyObject *
builtin_hash(PyObject *module, PyObject *obj)
{
    Py_hash_t x;
    //在内部会调用 obj -> ob_type -> tp_hash(obj)
    x = PyObject_Hash(obj);
    if (x == -1)
        return NULL;
    return PyLong_FromSsize_t(x);
}

9. sum

sum函数接收一个可迭代对象,并计算其中元素的总和。这里有一个需要特别注意的细节。

print(sum([1, 2, 3]))  # 6

try:
    print(sum(["1", "2", "3"]))
except TypeError as e:
    print(e)  # unsupported operand type(s) for +: 'int' and 'str'

有人可能会疑惑,字符串明明支持加法运算,为何sum函数直接处理字符串列表会报错?实际上,sum函数还可以接收第二个参数,如果不提供该参数,默认值为0。

这意味着sum([1, 2, 3])的计算过程实际上是0 + 1 + 2 + 3;同理,sum(["a", "b", "c"])的计算过程是0 + "a" + "b" + "c"。因此,上述报错信息指出不支持int类型和str类型的实例进行相加操作。

try:
    print(sum(["1", "2", "3"], ""))
except TypeError as e:
    print(e)  # sum() can't sum strings [use ''.join(seq) instead]

# 我们看到还是报错了,只能说明理论上是可以的
# 但 Python 建议我们使用 join

# 我们用列表举例吧
try:
    print(sum([[1], [2], [3]]))
except TypeError as e:
    print(e)  # unsupported operand type(s) for +: 'int' and 'list'

# 告诉我们 int 的实例和 list 的实例不可以相加
# 将第二个参数换成空列表
print(sum([[1], [2], [3]], []))  # [1, 2, 3]

# 如果不是空列表呢?
print(
    sum([[1], [2], [3]], ["古明地觉"])
)  # ['古明地觉', 1, 2, 3]

由此可见,sum函数的逻辑是将第二个参数(start)与第一个参数(可迭代对象)中的元素依次相加。接下来我们查看其底层实现代码。

static PyObject *
builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
{   
    //result 就是返回值,初始等于第二个参数
    //如果可迭代对象为空,那么返回的就是第二个参数
    //比如 sum([], 123) 得到的就是 123
    PyObject *result = start;
    PyObject *temp, *item, *iter;
    //获取可迭代对象的类型对象
    iter = PyObject_GetIter(iterable);
    if (iter == NULL)
        return NULL;
    
    //如果 result 为 NULL,说明我们没有传递第二个参数
    if (result == NULL) {
        //那么 result 赋值为 0
        result = PyLong_FromLong(0);
        if (result == NULL) {
            Py_DECREF(iter);
            return NULL;
        }
    } else {
        //否则的话,检测是不是 str、bytes、bytearray 类型
        //如果是的话,依旧报错,并提示使用 join 方法
        if (PyUnicode_Check(result)) {
            PyErr_SetString(PyExc_TypeError,
                "sum() can't sum strings [use ''.join(seq) instead]");
            Py_DECREF(iter);
            return NULL;
        }
        if (PyBytes_Check(result)) {
            PyErr_SetString(PyExc_TypeError,
                "sum() can't sum bytes [use b''.join(seq) instead]");
            Py_DECREF(iter);
            return NULL;
        }
        if (PyByteArray_Check(result)) {
            PyErr_SetString(PyExc_TypeError,
                "sum() can't sum bytearray [use b''.join(seq) instead]");
            Py_DECREF(iter);
            return NULL;
        }
        Py_INCREF(result);
    }

#ifndef SLOW_SUM
    //这里是快分支
    //假设所有元素都是整数
    if (PyLong_CheckExact(result)) {
        //将所有整数都迭代出来,依次相加
        //...
    }

    if (PyFloat_CheckExact(result)) {
        //将所有浮点数都迭代出来,依次相加
        //...
    }
#endif
    
    //如果不全是整数或浮点数,执行通用逻辑
    for(;;) {
        //迭代元素
        item = PyIter_Next(iter);
        if (item == NULL) {
            /* error, or end-of-sequence */
            if (PyErr_Occurred()) {
                Py_DECREF(result);
                result = NULL;
            }
            break;
        }
        //和 result 依次相加
        temp = PyNumber_Add(result, item);
        Py_DECREF(result);
        Py_DECREF(item);
        result = temp;
        if (result == NULL)
            break;
    }
    Py_DECREF(iter);
    //返回
    return result;
}

看似简单的sum函数,其底层代码量却相当庞大,此处还省略了一部分代码。

10. getattr、setattr、delattr

这几个函数应该非常熟悉。首先看getattr,它用于获取对象的指定属性,并且可以指定默认值。

static PyObject *
builtin_getattr(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
{
    PyObject *v, *name, *result;
    //参数个数必须是 2 或 3
    //对象、属性名、可选的默认值
    if (!_PyArg_CheckPositional("getattr", nargs, 2, 3))
        return NULL;
    //获取对象和属性名
    v = args[0];
    name = args[1];
    //name必须是字符串
    if (!PyUnicode_Check(name)) {
        PyErr_SetString(PyExc_TypeError,
                        "getattr(): attribute name must be string");
        return NULL;
    }
    //调用对象的 __getattr__,找不到返回默认值
    if (nargs > 2) {
        if (_PyObject_LookupAttr(v, name, &result) == 0) {
            PyObject *dflt = args[2];
            Py_INCREF(dflt);
            return dflt;
        }
    }
    else {
        result = PyObject_GetAttr(v, name);
    }
    return result;
}

同理,setattr函数是调用对象的__setattr__方法,而delattr函数则是调用对象的__delattr__方法。

至此,关于Python内置函数底层实现的解析介绍完毕。如需了解更多Python内置函数相关内容,请搜索本站历史文章或继续浏览后续相关文章,希望大家今后多多支持本站!

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。