python + C/C++混合編程的應(yīng)用

這篇文章想討論一個什么話題摄咆?我們討論一種方法——兼顧編譯語言的性能昨凡,同時又能保留動態(tài)語言的靈活性命满。我們想實現(xiàn)一個插件式框架童芹,框架結(jié)構(gòu)實用Python來實現(xiàn)命爬,插件使用c/c++來實現(xiàn)。這個框架可以實現(xiàn)插件的即插即用辐脖,插件的無損升級,插件的版本和依賴性管理皆愉,和靈活的擴展能力嗜价。

? ? ? ? 框架應(yīng)用在哪呢?在《基于Elasticsearch的數(shù)據(jù)分析系統(tǒng)(3)》中介紹了數(shù)據(jù)采集service幕庐,這個service目前僅對接一個設(shè)備久锥,未來會對接更多設(shè)備,接收處理不同的協(xié)議/格式异剥,數(shù)據(jù)切分成消息后除了送入kafka瑟由,也可能變更或通過復(fù)制的方式發(fā)到其他系統(tǒng),我們的框架要支持這些可變性冤寿。出于性能歹苦、資源節(jié)省等方面的考慮,我們沒有選用Flume督怜、Logstash等開源日志收集組件殴瘦。在二進制數(shù)據(jù)處理方面我們希望用編譯語言來快速處理。同理号杠,系統(tǒng)中的數(shù)據(jù)預(yù)處理servcie蚪腋,也有類似需求。

? ? ? ? 因為我們利用混合編程的優(yōu)勢來搭建框架姨蟋,所以我們從混合編程介紹起屉凯。

1.混合編程的應(yīng)用介紹

混合編程的應(yīng)用其實并不少見。最典型的是一款大名鼎鼎的網(wǎng)絡(luò)模擬軟件NS2(Network?Simulator)眼溶,最新版本是NS3悠砚。從事網(wǎng)絡(luò)研究、性能分析的人員對它可能都比較熟悉偷仿。

? ? ? ? NS2使用C++和Otcl作為開發(fā)語言哩簿,NS3使用C++和python宵蕉。之所以這樣設(shè)計就是出于性能和易用性的考慮。使用c++來寫網(wǎng)絡(luò)組件节榜,研究人員可以使用c++開發(fā)新的協(xié)議算法組件編譯進NS中羡玛。然后使用解釋型語言建模網(wǎng)絡(luò)——創(chuàng)建網(wǎng)絡(luò)組件及連接關(guān)系、網(wǎng)絡(luò)配置宗苍、網(wǎng)絡(luò)事件設(shè)置等稼稿。

? ? ? ? NS的組件使用OOP(面向?qū)ο缶幊?技術(shù)來實現(xiàn),組件的樣例代碼如下:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?圖1? Classifier的頭文件

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?圖2 tcp cubic算法實現(xiàn)

? ? ?圖1 中Classifier類是NS的地址分類器讳窟,負責(zé)將包轉(zhuǎn)發(fā)給下一級節(jié)點让歼,角色上相當(dāng)于路由器。圖2 是TCP cubic擁塞控制算法的實現(xiàn)丽啡,實際上就是從linux移植過來的谋右,是一個c代碼實現(xiàn)。這些內(nèi)部組件补箍,核心算法都是c/c++實現(xiàn)的改执,保證模擬時性能最優(yōu)。

? ? ?網(wǎng)絡(luò)建目友牛看上去是下面這個樣子的:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?圖3? ? ? tcl定義的模擬腳本

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?圖4? ? ? python定義的模擬腳本

可能有人覺得tcl辈挂、python的定義的模擬腳本看上去就像個配置文件,但跟配置文件不同的是裹粤,你可以在腳本中動態(tài)地修改模擬器內(nèi)部狀態(tài)终蒂。實際上,C/C++這部分就像一臺車的引擎遥诉,它負載快拇泣。而動態(tài)語言這部分就像控制臺,負載操作更方便矮锈、靈活挫酿。

2.C/C++與python混合編程

首先要說一下python只是一個語言規(guī)范,實際上python有很多實現(xiàn):CPython是標(biāo)準(zhǔn)Python愕难,是由C編寫的早龟,python腳本被編譯成CPython字節(jié)碼,然后由虛擬機解釋執(zhí)行猫缭,垃圾回收使用引用計數(shù)葱弟,我們談與C/C++混合編程實際指的是基于CPython解釋上的。除此之外猜丹,還有Jython芝加、IronPython、PyPy、Pyston藏杖,Jython是Java編寫的将塑,使用JVM的垃圾回收,可以與Java混合編程蝌麸,IronPython面向.NET平臺... ... 具體介紹可以參見這篇文章:http://python.jobbole.com/82703/

? ? ? ?python與C/C++混合編程的本質(zhì)是python調(diào)用C/C++編譯的動態(tài)鏈接庫点寥,關(guān)鍵就是把python中的數(shù)據(jù)類型轉(zhuǎn)換成c/c++中的數(shù)據(jù)類型,給編譯函數(shù)處理来吩,然后返回參數(shù)再轉(zhuǎn)換成python中的數(shù)據(jù)類型敢辩。但有幾種方式:

1)python中使用ctypes moduel,將python類型轉(zhuǎn)成c/c++類型

? ? ?一段示例代碼如下:

extern "C"

{

? ? int addBuf(char* data, int num, char* outData);

}

int addBuf(char* data, int num, char* outData)

{

? ? for (int i = 0; i < num; ++i)

? ? {

? ? ? ? outData[i] = data[i] + 3;?

? ? }

? ? return num;

}

? ? ?將上面的代碼編譯成so庫弟疆,在python中的調(diào)用:

from ctypes import * # cdll, c_int

lib = cdll.LoadLibrary('libmathBuf.so')

callAddBuf = lib.addBuf

num = 4

numbytes = c_int(num)

data_in = (c_byte * num)()

for i in range(num):

? ? data_in[i] = i

data_out = (c_byte * num)()

ret = lib.addBuf(data_in, numbytes, data_out)? #調(diào)用so庫中的函數(shù)

ctypes的更多說明參見https://docs.python.org/2/library/ctypes.html戚长。使用ctypes可以在python與so之間通過指針傳遞更復(fù)雜的結(jié)構(gòu)體等。

2)在C/C++程序中使用Python.h怠苔,寫wrap包裝接口

? ? 這種方式是在c/c++程序中處理入/出參數(shù)同廉,先看一段例程(輸入字符串,然后當(dāng)做系統(tǒng)命令執(zhí)行):

Html 代碼

01#include <Python.h>

02static?PyObject* SpamError;

03static?PyObject* spam_system(PyObject* self, PyObject* args)

04{

05????????const?char* command;

06????????int?sts;

07????????if?(!PyArg_ParseTuple(args,?"s", &command))?//將args參數(shù)按照string類型處理柑司,給command賦值

08????????????????return?NULL;

09????????sts =?system(command);?//調(diào)用系統(tǒng)命令

10????????if?(sts < 0) {

11????????????????PyErr_SetString(SpamError,?"System command failed");

12????????????????return?NULL;

13????????}

14????????return?PyLong_FromLong(sts);?//將返回結(jié)果轉(zhuǎn)換為PyObject類型

15}

16//方法表

17static?PyMethodDef SpamMethods[] = {

18????????{"system", spam_system, METH_VARARGS,

19????????"Execute a shell command."},

20????????{NULL, NULL, 0, NULL}

21};

22//模塊初始化函數(shù)

23PyMODINIT_FUNC initspam(void)

24{

25????????PyObject* m;

26????????//m = PyModule_Create(&spammodule); // v3.4

27????????m = Py_InitModule("spam", SpamMethods);

28????????if?(m == NULL)

29????????????????return;

30????????SpamError = PyErr_NewException("spam.error",NULL,NULL);

31????????Py_INCREF(SpamError);

32????????PyModule_AddObject(m,"error",SpamError);

33}

? ? ? 處理上所有的入?yún)⑿羧堋⒊鰠⒍甲鳛镻yObject對象來處理,然后使用轉(zhuǎn)換函數(shù)把python的數(shù)據(jù)類型轉(zhuǎn)換成c/c++中的類型帜羊,返回參數(shù)按相同方式處理。比第一種方法多了初始化函數(shù)鸠天,這部分是把編譯的so庫當(dāng)做python module所必需要做的讼育。python中可以這樣使用:

Html 代碼

1imoprt spam

2spam.system("ls")

使用c/c++編寫python擴展的介紹可以參見:http://docs.python.org/2.7/extending/extending.html

3)使用SWIG,來生成獨立的wrap文件

這種方式并不能算是一種新方式稠集,實際上是基于第二中方式的一種包裝奶段。SWIG是個幫助使用C或者C++編寫的軟件能與其它各種高級編程語言進行嵌入聯(lián)接的開發(fā)工具。SWIG能應(yīng)用于各種不同類型的語言包括常用腳本編譯語言例如Perl, PHP, Python, Tcl, Ruby, PHP剥纷,C#,Java,R等痹籍。

? ? ? ?操作上,是針對c/c++程序編寫?yīng)毩⒌慕涌诼暶魑募ㄍǔ:芎唵危┗扌瑂wig會分析c/c++源程序自動分析接口要如何包裝蹲缠。在指定目標(biāo)語言后,swig會生成額外的包裝源碼文件悠垛。編譯so庫時线定,把包裝文件一起編譯、連接即可确买〗锛ィ看個例子:

Cpp 代碼

1int?system(const?char* command)

2{

3????????sts =?system(command);

4????????if?(sts < 0) {

5????????????????return?NULL;

6????????}

7????????return?sts;

8}

? ? ? 將c源碼中去掉適配python的包裝,僅定義system函數(shù)本身湾趾。然后編寫接口聲明文件spam.i:

Python 代碼

1%module spam

2%{

3#include "spam.h"

4%}

5%include?"spam.h"

6%include?"typemaps.i"

7int?system(const?char* INPUT);

? ? ? 聲明要創(chuàng)建一個叫spam的模塊芭商,對system做一個聲明派草,主要是聲明參數(shù)作為入?yún)⑹褂谩H缓髨?zhí)行:

Cpp 代碼

#swig -c++ -python spam.i

? ? ? swig會生成spam_wrap.cxx和spam.py兩個文件铛楣。先看spam_wrap.cxx近迁,這個生成的文件很長,但關(guān)鍵的就是對函數(shù)的包裝:

? ? ? ?包裝函數(shù)傳入的還是PyObejct對象蛉艾,內(nèi)部進行了類型轉(zhuǎn)換钳踊,最終調(diào)了源碼中的system函數(shù)。

? ? ? ?生成的了另一個spam.py實際上是對so庫又用python包裝了一層(實際比較多余):

這里使用_spam模塊,這里實際上是把擴展命名為了_spam。關(guān)于swig在python上的應(yīng)用可以參見:http://www.swig.org/Doc1.3/Python.html

? ? ? ?下面就是編譯和安裝python 模塊巩步,Python提供了distutils module郑叠,可以很方便的編譯安裝python的module。像下面這樣寫一個安裝腳本setup.py:

執(zhí)行? python setup.py build叶撒,即可以完成編譯,程序會創(chuàng)建一個build目錄,下面有編譯好的so庫蛆橡。so庫放在當(dāng)前目錄下,其實Python就可以通過import來加載模塊了掘譬。當(dāng)然也可以用 python setup.py install 把模塊安裝到語言的擴展庫——site-packages目錄中泰演。關(guān)于build python擴展,可以參考https://docs.python.org/2/extending/building.html#building

3. 混合編程的性能分析

? ? ? ?混合編程的使用場景中葱轩,很重要一個就是性能攸關(guān)睦焕。那么這小節(jié)將通過幾個小實驗驗證下混合編程的性能如何,或者說怎樣寫程序能發(fā)揮好混合編程的性能優(yōu)勢靴拱。

我們使用冒泡排序算法來驗證性能垃喊。

1)實驗一? ? 使用冒泡程序驗證python和c/c++程序的性能差距

python版冒泡程序:

Cpp 代碼

01def?bubble(arr,length):

02????j?=?length?-?1

03????while?j >=?0:

04????????i?=?0

05????????while?i < j:

06????????????if?arr[i] > arr[i+1]:

07????????????????tmp?=?arr[i+1]

08????????????????arr[i+1]?=?arr[i]

09????????????????arr[i]?=?tmp

10????????????i?+=?1

11????????j?-=?1

c語言版冒泡程序:

Python 代碼

01void?bubble(int* arr,int?length){

02????int?j = length - 1;

03????int?i;

04????int?tmp;

05????while(j >= 0){

06????????i = 0;

07????????while(i < j){

08????????????if(arr[i] > arr[i+1]){

09????????????????tmp = arr[i+1];

10????????????????arr[i+1] = arr[i];

11????????????????arr[i] = tmp;

12????????????}

13????????????i += 1;

14????????}

15????????j -= 1;

16????}

17}

? ? ?使用一個長度為100內(nèi)容固定的數(shù)組,反復(fù)排序10000次(每次排序后袜炕,再把數(shù)組恢復(fù)成原始序列)本谜,記錄執(zhí)行時間:

在相同的機器上多次執(zhí)行,Python版執(zhí)行時間是10.3s左右偎窘,而c語言版本(未使用任何優(yōu)化編譯參數(shù))執(zhí)行時間只有0.29s左右乌助。相比之下python的性能的確差很多(主要是python中l(wèi)ist的操作跟c的數(shù)組相比,效率差非常多)陌知,但python中很多擴展都是c語言寫的眷茁,目的就是為了提升效率,python用于數(shù)據(jù)分析的numpy庫就擁有不錯的性能纵诞。下個實驗就驗證上祈,如果python使用c語言版本的冒泡排序擴展庫,性能會提升多少。

2)實驗二? ? Python使用c/c++擴展庫登刺,驗證性能提升

? ? ? ?我們還要驗證上面三種混合方式籽腕,各自效率怎樣。

??1.使用ctypes module

? ? ? ? ?這里直接使用c_int來定義了數(shù)組對象纸俭,這也節(jié)省了調(diào)用時數(shù)據(jù)類型轉(zhuǎn)換的開銷:

Python 代碼

01import?time

02from?ctypes?import?*

03IntArray100?=?c_int?*?100

04arr?=?IntArray100(87,23,41,?3,?2,?9,10,23,0,21,5,15,93,?6,19,24,18,56,11,80,34,?5,98,33,11,25,99,44,33,78,

05???????52,31,77,?5,22,47,87,67,46,83,?89,72,34,69,?4,67,97,83,23,47,?69,?8,?9,90,20,58,20,13,61,99,7,22,55,11,30,56,87,29,92,67,

06???????99,16,14,51,66,88,24,31,23,42,76,37,82,10,?8,?9,?2,17,84,32,66,77,32,17,?5,68,86,22,?1,?0)

07... ...

08if?__name__?==?"__main__":

09????libbubble?=?CDLL('libbubble.so')

10????time1?=?time.time()

11????for?i?in?xrange(100000):

12????????libbubble.initArr(arr1,arr,100)

13????????libbubble.bubble(arr1,100)

14????time2?=?time.time()

15????print?time2?-?time1

? ? ? 再次執(zhí)行:

為了減少誤差皇耗,把循環(huán)增加到10萬次,結(jié)果c原生程序執(zhí)行需要2.8s左右揍很,使用優(yōu)化參數(shù)編譯后用時0.65s左右郎楼。python使用c擴展后(相同編譯參數(shù))執(zhí)行僅需2.3s左右。

C 代碼

>gcc?-pthread?-fno-strict-aliasing?-g?-O2?-DNDEBUG?-g?-fwrapv?-O3?-Wall?-Wstrict-prototypes?-fPIC?bubble1.c?-o?bubble.o

?2.在c語言中使用PyObject處理入?yún)?/p>

? ? ? 這種方式是在python中依然使用list裝入待排序數(shù)列窒悔,在c函數(shù)中把list賦值給數(shù)組呜袁,再進行排序,排好序后简珠,再對原始list賦值阶界。循環(huán)排序10萬次,執(zhí)行用時1.0s左右聋庵。

?3.使用swig來包裝c方法

? ? ? 在接口文件中聲明%array_class(int,intArray);然后在Python中使用initArray來作為數(shù)組膘融,同樣修改成10萬次排序。python版本的程序(相同編譯參數(shù))執(zhí)行僅需0.7s左右祭玉,比c原生程序慢大概7%氧映。

? ? ? 【結(jié)論】

? ? ? ? 1.python 的list效率非常低,在高性能場景下避免對list大量循環(huán)脱货、取值岛都、賦值操作。如需要最好使用ctype中的數(shù)組蹭劈,或者是用c語言來實現(xiàn)。

? ? ? ? 2.應(yīng)該把耗時的cpu密集型的邏輯交給c/c++實現(xiàn)线召,python使用擴展即可铺韧。

?4.插件式框架設(shè)計

? ? ? ? 基于以上探索,參考NS3的實現(xiàn)方式缓淹,以數(shù)據(jù)收集Service為例來分析哈打。數(shù)據(jù)收集Service接收TCP/UDP的數(shù)據(jù)傳輸方式,應(yīng)用層協(xié)議支持自定義讯壶,不同的外設(shè)完全可以定義自己的協(xié)議解析邏輯料仗。我們定義每種解析為一個plugin,輸入為應(yīng)用層數(shù)據(jù)伏蚊,輸出為一個個Message立轧。轉(zhuǎn)換成Message之后要輸入給后續(xù)Plugin處理(可能是kafka,可能是ZeroMQ,也可能直接對接數(shù)據(jù)預(yù)處理Service)氛改,我們之定義這些最近的接口帐萎,輸入是一批Message,返回成功或失敗胜卤。

? ? ? ? 這些Plugin都可以使用c/c++實現(xiàn)疆导,因為這些數(shù)據(jù)處理使用指針會更快。

? ? ? ?主Service其實僅僅是**端口葛躏,對于tcp連接澈段,判斷該使用什么plugin來處理,然后新建一個線程舰攒,把socket交給這個線程败富。框架的公共程序要對socket等資源維護起來芒率,如支持線程池囤耳,資源回收,plugin熱替換時偶芍,可以將資源移交給新處理線程充择,實現(xiàn)無損升級等。像這些操作并不是性能攸關(guān)的匪蟀,就完全可以用python來實現(xiàn)椎麦。

? ? ? ? 以上可以應(yīng)用OOA、OOD的方法進一步分析設(shè)計材彪,然后使用c++來實現(xiàn)插件观挎。使用swig來包裝接口。使用時看起來像這個樣子:

-

Python 代碼

1import?epsn

2import?collector

3plug1?=?epsn.EpsnPlugin()

4mainService?=?collector.CollectServer()

5mainService.addPlugin(plug1)

6mainService.run()

?5.版本管理及依賴性管理

python有很成熟的包管理工具段化,可以方便地構(gòu)建嘁捷、安裝、移除显熏、升級雄嚣、版本管理、依賴性管理喘蟆。上文setup.py中的代碼就使用distutils包缓升,是python內(nèi)置的包管理庫,可以很方便地構(gòu)建包蕴轨。相對功能更強地有setpuptools支持依賴性的管理港谊,具體參見http://setuptools.readthedocs.io/en/latest/setuptools.html#building-and-distributing-packages-with-setuptools。文章不再做更多介紹橙弱。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末歧寺,一起剝皮案震驚了整個濱河市燥狰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌成福,老刑警劉巖碾局,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異奴艾,居然都是意外死亡净当,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門蕴潦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來像啼,“玉大人,你說我怎么就攤上這事潭苞『龆常” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵此疹,是天一觀的道長僧诚。 經(jīng)常有香客問我,道長蝗碎,這世上最難降的妖魔是什么湖笨? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮蹦骑,結(jié)果婚禮上慈省,老公的妹妹穿的比我還像新娘。我一直安慰自己眠菇,他們只是感情好边败,可當(dāng)我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著捎废,像睡著了一般笑窜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上登疗,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天排截,我揣著相機與錄音,去河邊找鬼谜叹。 笑死匾寝,一個胖子當(dāng)著我的面吹牛搬葬,可吹牛的內(nèi)容都是我干的荷腊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼急凰,長吁一口氣:“原來是場噩夢啊……” “哼女仰!你這毒婦竟也來了猜年?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤疾忍,失蹤者是張志新(化名)和其女友劉穎乔外,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體一罩,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡杨幼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了聂渊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片差购。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖汉嗽,靈堂內(nèi)的尸體忽然破棺而出欲逃,到底是詐尸還是另有隱情,我是刑警寧澤饼暑,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布稳析,位于F島的核電站,受9級特大地震影響弓叛,放射性物質(zhì)發(fā)生泄漏彰居。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一邪码、第九天 我趴在偏房一處隱蔽的房頂上張望裕菠。 院中可真熱鬧,春花似錦闭专、人聲如沸奴潘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽画髓。三九已至,卻和暖如春平委,著一層夾襖步出監(jiān)牢的瞬間奈虾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工廉赔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留肉微,地道東北人。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓蜡塌,卻偏偏與公主長得像碉纳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子馏艾,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容

  • TIOBE每個月都會新鮮出爐一份流行編程語言排行榜,這里會列出最流行的20種語言岖沛。排序說明不了語言的好壞鳖擒,反應(yīng)的不...
    Python編程社區(qū)閱讀 862評論 0 1
  • 今天我們在我妹妹家吃的晚飯,妹妹做了六個萊烫止,今天妹妹的親親母來這串門蒋荚,所以我們都聚在一起吃頓飯。
    心向陽光_d6d2閱讀 160評論 0 1
  • 2020的春節(jié)不平凡馆蠕,原來屬于春節(jié)的那份歡樂那份嬉笑那份團圓卻被新型冠狀病毒的到來‘奪走了’期升,也許這看起來...
    貓寧鴨閱讀 651評論 0 4
  • 存在重復(fù) 給定一個整數(shù)數(shù)組,判斷是否存在重復(fù)元素互躬。 如果任何值在數(shù)組中出現(xiàn)至少兩次播赁,函數(shù)返回 true。如果數(shù)組中...
    ngugg閱讀 143評論 0 0
  • 張清的日精進第529天 富人更愿意學(xué)習(xí)吼渡,而非娛樂容为,富人閱讀是為了獲取知識。 他們堅持鍛煉 他們結(jié)識其他成功的人做朋...
    kiyoi2017閱讀 119評論 0 0