python教程分享Python内置函数详细解析

前言:

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

需要了解更多python教程分享Python内置函数详细解析,都可以关注python教程分享栏目—猴子技术宅(www.ssfiction.com)

本文来自网络收集,不代表猴子技术宅立场,如涉及侵权请点击右边联系管理员删除。

如若转载,请注明出处:https://www.ssfiction.com/pythons/1091966.html

(0)
上一篇 3天前
下一篇 3天前

精彩推荐

发表评论

您的电子邮箱地址不会被公开。