摘要:源碼漫游指南一作者秘塔科技算法研究員前幾天發(fā)布了第五屆頂級(jí)語(yǔ)言交互排行榜,語(yǔ)言繼續(xù)穩(wěn)坐第一把交椅,并且相比去年的排行情況,拉開(kāi)了與第二名的距離去年第二名的排名得分為。包含字節(jié)碼相關(guān)的底層抽象。字節(jié)碼對(duì)象的實(shí)現(xiàn)。源文件執(zhí)行結(jié)束后的清理工作。
Python源碼漫游指南(一)
作者:秘塔科技算法研究員 Qian Wan
前幾天IEEE Spectrum發(fā)布了第五屆頂級(jí)語(yǔ)言交互排行榜,Python語(yǔ)言繼續(xù)穩(wěn)坐第一把交椅,并且相比去年的排行情況,拉開(kāi)了與第二名的距離(去年第二名的排名得分為99.7)。從下圖能看出Python的優(yōu)勢(shì)還是很明顯的,而且在Web、企業(yè)級(jí)和嵌入式這三種應(yīng)用類(lèi)別的流行度都很高。
冰凍三尺非一日之寒。Python語(yǔ)言自1990年由Guido van Rossum第一次發(fā)布至今已經(jīng)快三十年的歷史,它支持多種操作系統(tǒng),并以CPython為參考實(shí)現(xiàn)。Python語(yǔ)言在很多領(lǐng)域都有殺手級(jí)的應(yīng)用框架,如深度學(xué)習(xí)方面有PyTorch和Tensorflow,自然語(yǔ)言處理有NLTK,Web框架有Django、Flask,科學(xué)計(jì)算有Numpy、Scipy,計(jì)算機(jī)視覺(jué)有OpenCV,科學(xué)繪圖有Matplotlib,爬蟲(chóng)有Scrapy,凡此種種,不一而足。面對(duì)這么多不同種類(lèi)的Python應(yīng)用框架,下面一些問(wèn)題是值得我們思考的:
怎樣使用Python語(yǔ)言能將程序的性能發(fā)揮到極致?
什么類(lèi)型的單一語(yǔ)言框架不適合用Python來(lái)實(shí)現(xiàn)?
多語(yǔ)言框架中與Python語(yǔ)言的交互如何做到高效?
從架構(gòu)的角度看,Python內(nèi)部的架構(gòu)設(shè)計(jì)如何?
從使用Python語(yǔ)言的角度,它適合于什么樣的軟件架構(gòu)設(shè)計(jì)?
在多語(yǔ)言(Python與CUDA)、異構(gòu)節(jié)點(diǎn)(CPU與GPU)、多業(yè)務(wù)類(lèi)型(IO密集型與CPU密集型)以及跨區(qū)域(跨國(guó)多機(jī)房)的復(fù)雜系統(tǒng)中,Python語(yǔ)言的定位又如何?其他語(yǔ)言呢?
三言?xún)烧Z(yǔ)可能很難比較全面的回答上面一些問(wèn)題,而且只研究Python語(yǔ)言得到的答案也可能會(huì)有失偏頗。但是Python語(yǔ)言的源代碼能夠?yàn)榛卮疬@些問(wèn)題提供一些線索,而且通過(guò)閱讀源碼能讓我們?cè)谑褂肞ython語(yǔ)言時(shí)看到一些以前我們看不到的細(xì)節(jié),就如同《黑客帝國(guó)》電影里的Neo一樣能看到母體世界的源代碼,也能像Neo那樣在機(jī)器的世界里飛天遁地。
Python環(huán)境的部署我們使用pyenv花幾分鐘時(shí)間來(lái)構(gòu)建Python運(yùn)行環(huán)境,它不僅可以與操作系統(tǒng)原生的Python環(huán)境隔離,還能支持多種版本的Python環(huán)境,另外也支持在同一Python版本下的多個(gè)虛擬環(huán)境,可以用來(lái)隔離不同應(yīng)用的Python依賴(lài)包。部署代碼如下
$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv $ echo "export PYENV_ROOT="$HOME/.pyenv"" >> ~/.bashrc $ echo "export PATH="$PYENV_ROOT/bin:$PATH"" >> ~/.bashrc $ git clone https://github.com/pyenv/pyenv-virtualenv.git ${HOME}/.pyenv/plugins/pyenv-virtualenv $ echo "eval "$(pyenv init -)"" >> ~/.bashrc $ echo "eval "$(pyenv virtualenv-init -)"" >> ~/.bashrc $ CONFIGURE_OPTS=--enable-shared $HOME/.pyenv/bin/pyenv install 3.6.6 -k -v $ $HOME/.pyenv/bin/pyenv virtualenv 3.6.6 py3.6
部署好了之后每次運(yùn)行下面命令就能替換掉系統(tǒng)原生的Python環(huán)境
$ pyenv activate py3.6
安裝后的目錄結(jié)構(gòu)如下
Python源碼:~/.pyenv/sources/3.6.6/Python-3.6.6
頭文件:~/.pyenv/versions/3.6.6/include/python3.6m/
動(dòng)態(tài)鏈接庫(kù):~/.pyenv/versions/3.6.6/lib/libpython3.6m.dylib
目錄結(jié)構(gòu)要深入剖析Python的源代碼,就要對(duì)源碼中幾個(gè)大的模塊的作用有一個(gè)初步的認(rèn)識(shí)。我們進(jìn)入到源碼目錄~/.pyenv/sources/3.6.6/Python-3.6.6,其中幾個(gè)跟Python語(yǔ)言直接相關(guān)的目錄及其功能如下
Include:C頭文件,與部署好的頭文件目錄~/.pyenv/versions/3.6.6/include/python3.6m/中的文件一致(嚴(yán)格來(lái)說(shuō),部署好的頭文件目錄中會(huì)多一個(gè)自動(dòng)生成的pyconfig.h文件),這些頭文件定義了Python語(yǔ)言的底層抽象結(jié)構(gòu)。
Lib:Python語(yǔ)言庫(kù),這部分不參與Python的編譯,而是用Python語(yǔ)言寫(xiě)好的模塊庫(kù)。
Modules:用C語(yǔ)言實(shí)現(xiàn)的Python內(nèi)置庫(kù)。
Objects:Python內(nèi)置對(duì)象的C語(yǔ)言實(shí)現(xiàn)以及抽象接口的實(shí)現(xiàn)。
Parser:Python編譯器的前端,詞法分析器和語(yǔ)法分析器。后者就是基于龍書(shū)的LL(1)實(shí)現(xiàn)的。
Programs:可執(zhí)行文件~/.pyenv/versions/3.6.6/bin/python的源碼所在的目錄。
Python:Python虛擬機(jī)所在的目錄,也是整個(gè)Python語(yǔ)言較為核心的部分。
使用下面的圖示能更好的展示這些目錄之前的相互關(guān)系,虛線箭頭表示提供接口定義,實(shí)線箭頭表示提供服務(wù),自頂向下的結(jié)構(gòu)也體現(xiàn)了語(yǔ)言設(shè)計(jì)在架構(gòu)上的層次關(guān)系。
Include目錄從上面這些模塊的大致功能上分析,我們可以判斷出Include、Objects和Python中的代碼比較重要。我們先看一下這三個(gè)目錄包含的代碼量
$ cat Include/* Objects/* Python/* | wc -l cat: Objects/clinic: Is a directory cat: Objects/stringlib: Is a directory cat: Python/clinic: Is a directory 215478
21萬(wàn)行代碼的閱讀量有點(diǎn)略大,我們還是先挨個(gè)看看這些目錄中文件的命名、大小以及一些注釋?zhuān)茨懿荒艿玫揭恍┚€索。
$ wc -l Include/*.h | sort -k1 ... 324 pystate.h 370 objimpl.h 499 dynamic_annotations.h 503 pyerrors.h 637 Python-ast.h 767 pyport.h 1077 object.h 1377 abstract.h 2342 unicodeobject.h 15980 total
從文件名和文件大小可以初步判斷object.h和abstract.h是兩個(gè)比較重要的頭文件,實(shí)際上它們定義了Python底層的抽象對(duì)象以及統(tǒng)一的抽象接口。
unicodeobject.h雖然體積大,但是有很多跟它類(lèi)似的頭文件,如boolobject.h、longobject.h、floatobject.h等等,這些頭文件應(yīng)該是內(nèi)置類(lèi)型的頭文件,我們可以暫時(shí)不去理會(huì)這些文件,對(duì)語(yǔ)言的總體理解不會(huì)造成困難。
為了不漏掉一些重要的頭文件,我們快速閱讀一下其他頭文件中可能包含的一些引導(dǎo)性的注釋?zhuān)l(fā)現(xiàn)這些頭文件也比較重要:
Python.h:元頭文件,通常在寫(xiě)Python的C擴(kuò)展時(shí)會(huì)包含它。
ceval.h:作為Python/ceval.c的頭文件,而Python/ceval.c負(fù)責(zé)運(yùn)行編譯后的代碼。
code.h:包含字節(jié)碼相關(guān)的底層抽象。
compile.h:抽象語(yǔ)法樹(shù)的編譯接口。
objimpl.h:跟內(nèi)存相關(guān)的抽象對(duì)象高層接口,如內(nèi)存分配,初始化,垃圾回收等等。
pystate.h:線程狀態(tài)與解釋器狀態(tài)以及它們的接口。
pythonrun.h:Python代碼的語(yǔ)法分析與執(zhí)行接口。
通過(guò)以上篩選,我們看看還剩下多少代碼:
$ cat object.h abstract.h objimpl.h Python.h ceval.h code.h compile.h pystate.h pythonrun.h | wc -l 3950
核心頭文件壓縮到不到4千行。
Objects目錄用類(lèi)似的思路,我們能從Objects目錄中篩選出一些比較重要的文件
abstract.c:抽象對(duì)象的接口實(shí)現(xiàn)。
codeobject.c:字節(jié)碼對(duì)象的實(shí)現(xiàn)。
object.c:通用對(duì)象操作的實(shí)現(xiàn)。
obmalloc.c:內(nèi)存分配相關(guān)實(shí)現(xiàn)。
typeobject.c:Type對(duì)象實(shí)現(xiàn)。
統(tǒng)計(jì)一下代碼量
$ wc -l abstract.c codeobject.c object.c obmalloc.c typeobject.c 3246 abstract.c 921 codeobject.c 2048 object.c 2376 obmalloc.c 7612 typeobject.c 16203 total
一下子新增了1.6萬(wàn)行,畢竟是實(shí)打?qū)嵉腃語(yǔ)言實(shí)現(xiàn)。
另外還有一些具象化的對(duì)象實(shí)現(xiàn)文件,雖然它們跟longobject.c和dictobject.c之類(lèi)的對(duì)象實(shí)現(xiàn)類(lèi)似,都是具體的對(duì)象,但是它們跟Python語(yǔ)言特性比較相關(guān),在這里也把它們列出來(lái),做為備份。
classobject.c:類(lèi)對(duì)象實(shí)現(xiàn)。
codeobject.c:代碼對(duì)象實(shí)現(xiàn)。
frameobject.c:Frame對(duì)象實(shí)現(xiàn)。
funcobject.c:函數(shù)對(duì)象實(shí)現(xiàn)。
methodobject.c:方法對(duì)象實(shí)現(xiàn)。
moduleobject.c:模塊對(duì)象實(shí)現(xiàn)。
順便統(tǒng)計(jì)下行數(shù)
$ wc -l classobject.c codeobject.c frameobject.c funcobject.c methodobject.c moduleobject.c 648 classobject.c 921 codeobject.c 1038 frameobject.c 1031 funcobject.c 553 methodobject.c 802 moduleobject.c 4993 total
Objects目錄中合計(jì)約2.1萬(wàn)行。通過(guò)探索這些源代碼,我們看出Python的一個(gè)設(shè)計(jì)原則就是:一切皆對(duì)象。
嚴(yán)格來(lái)說(shuō),只有Python語(yǔ)言暴露給外部使用的部分才抽象成了對(duì)象,而一些僅在內(nèi)部使用的數(shù)據(jù)結(jié)構(gòu)則沒(méi)有對(duì)象封裝,如后面會(huì)提到的解釋器狀態(tài)和線程狀態(tài)等。Python目錄
依然經(jīng)過(guò)一輪篩選,能得到下面這些比較重要的文件
ast.c:將具體語(yǔ)法樹(shù)轉(zhuǎn)換成抽象語(yǔ)法樹(shù),主要函數(shù)是PyAST_FromNode()
ceval.c:執(zhí)行編譯后的字節(jié)碼。
ceval_gil.h:全局解釋器鎖(Global Interpreter Lock,GIL)的接口。
compile.c:將抽象語(yǔ)法樹(shù)編譯成Python字節(jié)碼。
pylifecycle.c:Python解釋器的頂層代碼,包括解釋器的初始化以及退出。
pystate.c:線程狀態(tài)與解釋器狀態(tài),以及它們的接口實(shí)現(xiàn)。
pythonrun.c:Python解釋器的頂層代碼,包括解釋器的初始化以及退出。
能夠注意到,pylifecycle.c和pythonrun.c的功能是類(lèi)似的,實(shí)際上查閱Python開(kāi)發(fā)歷史記錄能發(fā)現(xiàn)前者是因?yàn)殚_(kāi)發(fā)需要從后者分離出來(lái)的。統(tǒng)計(jì)一下代碼的數(shù)量:
$ wc -l ast.c ceval.c ceval_gil.h compile.c pystate.c pythonrun.c 5277 ast.c 5600 ceval.c 270 ceval_gil.h 5329 compile.c 958 pystate.c 1596 pythonrun.c 19030 total
這樣濃縮下來(lái)Include、Objects和Python三個(gè)文件夾中比較重要的代碼一共大約4.4萬(wàn)行,先不說(shuō)我們這樣篩選出來(lái)的一波有沒(méi)有漏掉重要信息,其他很多支持性的代碼都還沒(méi)有包含進(jìn)去。至少目前有了一個(gè)大的輪廓,接下來(lái)在深入代碼的時(shí)候可以慢慢擴(kuò)展開(kāi)。
頂層調(diào)用樹(shù)前面討論了Python源碼的主要目錄結(jié)構(gòu),以及其中主要的源文件。這里我們換一個(gè)思路,看看一個(gè)Python源文件是如何在Python解釋器里面運(yùn)行的。調(diào)用Python的可執(zhí)行文件~/.pyenv/versions/3.6.6/bin/python和調(diào)用我們編寫(xiě)的其他C語(yǔ)言程序在方式上并沒(méi)有太大區(qū)別,不同之處在于Python可執(zhí)行文件讀取的Python源文件,并執(zhí)行其中的代碼。Python之于C就如同C之于匯編,只是Python編譯的字節(jié)碼在Python虛擬機(jī)上運(yùn)行,匯編代碼直接在物理機(jī)上運(yùn)行(嚴(yán)格來(lái)說(shuō)還需要轉(zhuǎn)換成機(jī)器代碼)。
以下面這條Python源文件運(yùn)行為例來(lái)考察Python可執(zhí)行文件的執(zhí)行過(guò)程(大家可以玩玩這個(gè)生命游戲,運(yùn)氣好能看到滑翔機(jī))。
$ python ~/.pyenv/sources/3.6.6/Python-3.6.6/Tools/demo/life.py
既然Python的可執(zhí)行文件是C語(yǔ)言編譯成的,那么一定有C語(yǔ)言的入口函數(shù)main,它就位于Python源碼的./Programs/python.c文件中。
int main(int argc, char **argv) { // ... res = Py_Main(argc, argv_copy); // ... }
順藤摸瓜,我們可以梳理出調(diào)用樹(shù)的主干部分。下面的樹(shù)形結(jié)構(gòu)中,冒號(hào)左邊為函數(shù)名,右邊表示函數(shù)定義所在的C源文件,樹(shù)形結(jié)構(gòu)表示函數(shù)定義中包含的其他函數(shù)嵌套調(diào)用。
main: Programs/python.c └─ Py_Main: Modules/main.c ├─ Py_Initialize: Python/pylifecycle.c │ ├─ PyInterpreterState_New: Python/pystate.c │ ├─ PyThreadState_New: Python/pystate.c │ ├─ _PyGILState_Init: Python/pystate.c │ └─ _Py_ReadyTypes: Objects/object.c ├─ run_file: Modules/main.c │ └─ PyRun_FileExFlags: Python/pythonrun.c │ ├─ PyParser_ASTFromFileObject: Python/pythonrun.c │ │ ├─ PyParser_ParseFileObject: Parser/parsetok.c │ │ └─ PyAST_FromNodeObject: Python/ast.c │ └─ run_mod: Python/pythonrun.c │ ├─ PyAST_CompileObject: Python/compile.c │ └─ PyEval_EvalCode: Python/ceval.c │ ├─ PyFrame_New: Objects/frameobject.c │ └─ PyEval_EvalFrameEx: Python/ceval.c └─ Py_FinalizeEx: Python/pylifecycle.c
不得不說(shuō),Python源碼的可讀性非常好,這些函數(shù)的命名方式都是自解釋的。Python源文件的運(yùn)行大致分為兩個(gè)步驟:
Py_Initialize:初始化過(guò)程,主要涉及到解釋器狀態(tài)、線程狀態(tài)、全局解釋器鎖以及內(nèi)置類(lèi)型的初始化。
run_file:運(yùn)行源文件,可以分為三個(gè)小步驟
PyParser_ASTFromFileObject:對(duì)源文件的文本進(jìn)行語(yǔ)法分析,得到抽象語(yǔ)法樹(shù)。
PyAST_CompileObject:將抽象語(yǔ)法樹(shù)編譯成PyCodeObject對(duì)象。
PyEval_EvalCode:在Python虛擬機(jī)中運(yùn)行PyCodeObject對(duì)象。
Py_FinalizeEx:源文件執(zhí)行結(jié)束后的清理工作。
用流程圖的形式表示上述調(diào)用樹(shù)的主干部分應(yīng)該更加清晰明了。
需要指出的是,解釋器循環(huán)真正執(zhí)行的是PyEval_EvalFrameEx函數(shù),它的參數(shù)是PyFrameObject對(duì)象,該對(duì)象為PyCodeObject對(duì)象提供了執(zhí)行的上下文環(huán)境,所以PyFrameObject和PyCodeObject都是非常核心的對(duì)象。Python提供了一些工具讓我們可以查看編譯后的代碼對(duì)象,即對(duì)編譯好的函數(shù)進(jìn)行反匯編。下面的例子雖然簡(jiǎn)單,但已經(jīng)能給人清晰的直觀認(rèn)識(shí)
>>> from dis import dis >>> class C(object): ... def __init__(self, x): ... self.x = x ... def add(self, y): ... return self.x + y ... >>> dis(C) Disassembly of __init__: 3 0 LOAD_FAST 1 (x) 2 LOAD_FAST 0 (self) 4 STORE_ATTR 0 (x) 6 LOAD_CONST 0 (None) 8 RETURN_VALUE Disassembly of add: 5 0 LOAD_FAST 0 (self) 2 LOAD_ATTR 0 (x) 4 LOAD_FAST 1 (y) 6 BINARY_ADD 8 RETURN_VALUE
反編譯的結(jié)果是一系列的操作碼。頭文件Include/opcode.h包含了Python虛擬機(jī)的所有操作碼。能看出上面simple_tuple和simple_list這兩個(gè)函數(shù)反編譯后的最大區(qū)別么?tuple是作為常量被加載進(jìn)來(lái)的,而list的生成還需要調(diào)用BUILD_LIST。原因在于tuple在Python的運(yùn)行時(shí)會(huì)進(jìn)行緩存,也就是每次使用無(wú)需請(qǐng)求操作系統(tǒng)內(nèi)核以獲得內(nèi)存空間。對(duì)比一下使用tuple和list的耗時(shí)情況
>>> %timeit x = (1, 2, 3) 10.9 ns ± 0.0617 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each) >>> %timeit x = [1, 2, 3] 46.5 ns ± 0.186 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
從統(tǒng)計(jì)結(jié)果能看出,tuple的在效率上的優(yōu)勢(shì)非常明顯。如果某一段調(diào)用特別頻繁的代碼中有些list可以替換成tuple,千萬(wàn)不要猶豫。
總結(jié)我們可以試著為文章開(kāi)頭第一個(gè)問(wèn)題提供一些思路。我們知道,對(duì)計(jì)算機(jī)做任何形式上的抽象都有可能傷害到計(jì)算的效率,對(duì)于Python來(lái)說(shuō)有以下幾點(diǎn)
Python對(duì)象的內(nèi)存部署方式是以在滿(mǎn)足一定效率的前提下足夠通用為目標(biāo)的,因此在面臨特定問(wèn)題時(shí)它不一定是最優(yōu)的。
Python是動(dòng)態(tài)類(lèi)型語(yǔ)言,并不是編譯型語(yǔ)言,導(dǎo)致代碼在運(yùn)行時(shí)是可變的,從Python將抽象語(yǔ)法樹(shù)和PyCodeObject對(duì)象暴露出來(lái)這一點(diǎn)就能看出。
全局解釋器鎖也會(huì)妨礙使用多進(jìn)程來(lái)實(shí)現(xiàn)性能的提升。
Python虛擬機(jī)作為對(duì)CPU硬件的抽象也是沒(méi)法甩鍋的。
所以為了提高Python程序的效率,我們需要深入了解Python對(duì)象的實(shí)現(xiàn)原理、PyCodeObject的特性以及全局解釋器和Python虛擬機(jī)的限制。之于文章開(kāi)頭的其他問(wèn)題,我們將隨著Python源碼的深入研究慢慢展開(kāi)。
現(xiàn)在我們對(duì)Python代碼的運(yùn)行有了一個(gè)宏觀的理解,而且大量的細(xì)節(jié)都有待深入研究。通過(guò)對(duì)調(diào)用樹(shù)主干部分的梳理,能看出其他比較重要的支持性模塊還包括Python抽象對(duì)象PyObject,抽象語(yǔ)法樹(shù)及其編譯,PyCodeObject對(duì)象,PyFrameObject對(duì)象,解釋器狀態(tài),線程狀態(tài),全局解釋器鎖。在以后的文章中,我們會(huì)分別對(duì)這些模塊進(jìn)行探討。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/42183.html
平日學(xué)習(xí)接觸過(guò)的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個(gè)網(wǎng)址:http://www.kancloud.cn/jsfron... 1. Javascript 前端生成好看的二維碼 十大經(jīng)典排序算法(帶動(dòng)圖演示) 為什么知乎前端圈普遍認(rèn)為H5游戲和H5展示的JSer 個(gè)人整理和封裝的YU.js庫(kù)|中文詳細(xì)注釋|供新手學(xué)習(xí)使用 擴(kuò)展JavaScript語(yǔ)法記錄 - 掉坑初期工具 漢字拼音轉(zhuǎn)換...
平日學(xué)習(xí)接觸過(guò)的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個(gè)網(wǎng)址:http://www.kancloud.cn/jsfron... 1. Javascript 前端生成好看的二維碼 十大經(jīng)典排序算法(帶動(dòng)圖演示) 為什么知乎前端圈普遍認(rèn)為H5游戲和H5展示的JSer 個(gè)人整理和封裝的YU.js庫(kù)|中文詳細(xì)注釋|供新手學(xué)習(xí)使用 擴(kuò)展JavaScript語(yǔ)法記錄 - 掉坑初期工具 漢字拼音轉(zhuǎn)換...
平日學(xué)習(xí)接觸過(guò)的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個(gè)網(wǎng)址:http://www.kancloud.cn/jsfron... 1. Javascript 前端生成好看的二維碼 十大經(jīng)典排序算法(帶動(dòng)圖演示) 為什么知乎前端圈普遍認(rèn)為H5游戲和H5展示的JSer 個(gè)人整理和封裝的YU.js庫(kù)|中文詳細(xì)注釋|供新手學(xué)習(xí)使用 擴(kuò)展JavaScript語(yǔ)法記錄 - 掉坑初期工具 漢字拼音轉(zhuǎn)換...
平日學(xué)習(xí)接觸過(guò)的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個(gè)網(wǎng)址:http://www.kancloud.cn/jsfron... 1. Javascript 前端生成好看的二維碼 十大經(jīng)典排序算法(帶動(dòng)圖演示) 為什么知乎前端圈普遍認(rèn)為H5游戲和H5展示的JSer 個(gè)人整理和封裝的YU.js庫(kù)|中文詳細(xì)注釋|供新手學(xué)習(xí)使用 擴(kuò)展JavaScript語(yǔ)法記錄 - 掉坑初期工具 漢字拼音轉(zhuǎn)換...
摘要:編程書(shū)籍的整理和收集最近一直在學(xué)習(xí)深度學(xué)習(xí)和機(jī)器學(xué)習(xí)的東西,發(fā)現(xiàn)深入地去學(xué)習(xí)就需要不斷的去提高自己算法和高數(shù)的能力然后也找了很多的書(shū)和文章,隨著不斷的學(xué)習(xí),也整理了下自己的學(xué)習(xí)筆記準(zhǔn)備分享出來(lái)給大家后續(xù)的文章和總結(jié)會(huì)繼續(xù)分享,先分享一部分的 編程書(shū)籍的整理和收集 最近一直在學(xué)習(xí)deep learning深度學(xué)習(xí)和機(jī)器學(xué)習(xí)的東西,發(fā)現(xiàn)深入地去學(xué)習(xí)就需要不斷的去提高自己算法和高數(shù)的能力然后...
閱讀 1174·2021-10-20 13:48
閱讀 2203·2021-09-30 09:47
閱讀 3108·2021-09-28 09:36
閱讀 2350·2019-08-30 15:56
閱讀 1202·2019-08-30 15:52
閱讀 2028·2019-08-30 10:48
閱讀 614·2019-08-29 15:04
閱讀 576·2019-08-29 12:54