Python内置函数详细解析

目录
  • 1.abs
  • 2.all
  • 3.any
  • 4.callable
  • 5.dir
  • 6.id
  • 7.locals 和 globals
  • 8.hash
  • 9.sum
  • 10.getattr、setattr、delattr

前言:

Python 自带了很多的内置函数,极大地方便了我们的开发,下面就来挑几个内置函数,看看底层是怎么实现的。
内置函数位于 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);
}

由于 Python3 的整数是以数组的方式存储的,所以不会直接取相反数,还要做一些额外的处理,但从数学上直接理解为取相反数即可。

2.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

接收一个可迭代对象,只要里面有一个元素为真,则返回 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

判断一个对象是否可调用。

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

如果不接收任何对象,返回当前的 local 空间;否则返回某个对象的所有属性的名称。

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

查看对象的内存地址,我们知道 Python 虽然一切皆对象,但是我们拿到的都是指向对象的指针。比如 id(name) 是查看变量 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

这两者是查看当前的 local 空间和 global 空间,显然直接通过栈帧的 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

获取对象的哈希值。

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

接收一个可迭代对象,计算它们的和。但是这里面有一个需要注意的地方。

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 还可以接收第二个参数,我们不传的话就是 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 是将第二个参数和第一个参数(可迭代对象)里面的元素依次相加,然后看一下底层实现。

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内置函数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python中10个常用的内置函数详解

    目录 complex() dir() divmod(a,b) enumerate(iterable,start=0) eval(expression,globals,locals) filter(function,iterable) isinstance(object,classinfo) map(function,iterable,-) input() zip(*iteables) 总结 complex() 返回一个形如 a+bj 的复数,传入参数分为三种情况: 参数为空时,返回0j 参数为字

  • python 内置函数-range()+zip()+sorted()+map()+reduce()+filter()

    目录 range函数 zip() 函数 其它内置函数 数据类型转换相关内置函数 变量相关函数 数学相关函数 进制相关函数 高阶函数 sorted(iterable,[reverse,key]) map(func, *iterables) reduce(func,iterable) filter(func,iterable) range函数 能够生成一个指定的数字序列 使用案例: ''' range(start,stop,step) 参数: start : 开始的值 ,默认值为0 stop : 结

  • python内置函数zip详解

    目录 一.简介 二.详解 三.代码 四.Reference 总结 一.简介 zip() 函数用于将可迭代的对象作为参数,主要功能是将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表. 如果各个iterable迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表. 要点:打包成元组,返回列表,如果长度不一致,则与短的iterable对齐 二.详解 语法:zip([iterable, ...]) 参数:iterable是一个或者多个可以迭代的

  • python内置函数之eval函数详解

    目录 一.简介 二.语法 三.举例 1.计算str表达式的值 2.将str转换成list 3.将str转换成dict 四.Reference 总结 一.简介 eval()函数用来执行一个字符表达式的值,并返回表达式的值 二.语法 具体语法:eval(expression[, globals[, locals]]) 参数: expression – 表达式. globals – 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象. locals– 变量作用域,局部命名空间,如果被提供,可以

  • Python中非常好用的内置函数详解

    目录 Counter 举例 实战 sorted all F-strings 总结 Python中冷门但非常好用的内置函数 Counter举例 实战sortedallF-strings Python中有许多内置函数,不像print.len那么广为人知,但它们的功能却异常强大,用好了可以大大提高代码效率,同时提升代码的简洁度,增强可阅读性 Counter collections在python官方文档中的解释是High-performance container datatypes,直接的中文翻译解释

  • Python内置函数详细解析

    目录 1.abs 2.all 3.any 4.callable 5.dir 6.id 7.locals 和 globals 8.hash 9.sum 10.getattr.setattr.delattr 前言: Python 自带了很多的内置函数,极大地方便了我们的开发,下面就来挑几个内置函数,看看底层是怎么实现的.内置函数位于 Python/bitlinmodule.c 中. 1.abs abs 的功能是取一个整数的绝对值,或者取一个复数的模. static PyObject * builti

  • Python callable内置函数原理解析

    python内置函数 callable用于检查一个对象是否是可调用的,如果函数返回True,object 仍然可能调用失败:但如果返回 False,调用对象 object 绝对不会成功. 一.callable函数简介 语法如下: callable(object) 参数介绍: object : 调用的对象: 返回值:返回bool值,如果object对象可以被调用返回true,不能被调用返回false; 值得注意的是:即便函数返回true,object也有可能调用失败,返回false意味着觉得不会成

  • Python内置加密模块用法解析

    这篇文章主要介绍了Python内置加密模块用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 数据加密: 对称加密:数据加密和解密使用相同的密钥,主要解决数据的机密性(DES,AES) 非对称加密(公匙加密):数据加密和解密使用的不同密钥,主要用于身份的验证(DSA,RSA) 单向加密:只能加密不能解密,主要用于解决数据的完整性(MD5,SHA系列算法) Python内置加密模块: hashlib 主要提供了一些常见的单向加密算法(如MD5

  • Python内置函数property()如何使用

    代码 class Shuxing(): def __init__(self, size = 10): self.size = size def getSize(self): print('getSize') return self.size def setSize(self, value): print('setSize') self.size = value def delSize(self): print('delSize') del self.size x = property(getSi

  • Python 内置函数速查表一览

    如下所示: 函数 功能 abs(x) 返回一个数的绝对值. 参数可以是一个整数或浮点数. 如果参数是一个复数,则返回它的模. all(iterable) 如果 iterable 的所有元素为真(或迭代器为空),返回 True any(iterable) 如果 iterable 的任一元素为真则返回 True. 如果迭代器为空,返回 False ascii(object) 返回一个表示对象的字符串 bin(x) 将一个整数转变为一个前缀为"0b"的二进制字符串 bool([x]) 返回一

  • python 内置函数filter

    python 内置函数filter class filter(object): """ filter(function or None, iterable) --> filter object Return an iterator yielding those items of iterable for which function(item) is true. If function is None, return the items that are true. &

  • Python内置函数bin() oct()等实现进制转换

    使用Python内置函数:bin().oct().int().hex()可实现进制转换. 先看Python官方文档中对这几个内置函数的描述: bin(x) Convert an integer number to a binary string. The result is a valid Python expression. If x is not a Python int object, it has to define an __index__() method that returns

  • python内置函数:lambda、map、filter简单介绍

    lambda lambda可以理解为一种小函数,但是它是一个表达式,而不是一个语句,所以在def不允许出现的地方仍然可以使用lambda函数,例如list里.但是lambda内只可以执行一个表达式. def f(x): return x**2 print f(3) a = lambda x: x**2 print a(3) a = lambda x,y: x+y print a(1,2) ~ 一个lambda语句就相当于一个函数定义,调用的时候也和函数一样. map函数 有时候我们可以需要处理一

  • Python内置函数—vars的具体使用方法

    本文文章主要介绍了Python内置函数-vars的具体使用方法,分享给大家,具体如下: 英文文档: vars([object]) Return the __dict__ attribute for a module, class, instance, or any other object with a __dict__ attribute.Objects such as modules and instances have an updateable __dict__ attribute; h

  • 浅谈使用Python内置函数getattr实现分发模式

    本文研究的主要是使用Python内置函数getattr实现分发模式的相关问题,具体介绍如下. getattr 常见的使用模式是作为一个分发者.举个例子,如果你有一个程序可以以不同的格式输出数据,你可以为每种输出格式定义各自的格式输出函数,然后使用唯一的分发函数调用所需的格式输出函数. 例如,让我们假设有一个以 HTML.XML 和普通文本格式打印站点统计的程序.输出格式在命令行中指定,或者保存在配置文件中.statsout 模块定义了三个函数:output_html.output_xml 和 o

随机推荐