Python內(nèi)置函數(shù)詳細(xì)解析
前言:
Python 自帶了很多的內(nèi)置函數(shù),極大地方便了我們的開(kāi)發(fā),下面就來(lái)挑幾個(gè)內(nèi)置函數(shù),看看底層是怎么實(shí)現(xiàn)的。
內(nèi)置函數(shù)位于 Python/bitlinmodule.c 中。
1.abs
abs 的功能是取一個(gè)整數(shù)的絕對(duì)值,或者取一個(gè)復(fù)數(shù)的模。
static PyObject * builtin_abs(PyObject *module, PyObject *x) { return PyNumber_Absolute(x); }
該函數(shù)調(diào)用了 PyNumber_Absolute。
//Objects/abstract.c PyObject * PyNumber_Absolute(PyObject *o) { PyNumberMethods *m; if (o == NULL) { return null_error(); } //通過(guò)類(lèi)型對(duì)象獲取操作簇 PyNumberMethods m = o->ob_type->tp_as_number; //調(diào)用 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,那么取相反數(shù) return long_neg(v); else //否則返回本身 return long_long((PyObject *)v); }
由于 Python3 的整數(shù)是以數(shù)組的方式存儲(chǔ)的,所以不會(huì)直接取相反數(shù),還要做一些額外的處理,但從數(shù)學(xué)上直接理解為取相反數(shù)即可。
2.all
接收一個(gè)可迭代對(duì)象,如果里面的元素全部為真,則返回 True;只要有一個(gè)不為真,則返回 False。
static PyObject * builtin_all(PyObject *module, PyObject *iterable) { PyObject *it, *item; PyObject *(*iternext)(PyObject *); int cmp; //獲取可迭代對(duì)象的迭代器 it = PyObject_GetIter(iterable); if (it == NULL) return NULL; //拿到內(nèi)部的 __next__ 方法 iternext = *Py_TYPE(it)->tp_iternext; for (;;) { //迭代元素 item = iternext(it); //返回 NULL,說(shuō)明出異常了 //一種是迭代完畢拋出的 StopIteration //另一種是迭代過(guò)程中出現(xiàn)的異常 if (item == NULL) break; //判斷 item 的布爾值是否為真 //cmp > 0 表示為真 //cmp == 0表示為假 //cmp < 0 表示解釋器調(diào)用出錯(cuò)(極少發(fā)生) cmp = PyObject_IsTrue(item); Py_DECREF(item); if (cmp < 0) { Py_DECREF(it); return NULL; } //只要有一個(gè)元素為假,就返回 False if (cmp == 0) { Py_DECREF(it); Py_RETURN_FALSE; } } Py_DECREF(it); //PyErr_Occurred() 為真表示出現(xiàn)異常了 if (PyErr_Occurred()) { //判斷異常是不是 StopIteration if (PyErr_ExceptionMatches(PyExc_StopIteration)) //如果是,那么表示迭代正常結(jié)束 //PyErr_Clear() 負(fù)責(zé)將異常清空 PyErr_Clear(); else return NULL; } //走到這,說(shuō)明所有的元素全部為真 //返回 True,等價(jià)于 return Py_True Py_RETURN_TRUE; }
因此 all 就是一層 for 循環(huán),但它是 C 的循環(huán),所以比我們寫(xiě)的 Python 代碼快。
3.any
接收一個(gè)可迭代對(duì)象,只要里面有一個(gè)元素為真,則返回 True;如果全為假,則返回 False。
static PyObject * builtin_any(PyObject *module, PyObject *iterable) { //源碼和 builtin_all 是類(lèi)似的 PyObject *it, *item; PyObject *(*iternext)(PyObject *); int cmp; //獲取可迭代對(duì)象的迭代器 it = PyObject_GetIter(iterable); if (it == NULL) return NULL; //拿到內(nèi)部的 __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; } //只要有一個(gè)為真,則返回 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
判斷一個(gè)對(duì)象是否可調(diào)用。
static PyObject * builtin_callable(PyObject *module, PyObject *obj) { return PyBool_FromLong((long)PyCallable_Check(obj)); } PyBool_FromLong 是將一個(gè)整數(shù)轉(zhuǎn)成布爾值,所以就看 PyCallable_Check 是返回 0,還是返回非 0。 int PyCallable_Check(PyObject *x) { if (x == NULL) return 0; return x->ob_type->tp_call != NULL; }
邏輯非常簡(jiǎn)單,一個(gè)對(duì)象是否可調(diào)用,就看它的類(lèi)型對(duì)象有沒(méi)有實(shí)現(xiàn) __call__。
5.dir
如果不接收任何對(duì)象,返回當(dāng)前的 local 空間;否則返回某個(gè)對(duì)象的所有屬性的名稱(chēng)。
static PyObject * builtin_dir(PyObject *self, PyObject *args) { PyObject *arg = NULL; //要么不接收參數(shù),要么接收一個(gè)參數(shù) //如果沒(méi)有接收參數(shù),那么 arg 就是 NULL,否則就是我們傳遞的參數(shù) if (!PyArg_UnpackTuple(args, "dir", 0, 1, &arg)) return NULL; return PyObject_Dir(arg); }
該函數(shù)調(diào)用了 PyObject_Dir。
//Objects/object.c PyObject * PyObject_Dir(PyObject *obj) { //當(dāng) obj 為 NULL,說(shuō)明我們沒(méi)有傳參,那么返回 local 空間 //否則返回對(duì)象的所有屬性的名稱(chēng) return (obj == NULL) ? _dir_locals() : _dir_object(obj); }
先來(lái)看看 _dir_locals 函數(shù)。
//Objects/object.c static PyObject * _dir_locals(void) { PyObject *names; PyObject *locals; //獲取當(dāng)前的 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; }
還是比較簡(jiǎn)單的,然后是 _dir_object,它的代碼比較多,這里就不看了。但是邏輯很簡(jiǎn)單,就是調(diào)用對(duì)象的 dir 方法,將得到的列表排序后返回。
6.id
查看對(duì)象的內(nèi)存地址,我們知道 Python 雖然一切皆對(duì)象,但是我們拿到的都是指向?qū)ο蟮闹羔?。比?id(name) 是查看變量 name 指向?qū)ο蟮牡刂?,說(shuō)白了不就是 name 本身嗎?所以直接將指針轉(zhuǎn)成整數(shù)之后返回即可,
static PyObject * builtin_id(PyModuleDef *self, PyObject *v) { //將 v 轉(zhuǎn)成整數(shù),返回即可 PyObject *id = PyLong_FromVoidPtr(v); if (id && PySys_Audit("builtins.id", "O", id) < 0) { Py_DECREF(id); return NULL; } return id; }
7.locals 和 globals
這兩者是查看當(dāng)前的 local 空間和 global 空間,顯然直接通過(guò)棧幀的 f_locals 和 f_globals 字段即可獲取。
static PyObject * builtin_locals_impl(PyObject *module) { PyObject *d; //在內(nèi)部會(huì)通過(guò)線(xiàn)程狀態(tài)對(duì)象拿到棧幀 //再通過(guò)棧幀的 f_locals 字段拿到 local 空間 d = PyEval_GetLocals(); Py_XINCREF(d); return d; } static PyObject * builtin_globals_impl(PyObject *module) { PyObject *d; //和 PyEval_GetLocals 類(lèi)似 d = PyEval_GetGlobals(); Py_XINCREF(d); return d; }
8.hash
獲取對(duì)象的哈希值。
static PyObject * builtin_hash(PyObject *module, PyObject *obj) { Py_hash_t x; //在內(nèi)部會(huì)調(diào)用 obj -> ob_type -> tp_hash(obj) x = PyObject_Hash(obj); if (x == -1) return NULL; return PyLong_FromSsize_t(x); }
9.sum
接收一個(gè)可迭代對(duì)象,計(jì)算它們的和。但是這里面有一個(gè)需要注意的地方。
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'
咦,字符串明明也支持加法呀,為啥不行呢?其實(shí) sum 還可以接收第二個(gè)參數(shù),我們不傳的話(huà)就是 0。
也就是說(shuō) sum([1, 2, 3]) 其實(shí)是 0 + 1 + 2 + 3;那么同理,sum(["a", "b", "c"]) 其實(shí)是 0 + "a" + "b" + "c";所以上面的報(bào)錯(cuò)信息是不支持類(lèi)型為 int 和 str 的實(shí)例進(jìn)行相加。
try: print(sum(["1", "2", "3"], "")) except TypeError as e: print(e) # sum() can't sum strings [use ''.join(seq) instead] # 我們看到還是報(bào)錯(cuò)了,只能說(shuō)明理論上是可以的 # 但 Python 建議我們使用 join # 我們用列表舉例吧 try: print(sum([[1], [2], [3]])) except TypeError as e: print(e) # unsupported operand type(s) for +: 'int' and 'list' # 告訴我們 int 的實(shí)例和 list 的實(shí)例不可以相加 # 將第二個(gè)參數(shù)換成空列表 print(sum([[1], [2], [3]], [])) # [1, 2, 3] # 如果不是空列表呢? print( sum([[1], [2], [3]], ["古明地覺(jué)"]) ) # ['古明地覺(jué)', 1, 2, 3]
所以 sum 是將第二個(gè)參數(shù)和第一個(gè)參數(shù)(可迭代對(duì)象)里面的元素依次相加,然后看一下底層實(shí)現(xiàn)。
static PyObject * builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) { //result 就是返回值,初始等于第二個(gè)參數(shù) //如果可迭代對(duì)象為空,那么返回的就是第二個(gè)參數(shù) //比如 sum([], 123) 得到的就是 123 PyObject *result = start; PyObject *temp, *item, *iter; //獲取可迭代對(duì)象的類(lèi)型對(duì)象 iter = PyObject_GetIter(iterable); if (iter == NULL) return NULL; //如果 result 為 NULL,說(shuō)明我們沒(méi)有傳遞第二個(gè)參數(shù) if (result == NULL) { //那么 result 賦值為 0 result = PyLong_FromLong(0); if (result == NULL) { Py_DECREF(iter); return NULL; } } else { //否則的話(huà),檢測(cè)是不是 str、bytes、bytearray 類(lèi)型 //如果是的話(huà),依舊報(bào)錯(cuò),并提示使用 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 //這里是快分支 //假設(shè)所有元素都是整數(shù) if (PyLong_CheckExact(result)) { //將所有整數(shù)都迭代出來(lái),依次相加 //... } if (PyFloat_CheckExact(result)) { //將所有浮點(diǎn)數(shù)都迭代出來(lái),依次相加 //... } #endif //如果不全是整數(shù)或浮點(diǎn)數(shù),執(zhí)行通用邏輯 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; }
一個(gè)小小的 sum,代碼量還真不少呢,我們還省略了一部分。
10.getattr、setattr、delattr
這幾個(gè)應(yīng)該已經(jīng)很熟悉了,先來(lái)看看 getattr,它是獲取對(duì)象的某個(gè)屬性,并且還可以指定默認(rèn)值。
static PyObject * builtin_getattr(PyObject *self, PyObject *const *args, Py_ssize_t nargs) { PyObject *v, *name, *result; //參數(shù)個(gè)數(shù)必須是 2 或 3 //對(duì)象、屬性名、可選的默認(rèn)值 if (!_PyArg_CheckPositional("getattr", nargs, 2, 3)) return NULL; //獲取對(duì)象和屬性名 v = args[0]; name = args[1]; //name必須是字符串 if (!PyUnicode_Check(name)) { PyErr_SetString(PyExc_TypeError, "getattr(): attribute name must be string"); return NULL; } //調(diào)用對(duì)象的 __getattr__,找不到返回默認(rèn)值 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 是調(diào)用對(duì)象的 __setattr__,delattr 是調(diào)用對(duì)象的 __delattr__。
到此這篇關(guān)于Python內(nèi)置函數(shù)詳細(xì)解析的文章就介紹到這了,更多相關(guān)Python內(nèi)置函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
對(duì)python使用http、https代理的實(shí)例講解
今天小編就為大家分享一篇對(duì)python使用http、https代理的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05Python如何實(shí)現(xiàn)拆分?jǐn)?shù)據(jù)集
這篇文章主要介紹了Python如何實(shí)現(xiàn)拆分?jǐn)?shù)據(jù)集問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09Python的flask接收前臺(tái)的ajax的post數(shù)據(jù)和get數(shù)據(jù)的方法
這篇文章主要介紹了Python的flask接收前臺(tái)的ajax的post數(shù)據(jù)和get數(shù)據(jù)的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04基于Python實(shí)現(xiàn)文件分類(lèi)器的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何基于Python實(shí)現(xiàn)文件分類(lèi)器,目的主要是為了將辦公過(guò)程中產(chǎn)生的各種格式的文件完成整理,感興趣的可以了解一下2023-04-04在Python3.74+PyCharm2020.1 x64中安裝使用Kivy的詳細(xì)教程
這篇文章主要介紹了在Python3.74+PyCharm2020.1 x64中安裝使用Kivy的詳細(xì)教程,本文通過(guò)圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08Python多線(xiàn)程及其基本使用方法實(shí)例分析
這篇文章主要介紹了Python多線(xiàn)程及其基本使用方法,結(jié)合實(shí)例形式分析了Python相關(guān)概念、原理、使用方法及操作注意事項(xiàng),需要的朋友可以參考下2019-10-10pyqt5數(shù)據(jù)庫(kù)使用詳細(xì)教程(打包解決方案)
這篇文章主要介紹了pyqt5數(shù)據(jù)庫(kù)使用教程(打包解決方案),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03Python使用jsonpath-rw模塊處理Json對(duì)象操作示例
這篇文章主要介紹了Python使用jsonpath-rw模塊處理Json對(duì)象操作,結(jié)合實(shí)例形式分析了Python使用requests與response處理json的方法,并給出了jsonpath_rw模塊操作json對(duì)象的基本示例,需要的朋友可以參考下2018-07-07