摘要:數(shù)據(jù)類型變量用來存儲值,值可以被改變。數(shù)據(jù)的類型是各種各樣的,在小節(jié)中我們已經(jīng)看到非常普遍的數(shù)據(jù)類型數(shù)字,布爾值,字符串。在中有個內(nèi)置函數(shù),可以通過它來檢查一個對象的類型上面交互命令行中出現(xiàn)了四種簡單的數(shù)據(jù)類型,分別是,,,。
該系列文章:
《python入門,編程基礎(chǔ)概念介紹(變量,條件,函數(shù),循環(huán))》
《python中的數(shù)據(jù)類型(list,tuple,dict,set,None)》
《在python中創(chuàng)建對象(object)》
1.背景知識 1.1.簡化版python解釋器如何運行源代碼python解釋器主要包含了兩大部分,一個部分相當于編譯器,另一個部分相當于虛擬機。python解釋器的編譯器部分首先把程序編譯成中間碼(字節(jié)碼),再由python解釋器里的虛擬機部分(Python Virtual Machine (PVM))運行字節(jié)碼。
1.2.簡化版對象(object)與類(class)我們知道,給函數(shù)提供輸入(input),則函數(shù)會處理輸入(input),返回結(jié)果(output)或者不返回。程序就是解決問題的一系列步驟,這被稱為面向過程(Procedure Oriented) 的編程方式。后來,編程語言中出現(xiàn)了一種 面向?qū)ο螅∣bject Orientend) 的思想,簡單來說,對象借鑒了現(xiàn)實世界由一個個客體組成的概念,用一個個對象間的互動來組織起程序,跟現(xiàn)實世界的客體類似,對象有自己的特征(對象里的各種值),對象也有自己能夠做到的事(通過對象里的各種方法)。對象里的各種值被叫做對象的字段(field) ,對象里的各種方法被叫做對象的方法(method) ,對象的字段跟方法統(tǒng)稱為對象的屬性(attribute) 。
對象的字段類似于編程語言里的普通變量,所不同的是對象的字段是對象獨有的。如果一個對象叫a,a有個屬性是b,我們?nèi)绾卧L問b呢?答案是通過a.b這種形式的寫法訪問。a.b的意思就是a的屬性b。
對象的方法類似于編程語言中的函數(shù),所不同的是對象的方法是對象獨有的,如果一個對象叫c,c有個方法是d,我們?nèi)绾问褂茫ㄕ{(diào)用)d呢?答案是通過c.d()這種形式的寫法來是使用,d方法可以帶參數(shù),形如這樣:c.d(a)。c.d()的意思就是使用(調(diào)用)c的方法d。
如何實現(xiàn)對象呢?有一種方法就是通過類(class)來實現(xiàn)。對象對應(yīng)于現(xiàn)實中一個個具體的客體,這些客體各不相同,但是很明顯,有一些客體是可以歸到同一個陣營里去的,比如所有的人,所有的蘋果,所有的貓,這里的人、蘋果、貓是抽象的一般概念,程序中的類(class) 就是基于像這樣的一般概念而抽象出來的某一類客體的模板,可以是人的類,蘋果的類,貓的類。從類(模板)中可以構(gòu)造出這一類客體的對象。從類到對象,相當于從藍圖中實現(xiàn)了一個對象,所以可以說某對象是某個類的一個實例(實現(xiàn)了的例子)。反過來,某個類規(guī)定了將要實現(xiàn)的對象的該有的屬性跟方法,跟別的類實現(xiàn)的對象有了區(qū)別,所以對象的類型(type) 就是它所承襲的類。
1.3.簡化版調(diào)用棧(call stack),堆(heap)內(nèi)存(memory):是指計算中的隨機存取內(nèi)存(random access memory (RAM))??梢哉J為,內(nèi)存是一張很大的表,其中的每個表格(1個字節(jié))有兩個屬性:地址和值。地址表示了某個表格在內(nèi)存這張大表中的位置,因此我們可以通過地址找到該表格;一旦找到該表格,就可以對該表格的值進行擦除并重寫的操作,或者只是讀取該表格的值。具體的細節(jié)我們可以不用去考慮,我們需要更加抽象的思考地址和值,任意大小的一塊內(nèi)存都可以有一個(標識)地址來讓我們在整個內(nèi)存中找到它,該內(nèi)存塊中能存儲復(fù)雜的值。
程序要運行時,操作系統(tǒng)就會給它分配一塊可用的內(nèi)存,或者由某高級語言虛擬機提供運行時環(huán)境。在該內(nèi)存空間(運行時環(huán)境)里,首先會載入程序本身(機器碼或者字節(jié)碼),接下來會載入整個程序可以用的東西(全局(類)變量,模塊等),除此之外的內(nèi)存會劃分為兩種,一種是程序運行時的調(diào)用棧(call stack),另一種則是堆(heap)。在這里,內(nèi)存并非計算機中真實的物理內(nèi)存,而是由操作系統(tǒng)通過軟硬件結(jié)合的一種技術(shù)分配的一塊虛擬內(nèi)存,該虛擬內(nèi)存中的虛擬地址跟計算機中真實的物理內(nèi)存地址之間有著映射的關(guān)系。在這樣的虛擬內(nèi)存空間里,地址是連續(xù)的,也就是說程序運行在某一塊特定的虛擬內(nèi)存中,可以想象成一個長塊兒。
當程序運行時,主函數(shù)(main函數(shù))或者首先被執(zhí)行的函數(shù)(方法)會被放入到調(diào)用棧中,因為調(diào)用棧中只有這一幀,所以它處在調(diào)用棧的頂層,一旦處在調(diào)用棧的頂層,就會被激活,被激活的幀(frame)得到程序的執(zhí)行權(quán),該幀中的代碼被一步步執(zhí)行,我們把這一幀姑且叫第0幀吧。第0幀中調(diào)用另一個函數(shù)(方法)時,被調(diào)用函數(shù)(方法)的幀被放入到調(diào)用棧的最頂層,我們把這一幀叫第1幀,現(xiàn)在,被激活的幀就變成了第1幀,程序的執(zhí)行權(quán)轉(zhuǎn)移到第1幀中,它首先會把被調(diào)用時傳進來的參數(shù)(如果有)存儲,接著就聲明和初始化局部(實例)變量,操作變量……當第1幀調(diào)用另一個函數(shù)(方法)時,被調(diào)用函數(shù)(方法)的幀被放入到調(diào)用棧的最頂層,我們把這一幀叫第2幀,如前所述,第2幀被激活,得到程序執(zhí)行權(quán),當第2幀執(zhí)行結(jié)束,返回值(如果有),這時候第2幀的內(nèi)存被銷毀,包括其中的局部(實例)變量、參數(shù)等等,第2幀在調(diào)用棧中不在存在,這時候,第1幀成為調(diào)用棧的頂層,程序的執(zhí)行權(quán)又重新回到第1幀,第1幀繼續(xù)執(zhí)行剩余的代碼,當第1幀執(zhí)行結(jié)束,返回值(如果有),第1幀被銷毀,調(diào)用棧中最頂層的幀重新變成第0幀,如果第0幀執(zhí)行結(jié)束,則調(diào)用??瞻住@其中,被調(diào)用函數(shù)(方法)放入調(diào)用棧最頂層被稱為推入(push),而在調(diào)用棧中被銷毀則被稱為彈出(pop)。
調(diào)用棧(call stack)中的函數(shù)(方法)里存儲著程序運行過程中該如何做的行為(動作、指令)和需要處理的局部(實例)變量,這些變量實際上是怎么被存儲的呢?不同的編程語言有不同的存儲方式,實際上有兩種最為主流的做法。
我們從上篇文章《python入門,編程基礎(chǔ)概念介紹(變量,條件,函數(shù),循環(huán))》已經(jīng)知道,變量的值有各種各樣的類型。像簡單的數(shù)字,布爾值,字符串……,對于這些比較簡單的值,一種方法就是把它們看做原始(內(nèi)置)類型,直接在調(diào)用棧內(nèi)存中分配出一塊能容納該類型值的內(nèi)存,把該值存進去,通過變量名來使用該值。這時候變量仿佛就是這些值了。這種存儲類型又被稱為值類型(value type)。需要注意的是,靜態(tài)類型語言中的變量是有類型的,一旦聲明成某種類型的,則只能存儲該類型的值;動態(tài)類型語言中的變量不存在類型,可以存儲任何類型的值。
對于簡單的數(shù)字,布爾值,字符串……這些類型的值,另一種方法就是把它們都存在堆(heap)內(nèi)存空間里。在調(diào)用棧中的變量里,實際存儲的是堆(heap)內(nèi)存空間里的某一塊內(nèi)存的地址。當一個變量被聲明并且被賦值的時候發(fā)生了什么呢?實際上發(fā)生的是首先在堆(heap)內(nèi)存空間中分配出一塊能容納該類型值的內(nèi)存,把該值存進去;然后把該內(nèi)存的地址綁定到變量上。這時候我們就說該變量引用(reference) 了該值。這種存儲類型被稱為引用類型(reference type)。有時候也能夠看到這種說法:變量里存儲的是值的引用,可以看到,值的內(nèi)存地址跟值的引用的說法可以互換。
前面我們簡略介紹了對象,對象可以看做是種復(fù)雜類型的值,你可以想想,對象里的各種屬性和各種方法。那么當我們有一個對象的時候,或者有某個復(fù)雜類型的值的時候,不同的編程語言都不約而同的選擇了堆(heap)內(nèi)存空間。這是為什么呢?因為調(diào)用棧中的值需要的內(nèi)存塊的大小一旦確定就不能改變,而堆則沒有這個限制,有可能一個對象占用很大的一塊內(nèi)存,并且在程序運行過程中動態(tài)的變大或者變小。
2.數(shù)據(jù)類型變量用來存儲值,值可以被改變。正如我們之前好幾次說到的那樣,值的類型各種各樣,但都攜帶了某種信息,并且這種信息可以被操作(處理),我們可以認為,它們都是數(shù)據(jù)(data)。
數(shù)據(jù)的類型(type) 是各種各樣的,在1.3小節(jié)中我們已經(jīng)看到非常普遍的數(shù)據(jù)類型:數(shù)字,布爾值,字符串。對這些簡單的數(shù)據(jù)類型,我們已經(jīng)知道普遍存在的兩種存儲方式,那么在python中呢?在python中,一切數(shù)據(jù)類型都是對象,所以,我們存儲任何數(shù)據(jù)類型的方式都是通過引用(reference) 。
在python中有個內(nèi)置函數(shù)type(),可以通過它來檢查一個對象的類型:
>>> type(1)>>> type(1.1) >>> type("int") >>> type(True) >>>
上面交互命令行中出現(xiàn)了四種簡單的數(shù)據(jù)類型,分別是int,float,str,bool。如果兩個變量同時引用了相同的對象,會發(fā)生什么呢?在python中有個內(nèi)置的函數(shù)id(),這個函數(shù)返回對象的一個id,可以把該id看做該對象的引用(內(nèi)存地址)。讓我們看看兩個變量同時引用相同的對象時發(fā)生了什么:
>>> a=1 >>> b=1 >>> id(a) 255681632 >>> id(b) 255681632 >>>
可以看到,兩個變量指向了同一個對象,那么當我們改變了其中某個變量引用的對象,是不是另一個變量引用的對象也同時改變了呢?從理論上講會改變,對吧,因為是同一個對象。我們來看看:
>>> a=1 >>> b=1 >>> id(a) 255681632 >>> id(b) 255681632 >>> b=2 #改變b的值 >>> a 1 >>>
事實并不像我們認為的那樣,另一個變量的對象并沒有改變。這是因為,在python中,上面出現(xiàn)的這四種簡單的數(shù)據(jù)類型都是不可變(immutable) 對象。舉個數(shù)字的例子來理解這種不可變性:數(shù)字1是個對象,是個獨立的客體,看起來這個對象簡單到不能再簡單,我們無法改變它,如果將變量的引用從數(shù)字1改變成數(shù)字2,那么,已經(jīng)是另一個對象了,相當于是更新了變量的引用。
2.1.列表(list)直到現(xiàn)在,我們處理過的數(shù)據(jù)類型都很簡單,但是當我們想要存儲更為復(fù)雜多變的數(shù)據(jù),用我們目前知道的數(shù)據(jù)類型來存儲就會越來越繁瑣,容易出錯了。比如我們想要在程序里一次性處理包含6個數(shù)字的內(nèi)容(7,9,11,36,74,12),難道我們要給每個數(shù)字都提供一個變量名,并一一存儲嗎?我們還有更多選擇,在python中,它提供了一種叫列表(list) 的數(shù)據(jù)類型,這種數(shù)據(jù)類型像個容器,可以裝進去其他各種數(shù)據(jù)類型,甚至也可以將其他列表(list)嵌套進去。我們要把7,9,11,36,74,12放進一個列表(list)中,可以這么做:
#把這幾個數(shù)字放進列表,并賦值給變量x x=[7,9,11,36,74,12] #可以定義一個空列表 y=[] #使用內(nèi)置函數(shù)list()創(chuàng)建列表 a=list("abc") b=list()
現(xiàn)在把數(shù)字放進一個列表了,那么我們怎么拿出某個數(shù)字呢?跟字符串類似,列表中的元素組成了一串,每個元素在列表中都是有順序的。每個元素都被分配了一個位置索引(index)。我們可以通過特定的索引來訪問對應(yīng)的元素,根據(jù)慣例,索引總是從0開始。
x=[7,9,11,36,74,12] z=len(x) #從列表x中循環(huán)取出數(shù)字,并打印到命令行 for index in range(z): print(str(x[index]))
列表中的元素的值能不能改變呢?能不能增加或者減少元素?答案是可以的。我們說python中一切數(shù)據(jù)類型都是對象,列表也是對象,所有它有自己的專屬方法??梢酝ㄟ^列表的append()方法來增加元素,增加的元素被追加到列表結(jié)尾。刪除一個元素呢,可以通過del語句來刪除,也可以通過列表的remove()方法或者pop()方法來刪除。這里要注意,remove方法通過值來刪除,pop方法通過索引來刪除,并且remove方法沒有返回值,而pop方法則返回要刪除的值。如果我們不只想刪除某一個元素,而是想清空整個列表,則可以使用列表的clear()方法??聪旅妫?/p>
a=["change me",["first",1,2],2019,True] #以下注釋都根據(jù)慣例,從0開始計數(shù) #改變第0個元素的值 a[0]="changed" print("列表a: {}".format(a)) #改變第1個元素(list)中的第0個元素 a[1][0]=0 print("列表a: {}".format(a)) #增加一個元素 a.append(2019) print("列表a: {}".format(a)) #刪除一個元素,通過del語句 del a[0] print("列表a: {}".format(a)) #刪除一個元素,通過remove方法 a.remove(True) print("列表a: {}".format(a)) #刪除一個元素,通過pop方法,并將返回值賦給變量b b=a.pop(2) print("被刪除的元素是{}".format(b)) print("列表a: {}".format(a)) #清空列表 a.clear() print("列表a: {}".format(a))
以上代碼中,用到了str的format()方法,這種方法通過在字符串保留一對{},來讓format方法中的參數(shù)填充其中,參數(shù)可以是任意多個(需要與前面{}的數(shù)量一致),可以是各種數(shù)據(jù)類型。這個方法大大簡化了我們想把其他數(shù)據(jù)類型加入到字符串的過程。運行結(jié)果如下:
列表a: ["changed", ["first", 1, 2], 2019, True] 列表a: ["changed", [0, 1, 2], 2019, True] 列表a: ["changed", [0, 1, 2], 2019, True, 2019] 列表a: [[0, 1, 2], 2019, True, 2019] 列表a: [[0, 1, 2], 2019, 2019] 被刪除的元素是2019 列表a: [[0, 1, 2], 2019] 列表a: []
既然python中的數(shù)據(jù)類型都是對象,那么我們?nèi)绾闻袛鄡蓚€對象是否是同一個呢。答案用is操作符。比如我們想要判斷a對象與b對象是否同一,則可以通過a is b來判斷,其結(jié)果是布爾值。
我們看到,列表里的元素可以比一個多,字符串里的字符也可以比一個多,所以我們給這種其中的元素或者屬性可以比一個多的對象運用in操作符(查看某元素是否屬于該對象,這被稱為成員檢測),來提供給for循環(huán)語句或者別的語句,讓這些語句逐個訪問其中的元素或者屬性,這個行為可以稱為迭代。前面說到的內(nèi)置函數(shù)list()可接受的參數(shù)就是可以被迭代的對象。上篇文章中講到的for...in循環(huán)就是迭代的一個例子。
#in操作符的例子 >>> x="hello" >>> "h" in x True >>>
在講列表(list)之前,我們說到了不可變對象。那么列表的情況又是如何呢?我們把那個例子中的數(shù)據(jù)類型換成列表來看看:
a=["change me",["first",1,2],2019,True] #把同樣的引用賦值給變量b b=a #看看引用是否相同,是否對象是同一個 print(a is b) #通過變量b改變列表 b[0]="changed" #現(xiàn)在打印出變量a,看是否有變化 print(a)
以上代碼運行結(jié)果如下:
True ["changed", ["first", 1, 2], 2019, True]
從運行結(jié)果來看,列表是可以改變的,所以是可變(mutable)對象?,F(xiàn)在我們已經(jīng)把可變對象和不可變對象的行為差不多摸清楚了。講不可變對象的時候我們講解了一個數(shù)字對象的例子來幫助理解不可變對象,現(xiàn)在我們來通過字符串的例子來進一步說明,假如我們把一個字符串“string”的引用賦值給變量a,那么我們是不能對其中的字符來進行修改的,如下圖:
>>> a="string" >>> a[0]="a" Traceback (most recent call last): File "", line 1, in TypeError: "str" object does not support item assignment
但是對于變量a來說,它是完全可以更新的,可以把另一個對象的引用重新賦值給它:
>>> a="string" >>> a[0]="a" Traceback (most recent call last): File "", line 1, in TypeError: "str" object does not support item assignment >>> a="hello" >>> a "hello" >>>
事實上不可變對象無論是看著無法改變的,還是直覺上感覺可以改變的(比如字符串),都是python中規(guī)定好了的。所以不必糾結(jié)于直覺,我們要做的是記住哪些數(shù)據(jù)類型是可變的,而哪些數(shù)據(jù)類型又是不可變的。下面將介紹一種新的數(shù)據(jù)類型,它跟列表類似,最大的不同是,它是不可變的。它叫元組(tuple)。
2.2.元組(tuple)元組(tuple) 中可以裝進去其他各種數(shù)據(jù)類型,甚至也可以將其他元組(tuple)嵌套進去。元組(tuple)的元素有索引(index),可以通過索引訪問到。
#空元組 tuple1=() #一個元素的tuple,寫法特殊一點。如果在元素后面不加逗號 #則python解釋器會當成元素的數(shù)據(jù)類型,而不認為是個元組 tuple2=(2,) #另一個元組,它里面有個列表作為元素 tuple3=(2,"lalala",True,45,[4,5]) #使用內(nèi)置函數(shù)tuple()創(chuàng)建元組,該函參數(shù)接受可迭代的對象 a=tuple() b=tuple([1,2,3])
因為元組是不可變對象,所以它其中的元素是不能修改的。元素也不能增刪。但整個元組是可以通過del語句刪除的。但是當元組中的元素是可變對象時,比如元組中的某個元素是列表,那該列表能不能修改?因為該列表是可變對象,所以該列表中的元素是可以自然增刪修改的,但該列表因為是不可變對象中的元素,所以無法刪除,如下:
>>> d=(2, "lalala", True, 45, [5, 5]) >>> d[4][0]=88 >>> print(d) (2, "lalala", True, 45, [88, 5]) >>> del d[4] Traceback (most recent call last): File "", line 1, in TypeError: "tuple" object doesn"t support item deletion >>> del d[4][0] >>> print(d) (2, "lalala", True, 45, [5]) >>>
那么情況反過來呢?當可變對象中的元素是不可變對象時,比如當列表中的某元素是個元組,該元組是否能夠被修改?因為該元組是不可變對象,所以該元組中的元素不能被修改,但是該元組本身是可變對象的元素,所以可以被刪除,如下:
>>> e=["he!",0,(4,5,"last")] >>> e[2][0]=5 Traceback (most recent call last): File "2.3.序列(sequence)", line 1, in TypeError: "tuple" object does not support item assignment >>> del e[2][0] Traceback (most recent call last): File " ", line 1, in TypeError: "tuple" object doesn"t support item deletion >>> del e[2] >>> print(e) ["he!", 0] >>>
一個數(shù)據(jù)集合里面的元素根據(jù)放入的先后順序排成一串,這種形式的數(shù)據(jù)可以被稱為序列。字符串,列表,元組都有著類似序列的結(jié)構(gòu),所以也就有類似的行為,它們都可以被索引,都可以被迭代,都能夠通過索引訪問到其中的元素;不僅僅是能夠訪問到其中的某一個元素,還能訪問到其中的某幾個元素,這種同時訪問到好幾個元素的行為,稱為切片(也可以把被訪問的這些元素稱為數(shù)據(jù)的切片),因為這樣做相當于從整個數(shù)據(jù)序列中切下來一部分,如下:
x="string" y=[1,2,3,4,5,6] z=(7,8,9,10,11,12) #以下注釋都根據(jù)慣例,從0開始計數(shù) #打印出字符串的第3個字符 print(x[3]) #打印出字符串的倒數(shù)第2個字符 print(x[-2]) #打印出字符串的一部分,從第1個開始,到第3個字符,不包括第4個字符 print(x[1:4]) #打印出列表的倒數(shù)第3個元素 print(str(y[-3])) #打印出列表的一部分,從第1個元素開始,到第4個元素,不包括第5個元素 print(str(y[1:5])) #打印出列表的一部分,從第1個元素開始,直到結(jié)束 print(str(y[1:])) #打印出元組的倒數(shù)第4個元素 print(str(z[-4])) #打印出元組的一部分,從第1個元素開始,到第4個元素,不包括第5個元素 print(str(z[1:5])) #打印出列表的一部分,從第1個元素開始,直到結(jié)束 print(str(z[1:]))
運行如下:
i n tri 4 [2, 3, 4, 5] [2, 3, 4, 5, 6] 9 (8, 9, 10, 11) (8, 9, 10, 11, 12)
切片中可以設(shè)置步長,就是說可以設(shè)置隔著幾個元素的方式訪問數(shù)據(jù)的一部分,默認步長為1,如下:
>>> a=(1,2,3,4,5,6,7,8,9,10) >>> a[::1] (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) >>> a[::2] (1, 3, 5, 7, 9) >>>
當我們把數(shù)據(jù)的切片賦值給變量時會發(fā)生什么?一般來說,數(shù)據(jù)被切片部分會被復(fù)制一份副本,然后把副本的引用賦值給變量。但是,有個特殊情況,就是當數(shù)據(jù)的被切片部分是全部數(shù)據(jù)本身時,那該數(shù)據(jù)要看是可變對象還是不可變對象了。列表(可變對象)則會復(fù)制一份副本,然后把副本的引用賦值給變量,而字符串、元組(不可變對象)則會直接將該數(shù)據(jù)本身的引用直接賦值給變量,如下:
>>> a=[1,2,3,4] >>> b=a[:] #省略掉開頭跟結(jié)尾index則切片結(jié)果是數(shù)據(jù)本身 >>> id(a) 140046385947272 >>> id(b) 140046386409992 >>> c=(1,2,[2,3]) >>> d=c[:] >>> id(c) 140046385743768 >>> id(d) 140046385743768 >>>2.4.字典(dict)
前面我們介紹了序列,我們知道序列的索引隱含在數(shù)據(jù)類型里,不需要我們明確去定義,因為都是0,1,2,3,4……有順序的排列下去,當可變對象里的元素被刪除,保留下來的元素的索引會改變,因為序列里的索引永遠都是順序排列的數(shù)字,這些數(shù)字沒有跟元素綁定,而只是允許我們通過索引的數(shù)字來訪問該位置的元素。那如果我們想要自定義索引,用字符串,數(shù)字等來做索引,并希望這些索引能夠跟特定的元素綁定在一起,我們該怎么辦?更簡單的說,就是我們希望索引是獨特并且容易記住,有語義,我們該怎么辦?python給我們提供了一種數(shù)據(jù)類型字典(dict),可以勝任這樣的要求。我們把這樣的索引可以看作是鍵 (keys),它與要存儲的值綁定在一起,叫做鍵值對。
字典里存儲鍵值對兒,索引獨特,所以是沒有順序的。順序已經(jīng)無關(guān)緊要,我們只需要記住獨特的鍵就行了。如何創(chuàng)建字典,如何存儲鍵值對,看下面的例子:
>>> a={} #創(chuàng)建一個空字典 >>> type(a)>>> b={"id":4667,"name":"john"} #創(chuàng)建一個有兩對鍵值對的字典 >>> b["id"] #通過鍵"id"來獲得對應(yīng)的值 4667 >>> c=dict(id=4555,name="li") #通過內(nèi)置的dict函數(shù)創(chuàng)建字典 >>> print(c) {"id": 4555, "name": "li"} >>>
字典是可變對象。為了通過鍵來查找值,就需要字典鍵(keys)保持唯一性,如果鍵用了可變對象來存儲,會出現(xiàn)不可控因素,舉個例子,假如兩個鍵都是由列表來存儲,則一旦把這兩個列表修改相同,那么查找這兩個鍵所對應(yīng)的值時就會出現(xiàn)矛盾,所以鍵一定要用不可變對象來存儲,包括數(shù)字,布爾值,字符串,元組(需要元組中的元素不包含可變對象)。又因為字典是可變對象,所以字典中鍵值對里的值是可以改變的。如下:
>>> c={("id",):46678,"name":"john_ss"} >>> c[("id",)] 46678 >>> d=8 >>> e={d:8,"d":"8"} >>> e[d] 8 >>> e["d"] "8" >>> e[d]=123 >>> print(e) {8: 123, "d": "8"} >>>
在字典中,也可以用字典的get()方法通過鍵獲取值。如果要給字典里增加鍵值對,可以直接用方括號(下標)的方式增加,例如dict["key_word"]="some values"??梢杂米值涞膒op()方法來刪除鍵值對,要注意的是,pop()方法在刪除鍵值對的同時會返回要移除的鍵值對,把返回值賦給變量,變量就會得到被移除的鍵值對:pair=dict.pop("id")。如果我們不只想刪除某一個鍵值對,而是想清空整個字典,則可以使用字典的clear()方法。如果看下面的例子:
>>> a={1:1,2:2,3:3} >>> a.get(1) 1 >>> a["appended"]="ok,then!" >>> print(a) {1: 1, 2: 2, 3: 3, "appended": "ok,then!"} >>> a.pop(2) 2 >>> print(a) {1: 1, 3: 3, "appended": "ok,then!"} >>> a.clear() >>> print(a) {} >>>
當我們嘗試像迭代序列那樣直接迭代字典時,交互命令行顯示結(jié)果如下,明顯只迭代了鍵值對中的鍵(key):
>>> a={1: 1, 2: 2, 3: 3, "appended": "ok,then!"} >>> for item in a: ... print(item) ... 1 2 3 appended >>>
這時候字典中有兩個內(nèi)置方法可以協(xié)助來完成迭代,分別是items()跟keys()。items()返回字典中無序的鍵值對,keys()返回字典中無序的鍵(keys)。如下:
>>> a={1: 1, 2: 2, 3: 3, "appended": "ok,then!"} >>> for item in a.items(): ... print(item) ... (1, 1) #可以看到把鍵值對裝進了元組 (2, 2) (3, 3) ("appended", "ok,then!") >>> for key in a.keys(): ... print(key) ... 1 2 3 appended >>>2.5.集合(set),frozenset
前面我們介紹了字典,跟字典類似,在python里還有一種無序的數(shù)據(jù)類型:集合(set)?;旧希@兒的集合跟數(shù)學(xué)上的集合的概念是一樣的。其中的元素是無序的,并且每個元素都是唯一不可重復(fù)的。創(chuàng)建集合跟創(chuàng)建字典的符號一樣,都是花括號“{}”,所以當創(chuàng)建空集合時,會跟創(chuàng)建空字典的符號有沖突,所以python里“{}”表示創(chuàng)建空字典,而創(chuàng)建空集合只能用內(nèi)置函數(shù)set()來創(chuàng)建,如下:
>>> a={1,4,7,"string",("lalala",2,3,4)} #創(chuàng)建集合 >>> print(a) {1, 4, 7, "string", ("lalala", 2, 3, 4)} >>> b={} #空字典 >>> type(b)>>> c=set() #空集合 >>> type(c) >>>
集合是可變對象,但是它的元素則要求一定是不可變對象,根據(jù)集合的定義,每個元素都是唯一不可重復(fù),那么一旦元素是可變對象,則有了可重復(fù)的可能,比如元素中有兩個列表的話,通過一些操作有可能會讓這兩個列表成為一樣的。如下:
>>> d={1,2,[1,2]} Traceback (most recent call last): File "", line 1, in TypeError: unhashable type: "list" >>>
既然集合是可變對象,我們就可以通過add()方法增加元素,通過remove()、discard()方法、pop()方法刪除元素,前提是所有元素都要是不同的,通過pop()方法刪除元素時,不帶參數(shù),并且集合無序,所以無法預(yù)知哪個元素被丟掉,但pop()方法會返回被丟的元素:
>>> a={1,4,7,("string",55),"hello"} >>> a.add("hello,world!") #add方法新增元素 >>> print(a) {1, 4, 7, "hello,world!", ("string", 55), "hello"} >>> a.remove(7) #remove方法刪除元素 >>> print(a) {1, 4, "hello,world!", ("string", 55), "hello"} >>> a.discard(1) #discard方法刪除元素 >>> print(a) {4, "hello,world!", ("string", 55), "hello"} >>> a.pop() #通過pop方法刪除隨機某個元素并返回被刪除的元素,可以使用變量來存儲 4 >>> print(a) {"hello,world!", ("string", 55), "hello"}
可以使用update方法來增加多個元素,使用clear()方法來清空整個集合,接上面的例子:
>>> a.update([1,2,3,4]) #用列表來更新集合 >>> a.update((5,6,7)) #用元組來更新集合 >>> print(a) {1, 2, 3, 4, 5, 6, 7, "hello,world!", ("string", 55), "hello"} >>> a.clear() >>> print(a) set() #代表空集合
集合可以實現(xiàn)數(shù)學(xué)上的并集,交集,差集,對稱差,有兩種方式:操作符跟方法。并集是兩個集合中所有的元素組成的新集合,交集是兩個集合中都存在的元素組成的新集合。差集是兩個集合中在某個集合中存在,并且在另一個集合中不存在的元素組成的新集合。對稱差集是兩個集合中所有元素,除去在交集中的元素,由這樣的元素組成的新集合。下面這個表顯示python的操作方法:
操作名稱 | 操作符 | 集合(比如A,B)內(nèi)置方法 |
---|---|---|
并 | | | A.union(B) |
交 | & | A.intersection(B) |
差 | - | A.difference(B) |
對稱差 | ^ | A.symmetric_difference(B) |
通過例子來看看:
>>> A={1,2,4,6,7} >>> B={1,3,4,5,8} >>> print(A | B) {1, 2, 3, 4, 5, 6, 7, 8} >>> print(A.union(B)) {1, 2, 3, 4, 5, 6, 7, 8} >>> print(A & B) {1, 4} >>> print(A.intersection(B)) {1, 4} >>> print(A - B) {2, 6, 7} >>> print(A.difference(B)) {2, 6, 7} >>> print(B - A) {8, 3, 5} >>> print(B.difference(A)) {8, 3, 5} >>> print(A ^ B) {2, 3, 5, 6, 7, 8} >>> print(A.symmetric_difference(B)) {2, 3, 5, 6, 7, 8} >>>
從上面的例子可以看出來,兩個集合的并集,交集,對稱差集都是可以互換的,而差集不是,差集需要區(qū)分A-B跟B-A。
在python中,還可以創(chuàng)建類似于tuple這樣的不可變對象的set,那就是frozenset,frozen在英文中是凍結(jié)了的意思,顧名思義,frozenset就是凍結(jié)的集合。frozenset不能增加或者更新元素,刪除或者清除元素,類似于只讀文件。并集,交集,差集,對稱差的操作對于frozenset同樣適用。如下:
>>> a=frozenset([1,2,3]) #創(chuàng)建一個frozenset >>> print(a) frozenset({1, 2, 3}) >>> a.add(4) Traceback (most recent call last): File "2.6.簡化內(nèi)置的help函數(shù),獲取python中各數(shù)據(jù)類型內(nèi)置的方法及其簡要說明", line 1, in AttributeError: "frozenset" object has no attribute "add" >>> a.clear() Traceback (most recent call last): File " ", line 1, in AttributeError: "frozenset" object has no attribute "clear" >>> b=frozenset((4,5,3)) >>> a.union(b) frozenset({1, 2, 3, 4, 5}) >>>
在python中,有個help()內(nèi)置函數(shù)可以獲取關(guān)于對象的說明文檔。但說明文檔中有關(guān)于該對象實現(xiàn)的細節(jié),當我們只是想知道各數(shù)據(jù)類型的常用內(nèi)置方法及其使用時,就會變得很不方便。我寫了一個很短的程序,運行該程序可以將help函數(shù)的輸出簡化(刪去實現(xiàn)對象的相關(guān)細節(jié))并存入當前目錄新建的一個文本文件中,并且該程序還有將方法的解釋翻譯成中文的可選功能。下面是該程序內(nèi)容:
#!/usr/bin/env python # -*- coding: utf-8 -*- import os import sys import re #如果想將解釋翻譯成中文,把下面一行的注釋取消 #from translate import Translator def command_line_arguments(usge,len_of_arguments): if len(sys.argv)!=len_of_arguments+1: print(usge) return sys.argv def stdout_to_file(dir,func,func_arguments): #把函數(shù)的標準輸出存入文件中 default_stdout = sys.stdout with open(dir,"w") as w: sys.stdout=w func(func_arguments) sys.stdout=default_stdout def simpler_help(object): stdout_to_file("simpler_help_{}.txt".format(object),help,object) #把文件中的內(nèi)容分行存入內(nèi)存 with open("simpler_help_{}.txt".format(object),"r") as r: lines=r.readlines() #將內(nèi)存中的內(nèi)容修改后寫入同名文件,覆蓋原有文件 #mark1,為了將帶有“__”的特殊函數(shù)不要寫入,并將其下面的解釋一并略過 mark1=False #mark2,為了將“---”下面的內(nèi)容略過 mark2=False with open("simpler_help_{}.txt".format(object),"w") as w: for line in lines: #將帶有“__”的特殊方法不要寫入,并將其下面的解釋一并略過 if "__" in line: mark1=True continue elif mark1==True: result=re.search("[A-Za-z]+",line) if result==None: mark1=False continue #將“---”下面的內(nèi)容略過 elif "---" in line: mark2=True continue elif mark2==True: pass #將行中的self跟“/”替換成空 else: if ("self," in line) or ("/" in line): if ("self," in line) and ("/" in line): w.write(line.replace("self,","").replace("/","").replace(", )"," )").replace(" , "," ")) elif ("self," in line) and ("/" not in line): w.write(line.replace("self,","")) elif ("self," not in line) and ("/" in line): w.write(line.replace("/","").replace(", )"," )").replace(" , "," ")) else: w.write(line) #如果想將解釋翻譯成中文,可以把下面的函數(shù)注釋取消 """ def translation_of_help(object): translator= Translator(to_lang="zh") with open("simpler_help_{}_zh.txt".format(object),"r") as r: lines=r.readlines() with open("simpler_help_{}_zh.txt".format(object),"w") as w: line_process="" count_line=0 for line in lines: if ("(" in line) and (")"in line) and ("." not in line): w.write(line) else: if count_line<=1: w.write(translator.translate(line)+" ") else: line_process=line.replace("|","") w.write(" | "+translator.translate(line_process)+" ") count_line=count_line+1 """ len_of_arguments=1 arguments=command_line_arguments("""You need provide a name of object,for example: python3 simpler_help.py list""",len_of_arguments) if len(arguments)==len_of_arguments+1: simpler_help(arguments[1]) #如果想將解釋翻譯成中文,可以把下面一行的注釋取消 #translation_of_help("list")
將上面內(nèi)容復(fù)制粘貼,并命名為simpler_help.py。舉個例子,如果想知道列表中的內(nèi)置方法,則在命令行中通過類似這樣的python3 simpler_help.py list命令來運行,特別注意需要在文件名空一格后寫上需要查詢的數(shù)據(jù)類型名稱。下面是程序創(chuàng)建的簡化版的列表的help函數(shù)輸出,保存在當前目錄的simpler_help_list.txt中:
Help on class list in module builtins: class list(object) | list(iterable=() ) | | Built-in mutable sequence. | | If no argument is given, the constructor creates a new empty list. | The argument must be an iterable if specified. | | Methods defined here: | | append( object ) | Append object to the end of the list. | | clear( ) | Remove all items from list. | | copy( ) | Return a shallow copy of the list. | | count( value ) | Return number of occurrences of value. | | extend( iterable ) | Extend list by appending elements from the iterable. | | index( value, start=0, stop=9223372036854775807 ) | Return first index of value. | | Raises ValueError if the value is not present. | | insert( index, object ) | Insert object before index. | | pop( index=-1 ) | Remove and return item at index (default last). | | Raises IndexError if list is empty or index is out of range. | | remove( value ) | Remove first occurrence of value. | | Raises ValueError if the value is not present. | | reverse( ) | Reverse *IN PLACE*. | | sort( *, key=None, reverse=False) | Stable sort *IN PLACE*. |
通過程序創(chuàng)建的文件,可以大概了解各數(shù)據(jù)類型都有哪些內(nèi)置方法,之后可以從網(wǎng)上通過關(guān)鍵詞搜索出更為詳細的介紹跟用例。另外,如果想將方法的解釋翻譯成中文,把simpler_help.py中相關(guān)內(nèi)容的注釋取消,并且首先需要在命令行中執(zhí)行pip install translate。
2.7.關(guān)于None>>> type(None)>>>
None屬于NoneType這個數(shù)據(jù)類型(對象),是它唯一的值。并且,NoneType對象并不像別的數(shù)據(jù)類型一樣,可以有很多實例,而是從始至終都只能有一個實例。
>>> None==False False >>> None==1 False >>> None=="" False >>> None==0 False >>> None=={} False >>> None==None True >>>
從上面的例子(做邏輯判斷)可以看出來,None不等于其他任何值,None只等于None。None一般用來代表數(shù)據(jù)的缺失,看下面的例子:
def addTwo(a,b): if (type(a)==int or type(a)==float) and ((type(b)==int or type(b)==float)): return a+b else: return None b=addTwo("ss",4) print(b) #OUTPUT應(yīng)該為None,None代表函數(shù)沒法合理的處理參數(shù),只能返回None。
當我們拿到某個數(shù)據(jù),我們想要判斷它不是None的時候該如何做呢,這時候應(yīng)該用is,因為None是數(shù)據(jù)類型,也是對象,所以我們一般會想要這樣做:if an_object is not None:。此處not的位置跟成員檢測if an_object not in an_object:的位置不同,需要特別注意。如下:
a=1 if a is not None: print("a is not None") else: print("a is None") #OUTPUT應(yīng)該為"a is not None"。
有些時候我們會在別人的代碼中看到if an_object:這樣的寫法,這種寫法跟上面的判斷某數(shù)據(jù)(對象)是不是None的寫法沒有什么聯(lián)系,這種代碼暗示了數(shù)據(jù)(對象)本身在做邏輯判斷時會另外呈現(xiàn)出或真或假的布爾值,事實上也的確如此,在各種數(shù)據(jù)類型中,會有某些特殊的值在邏輯判斷的時候布爾值表現(xiàn)為False,其余值表現(xiàn)為True。在python中,規(guī)定了一套規(guī)則,當我們拿某個數(shù)據(jù)本身來做邏輯判斷的時候,解釋器如何解確定其布爾值:
布爾型,F(xiàn)alse表示False,其他為True
整數(shù)和浮點數(shù),0表示False,其他為True
字符串和類字符串類型(包括bytes和unicode),空字符串表示False,其他為True
序列類型(包括tuple,list,dict,set等),空表示False,非空表示True
None永遠表示False
舉個例子,當我們在理論上來說從某函數(shù)(方法)返回了一個列表list1,我們用if list1:來判斷的時候,則會出現(xiàn)三種情況:list1是None,list1是空列表,list1是非空列表,這時候list1在第一和第二種情況下表現(xiàn)出的布爾值為False,在第三種情況下表現(xiàn)出的布爾值為True。如下:
list1=[] while True: if list1: print("true") else: if list1 != None: print("false,empty list") list1=None else: print("false,None") list1=[] #OUTPUT應(yīng)該永遠在None跟空列表之間徘徊。交替打印“false,empty list”和“false,None”。
[歡迎瀏覽我的個人博客,https://diwugebingren.github.io
](https://diwugebingren.github....
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/43950.html
摘要:聲明一個很簡單,只需盡量不要將變量名起為關(guān)鍵字。有如下基本方法在的末尾添加一個元素,并且返回將另一個的對象添加到尾部,返回值為。返回值為刪掉的元素。為一個表達式,傳入當前元素,返回時依據(jù)德關(guān)鍵字。 1.list 聲明一個list很簡單,只需list1=[](盡量不要將變量名起為關(guān)鍵字list)。list有如下基本方法: (1)append(x) 在list的末尾添加一個元素x,并且返回...
摘要:元組本質(zhì)是一種有序集合和相似元組是不可變的用創(chuàng)建其中類型可以不同如果一個元組中只有一個元素,在這個元素后面加一個逗號來消除歧義修改元組表面上看,的元素改變了,但實際上變得不是的元素,是的元素刪除被直接刪除數(shù)據(jù)類型轉(zhuǎn)換字典字典中的必須唯一值必 元組 tuple 本質(zhì)是一種有序集合 和list相似 元組是不可變的 用()創(chuàng)建 其中類型可以不同 如果一個元組中只有一個元素,在這個元素后面加...
摘要:元素的獲取與一致,即即可。中的是唯一的不可變對象。若不存在該則返回。是一個有序且不重復(fù)的數(shù)據(jù)集合。創(chuàng)建創(chuàng)建時重復(fù)的元素將被自動刪除。添加元素刪除元素若元素不存在,則會報錯誤。 Python的數(shù)據(jù)集合有四種,即list,tuple,dict,set 列表,List List是Python中一種有序的可變的數(shù)據(jù)集合。它的元素可以被添加或則刪除。List的表示方法是用一個[]將元素包含起來,...
摘要:精簡,快速,并持續(xù)完善。布爾類型一個布爾值只有兩種值,可以用和運算中的從左到右計算表達式,若所有值均為真,則返回最后一個值,若存在假,返回第一個假值。浮點型在運算中,整數(shù)與浮點數(shù)運算的結(jié)果是浮點數(shù)。 精簡,快速,并持續(xù)完善。 輸入與輸出 >>> print(Name: %s, score: %d % (Jack, 89)) Name: Jack, score: 89 >>> prin...
摘要:本文是通過廖雪峰的網(wǎng)站學(xué)習(xí)而整理的真的是很好的教程,省得我花錢買書了,然后我沒有去再整理總結(jié)語法,而是直接通過寫出代碼段來體現(xiàn)自己的學(xué)習(xí),也方便以后的快速復(fù)習(xí)回顧。 不想再像以前那樣,什么都從頭開始學(xué)習(xí)語法、總結(jié)語法,這樣反而會過分糾結(jié)于語法,耽誤了開發(fā),畢竟語言的主要屬性是工具,次要的屬性是語言本身。 所以還是先熟練使用語言去進行開發(fā),等足夠熟悉了,再去研究語言本身(編譯原理……)。...
摘要:的強大之處在于數(shù)據(jù)操作的方便和大類的第三方本文主要是學(xué)習(xí)收集數(shù)據(jù)操作相關(guān)的內(nèi)容相關(guān)內(nèi)容通過代碼對基本內(nèi)容進行學(xué)習(xí)這是一個空的注意這不是一個變量,而是一個類型的變量注意與上面的例子對比,這里在括號內(nèi)加了一個逗號,變量類型為變量包含多個元素,元 0:Python的強大之處在于數(shù)據(jù)操作的方便和大類的第三方module,本文主要是學(xué)習(xí)收集數(shù)據(jù)操作相關(guān)的內(nèi)容 1:Tuple相關(guān)內(nèi)容 #通過代碼對...
閱讀 3369·2019-08-29 16:17
閱讀 2007·2019-08-29 15:31
閱讀 2679·2019-08-29 14:09
閱讀 2582·2019-08-26 13:52
閱讀 771·2019-08-26 12:21
閱讀 2175·2019-08-26 12:08
閱讀 1032·2019-08-23 17:08
閱讀 1961·2019-08-23 16:59