python中的數據類型(list,tuple,dict,set,None)

該系列文章:python

1.背景知識

1.1.簡化版python解釋器如何運行源代碼

python解釋器主要包含了兩大部分,一個部分至關於編譯器,另外一個部分至關於虛擬機。python解釋器的編譯器部分首先把程序編譯成中間碼(字節碼),再由python解釋器裏的虛擬機部分(Python Virtual Machine (PVM))運行字節碼。git

1.2.簡化版對象(object)與類(class)

咱們知道,給函數提供輸入(input),則函數會處理輸入(input),返回結果(output)或者不返回。程序就是解決問題的一系列步驟,這被稱爲面向過程(Procedure Oriented) 的編程方式。後來,編程語言中出現了一種 面向對象(Object Orientend) 的思想,簡單來講,對象借鑑了現實世界由一個個客體組成的概念,用一個個對象間的互動來組織起程序,跟現實世界的客體相似,對象有本身的特徵(對象裏的各類值),對象也有本身可以作到的事(經過對象裏的各類方法)。對象裏的各類值被叫作對象的字段(field) ,對象裏的各類方法被叫作對象的方法(method) ,對象的字段跟方法統稱爲對象的屬性(attribute)github

對象的字段相似於編程語言裏的普通變量,所不一樣的是對象的字段是對象獨有的。若是一個對象叫a,a有個屬性是b,咱們如何訪問b呢?答案是經過a.b這種形式的寫法訪問。a.b的意思就是a的屬性b編程

對象的方法相似於編程語言中的函數,所不一樣的是對象的方法是對象獨有的,若是一個對象叫cc有個方法是d,咱們如何使用(調用)d呢?答案是經過c.d()這種形式的寫法來是使用,d方法能夠帶參數,形如這樣:c.d(a)c.d()的意思就是使用(調用)c的方法dapp

如何實現對象呢?有一種方法就是經過類(class)來實現。對象對應於現實中一個個具體的客體,這些客體各不相同,可是很明顯,有一些客體是能夠歸到同一個陣營裏去的,好比全部的人,全部的蘋果,全部的貓,這裏的人、蘋果、貓是抽象的通常概念,程序中的類(class) 就是基於像這樣的通常概念而抽象出來的某一類客體的模板,能夠是人的類,蘋果的類,貓的類。從類(模板)中能夠構造出這一類客體的對象。從類到對象,至關於從藍圖中實現了一個對象,因此能夠說某對象是某個類的一個實例(實現了的例子)。反過來,某個類規定了將要實現的對象的該有的屬性跟方法,跟別的類實現的對象有了區別,因此對象的類型(type) 就是它所承襲的類。dom

1.3.簡化版調用棧(call stack),堆(heap)

內存(memory):是指計算中的隨機存取內存(random access memory (RAM))。能夠認爲,內存是一張很大的表,其中的每一個表格(1個字節)有兩個屬性:地址和值。地址表示了某個表格在內存這張大表中的位置,所以咱們能夠經過地址找到該表格;一旦找到該表格,就能夠對該表格的值進行擦除並重寫的操做,或者只是讀取該表格的值。具體的細節咱們能夠不用去考慮,咱們須要更加抽象的思考地址和值,任意大小的一塊內存均可以有一個(標識)地址來讓咱們在整個內存中找到它,該內存塊中能存儲複雜的值。編程語言

程序要運行時,操做系統就會給它分配一塊可用的內存,或者由某高級語言虛擬機提供運行時環境。在該內存空間(運行時環境)裏,首先會載入程序自己(機器碼或者字節碼),接下來會載入整個程序能夠用的東西(全局(類)變量,模塊等),除此以外的內存會劃分爲兩種,一種是程序運行時的調用棧(call stack),另外一種則是堆(heap)。在這裏,內存並不是計算機中真實的物理內存,而是由操做系統經過軟硬件結合的一種技術分配的一塊虛擬內存,該虛擬內存中的虛擬地址跟計算機中真實的物理內存地址之間有着映射的關係。在這樣的虛擬內存空間裏,地址是連續的,也就是說程序運行在某一塊特定的虛擬內存中,能夠想象成一個長塊兒。
程序運行的內存塊ide

當程序運行時,主函數(main函數)或者首先被執行的函數(方法)會被放入到調用棧中,由於調用棧中只有這一幀,因此它處在調用棧的頂層,一旦處在調用棧的頂層,就會被激活,被激活的幀(frame)獲得程序的執行權,該幀中的代碼被一步步執行,咱們把這一幀姑且叫第0幀吧。第0幀中調用另外一個函數(方法)時,被調用函數(方法)的幀被放入到調用棧的最頂層,咱們把這一幀叫第1幀,如今,被激活的幀就變成了第1幀,程序的執行權轉移到第1幀中,它首先會把被調用時傳進來的參數(若是有)存儲,接着就聲明和初始化局部(實例)變量,操做變量……當第1幀調用另外一個函數(方法)時,被調用函數(方法)的幀被放入到調用棧的最頂層,咱們把這一幀叫第2幀,如前所述,第2幀被激活,獲得程序執行權,當第2幀執行結束,返回值(若是有),這時候第2幀的內存被銷燬,包括其中的局部(實例)變量、參數等等,第2幀在調用棧中不在存在,這時候,第1幀成爲調用棧的頂層,程序的執行權又從新回到第1幀第1幀繼續執行剩餘的代碼,當第1幀執行結束,返回值(若是有),第1幀被銷燬,調用棧中最頂層的幀從新變成第0幀,若是第0幀執行結束,則調用棧空白……這其中,被調用函數(方法)放入調用棧最頂層被稱爲推入(push),而在調用棧中被銷燬則被稱爲彈出(pop)
image函數

調用棧(call stack)中的函數(方法)裏存儲着程序運行過程當中該如何作的行爲(動做、指令)和須要處理的局部(實例)變量,這些變量其實是怎麼被存儲的呢?不一樣的編程語言有不一樣的存儲方式,實際上有兩種最爲主流的作法。post

咱們從上篇文章《python入門,編程基礎概念介紹(變量,條件,函數,循環)》已經知道,變量的值有各類各樣的類型。像簡單的數字,布爾值,字符串……,對於這些比較簡單的值,一種方法就是把它們看作原始(內置)類型,直接在調用棧內存中分配出一塊能容納該類型值的內存,把該值存進去,經過變量名來使用該值。這時候變量彷彿就是這些值了。這種存儲類型又被稱爲值類型(value type)。須要注意的是,靜態類型語言中的變量是有類型的,一旦聲明成某種類型的,則只能存儲該類型的值;動態類型語言中的變量不存在類型,能夠存儲任何類型的值。
image

對於簡單的數字,布爾值,字符串……這些類型的值,另外一種方法就是把它們都存在堆(heap)內存空間裏。在調用棧中的變量裏,實際存儲的是堆(heap)內存空間裏的某一塊內存的地址。當一個變量被聲明而且被賦值的時候發生了什麼呢?實際上發生的是首先在堆(heap)內存空間中分配出一塊能容納該類型值的內存,把該值存進去;而後把該內存的地址綁定到變量上。這時候咱們就說該變量引用(reference) 了該值。這種存儲類型被稱爲引用類型(reference type)。有時候也可以看到這種說法:變量裏存儲的是值的引用,能夠看到,值的內存地址跟值的引用的說法能夠互換。
image

前面咱們簡略介紹了對象,對象能夠看作是種複雜類型的值,你能夠想一想,對象裏的各類屬性和各類方法。那麼當咱們有一個對象的時候,或者有某個複雜類型的值的時候,不一樣的編程語言都不約而同的選擇了堆(heap)內存空間。這是爲何呢?由於調用棧中的值須要的內存塊的大小一旦肯定就不能改變,而堆則沒有這個限制,有可能一個對象佔用很大的一塊內存,而且在程序運行過程當中動態的變大或者變小。

2.數據類型

變量用來存儲值,值能夠被改變。正如咱們以前好幾回說到的那樣,值的類型各類各樣,但都攜帶了某種信息,而且這種信息能夠被操做(處理),咱們能夠認爲,它們都是數據(data)

數據的類型(type) 是各類各樣的,在1.3小節中咱們已經看到很是廣泛的數據類型:數字,布爾值,字符串。對這些簡單的數據類型,咱們已經知道廣泛存在的兩種存儲方式,那麼在python中呢?在python中,一切數據類型都是對象,因此,咱們存儲任何數據類型的方式都是經過引用(reference)

在python中有個內置函數type(),能夠經過它來檢查一個對象的類型:

>>> type(1)
<class 'int'>
>>> type(1.1)
<class 'float'>
>>> type("int")
<class 'str'>
>>> type(True)
<class 'bool'>
>>>

上面交互命令行中出現了四種簡單的數據類型,分別是int,float,str,bool。若是兩個變量同時引用了相同的對象,會發生什麼呢?在python中有個內置的函數id(),這個函數返回對象的一個id,能夠把該id看作該對象的引用(內存地址)。讓咱們看看兩個變量同時引用相同的對象時發生了什麼:

>>> 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中,上面出現的這四種簡單的數據類型都是不可變(immutable) 對象。舉個數字的例子來理解這種不可變性:數字1是個對象,是個獨立的客體,看起來這個對象簡單到不能再簡單,咱們沒法改變它,若是將變量的引用從數字1改變成數字2,那麼,已是另外一個對象了,至關因而更新了變量的引用。

2.1.列表(list)

直到如今,咱們處理過的數據類型都很簡單,可是當咱們想要存儲更爲複雜多變的數據,用咱們目前知道的數據類型來存儲就會愈來愈繁瑣,容易出錯了。好比咱們想要在程序裏一次性處理包含6個數字的內容(7,9,11,36,74,12),難道咱們要給每一個數字都提供一個變量名,並一一存儲嗎?咱們還有更多選擇,在python中,它提供了一種叫列表(list) 的數據類型,這種數據類型像個容器,能夠裝進去其餘各類數據類型,甚至也能夠將其餘列表(list)嵌套進去。咱們要把7,9,11,36,74,12放進一個列表(list)中,能夠這麼作:

#把這幾個數字放進列表,並賦值給變量x
x=[7,9,11,36,74,12]  
#能夠定義一個空列表
y=[]
#使用內置函數list()建立列表
a=list("abc")
b=list()

如今把數字放進一個列表了,那麼咱們怎麼拿出某個數字呢?跟字符串相似,列表中的元素組成了一串,每一個元素在列表中都是有順序的。每一個元素都被分配了一個位置索引(index)。咱們能夠經過特定的索引來訪問對應的元素,根據慣例,索引老是從0開始。

x=[7,9,11,36,74,12]
z=len(x)
#從列表x中循環取出數字,並打印到命令行
for index in range(z):
    print(str(x[index]))

列表中的元素的值能不能改變呢?能不能增長或者減小元素?答案是能夠的。咱們說python中一切數據類型都是對象,列表也是對象,全部它有本身的專屬方法。能夠經過列表的append()方法來增長元素,增長的元素被追加到列表結尾。刪除一個元素呢,能夠經過del語句來刪除,也能夠經過列表的remove()方法或者pop()方法來刪除。這裏要注意,remove方法經過值來刪除,pop方法經過索引來刪除,而且remove方法沒有返回值,而pop方法則返回要刪除的值。若是咱們不僅想刪除某一個元素,而是想清空整個列表,則可使用列表的clear()方法。看下面:

a=['change me',['first',1,2],2019,True]
#如下注釋都根據慣例,從0開始計數
#改變第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方法中的參數填充其中,參數能夠是任意多個(須要與前面{}的數量一致),能夠是各類數據類型。這個方法大大簡化了咱們想把其餘數據類型加入到字符串的過程。運行結果以下:

列表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中的數據類型都是對象,那麼咱們如何判斷兩個對象是不是同一個呢。答案用is操做符。好比咱們想要判斷a對象與b對象是否同一,則能夠經過a is b來判斷,其結果是布爾值。

咱們看到,列表裏的元素能夠比一個多,字符串裏的字符也能夠比一個多,因此咱們給這種其中的元素或者屬性能夠比一個多的對象運用in操做符(查看某元素是否屬於該對象,這被稱爲成員檢測),來提供給for循環語句或者別的語句,讓這些語句逐個訪問其中的元素或者屬性,這個行爲能夠稱爲迭代。前面說到的內置函數list()可接受的參數就是能夠被迭代的對象。上篇文章中講到的for...in循環就是迭代的一個例子。

#in操做符的例子
>>> x='hello'
>>> 'h' in x
True
>>>

在講列表(list)以前,咱們說到了不可變對象。那麼列表的狀況又是如何呢?咱們把那個例子中的數據類型換成列表來看看:

a=['change me',['first',1,2],2019,True]
#把一樣的引用賦值給變量b
b=a
#看看引用是否相同,是否對象是同一個
print(a is b)
#經過變量b改變列表
b[0]='changed'
#如今打印出變量a,看是否有變化
print(a)

以上代碼運行結果以下:

True
['changed', ['first', 1, 2], 2019, True]

從運行結果來看,列表是能夠改變的,因此是可變(mutable)對象。如今咱們已經把可變對象不可變對象的行爲差很少摸清楚了。講不可變對象的時候咱們講解了一個數字對象的例子來幫助理解不可變對象,如今咱們來經過字符串的例子來進一步說明,假如咱們把一個字符串「string」的引用賦值給變量a,那麼咱們是不能對其中的字符來進行修改的,以下圖:

>>> a="string"
>>> a[0]="a"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

可是對於變量a來講,它是徹底能夠更新的,能夠把另外一個對象的引用從新賦值給它:

>>> a="string"
>>> a[0]="a"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> a="hello"
>>> a
'hello'
>>>

事實上不可變對象不管是看着沒法改變的,仍是直覺上感受能夠改變的(好比字符串),都是python中規定好了的。因此沒必要糾結於直覺,咱們要作的是記住哪些數據類型是可變的,而哪些數據類型又是不可變的。下面將介紹一種新的數據類型,它跟列表相似,最大的不一樣是,它是不可變的。它叫元組(tuple)。

2.2.元組(tuple)

元組(tuple) 中能夠裝進去其餘各類數據類型,甚至也能夠將其餘元組(tuple)嵌套進去。元組(tuple)的元素有索引(index),能夠經過索引訪問到。

#空元組
tuple1=()
#一個元素的tuple,寫法特殊一點。若是在元素後面不加逗號
#則python解釋器會當成元素的數據類型,而不認爲是個元組
tuple2=(2,)
#另外一個元組,它裏面有個列表做爲元素
tuple3=(2,"lalala",True,45,[4,5])
#使用內置函數tuple()建立元組,該函參數接受可迭代的對象
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 "<stdin>", line 1, in <module>
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 "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> del e[2][0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object doesn't support item deletion
>>> del e[2]
>>> print(e)
['he!', 0]
>>>

2.3.序列(sequence)

一個數據集合裏面的元素根據放入的前後順序排成一串,這種形式的數據能夠被稱爲序列。字符串,列表,元組都有着相似序列的結構,因此也就有相似的行爲,它們均可以被索引,均可以被迭代,都可以經過索引訪問到其中的元素;不只僅是可以訪問到其中的某一個元素,還能訪問到其中的某幾個元素,這種同時訪問到好幾個元素的行爲,稱爲切片(也能夠把被訪問的這些元素稱爲數據的切片),由於這樣作至關於從整個數據序列中切下來一部分,以下:

x='string'
y=[1,2,3,4,5,6]
z=(7,8,9,10,11,12)
#如下注釋都根據慣例,從0開始計數
#打印出字符串的第3個字符
print(x[3])
#打印出字符串的倒數第2個字符
print(x[-2])
#打印出字符串的一部分,從第1個開始,到第3個字符,不包括第4個字符
print(x[1:4])
#打印出列表的倒數第3個元素
print(str(y[-3]))
#打印出列表的一部分,從第1個元素開始,到第4個元素,不包括第5個元素
print(str(y[1:5]))
#打印出列表的一部分,從第1個元素開始,直到結束
print(str(y[1:]))
#打印出元組的倒數第4個元素
print(str(z[-4]))
#打印出元組的一部分,從第1個元素開始,到第4個元素,不包括第5個元素
print(str(z[1:5]))
#打印出列表的一部分,從第1個元素開始,直到結束
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)

切片中能夠設置步長,就是說能夠設置隔着幾個元素的方式訪問數據的一部分,默認步長爲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)
>>>

當咱們把數據的切片賦值給變量時會發生什麼?通常來講,數據被切片部分會被複制一份副本,而後把副本的引用賦值給變量。可是,有個特殊狀況,就是當數據的被切片部分是所有數據自己時,那該數據要看是可變對象仍是不可變對象了。列表(可變對象)則會複製一份副本,而後把副本的引用賦值給變量,而字符串、元組(不可變對象)則會直接將該數據自己的引用直接賦值給變量,以下:

>>> a=[1,2,3,4]
>>> b=a[:]  #省略掉開頭跟結尾index則切片結果是數據自己
>>> id(a)
140046385947272
>>> id(b)
140046386409992
>>> c=(1,2,[2,3])
>>> d=c[:]
>>> id(c)
140046385743768
>>> id(d)
140046385743768
>>>

2.4.字典(dict)

前面咱們介紹了序列,咱們知道序列的索引隱含在數據類型裏,不須要咱們明確去定義,由於都是0,1,2,3,4……有順序的排列下去,當可變對象裏的元素被刪除,保留下來的元素的索引會改變,由於序列裏的索引永遠都是順序排列的數字,這些數字沒有跟元素綁定,而只是容許咱們經過索引的數字來訪問該位置的元素。那若是咱們想要自定義索引,用字符串,數字等來作索引,並但願這些索引可以跟特定的元素綁定在一塊兒,咱們該怎麼辦?更簡單的說,就是咱們但願索引是獨特而且容易記住,有語義,咱們該怎麼辦?python給咱們提供了一種數據類型字典(dict),能夠勝任這樣的要求。咱們把這樣的索引能夠看做是鍵 (keys),它與要存儲的值綁定在一塊兒,叫作鍵值對。

字典裏存儲鍵值對兒,索引獨特,因此是沒有順序的。順序已經可有可無,咱們只須要記住獨特的鍵就好了。如何建立字典,如何存儲鍵值對,看下面的例子:

>>> a={}    #建立一個空字典
>>> type(a)
<class 'dict'>
>>> b={"id":4667,"name":"john"}  #建立一個有兩對鍵值對的字典
>>> b["id"]  #經過鍵"id"來得到對應的值
4667
>>> c=dict(id=4555,name="li")  #經過內置的dict函數建立字典
>>> print(c)
{'id': 4555, 'name': 'li'}
>>>

字典是可變對象。爲了經過鍵來查找值,就須要字典鍵(keys)保持惟一性,若是鍵用了可變對象來存儲,會出現不可控因素,舉個例子,假如兩個鍵都是由列表來存儲,則一旦把這兩個列表修改相同,那麼查找這兩個鍵所對應的值時就會出現矛盾,因此鍵必定要用不可變對象來存儲,包括數字,布爾值,字符串,元組(須要元組中的元素不包含可變對象)。又由於字典是可變對象,因此字典中鍵值對裏的值是能夠改變的。以下:

>>> 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"。能夠用字典的pop()方法來刪除鍵值對,要注意的是,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)
{}
>>>

當咱們嘗試像迭代序列那樣直接迭代字典時,交互命令行顯示結果以下,明顯只迭代了鍵值對中的鍵(key):

>>> a={1: 1, 2: 2, 3: 3, 'appended': 'ok,then!'}
>>> for item in a:
...     print(item)
... 
1
2
3
appended
>>>

這時候字典中有兩個內置方法能夠協助來完成迭代,分別是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裏還有一種無序的數據類型:集合(set)。基本上,這兒的集合跟數學上的集合的概念是同樣的。其中的元素是無序的,而且每一個元素都是惟一不可重複的。建立集合跟建立字典的符號同樣,都是花括號「{}」,因此當建立空集合時,會跟建立空字典的符號有衝突,因此python裏「{}」表示建立空字典,而建立空集合只能用內置函數set()來建立,以下:

>>> a={1,4,7,"string",("lalala",2,3,4)}  #建立集合
>>> print(a)
{1, 4, 7, 'string', ('lalala', 2, 3, 4)}
>>> b={}   #空字典
>>> type(b)
<class 'dict'>
>>> c=set()  #空集合
>>> type(c)
<class 'set'>
>>>

集合是可變對象,可是它的元素則要求必定是不可變對象,根據集合的定義,每一個元素都是惟一不可重複,那麼一旦元素是可變對象,則有了可重複的可能,好比元素中有兩個列表的話,經過一些操做有可能會讓這兩個列表成爲同樣的。以下:

>>> d={1,2,[1,2]}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>>

既然集合是可變對象,咱們就能夠經過add()方法增長元素,經過remove()、discard()方法、pop()方法刪除元素,前提是全部元素都要是不一樣的,經過pop()方法刪除元素時,不帶參數,而且集合無序,因此沒法預知哪一個元素被丟掉,但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()   #表明空集合

集合能夠實現數學上的並集,交集,差集,對稱差,有兩種方式:操做符跟方法。並集是兩個集合中全部的元素組成的新集合,交集是兩個集合中都存在的元素組成的新集合。差集是兩個集合中在某個集合中存在,而且在另外一個集合中不存在的元素組成的新集合。對稱差集是兩個集合中全部元素,除去在交集中的元素,由這樣的元素組成的新集合。下面這個表顯示python的操做方法:

操做名稱 操做符 集合(好比A,B)內置方法
| 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}
>>>

從上面的例子能夠看出來,兩個集合的並集,交集,對稱差集都是能夠互換的,而差集不是,差集須要區分A-BB-A

在python中,還能夠建立相似於tuple這樣的不可變對象的set,那就是frozenset,frozen在英文中是凍結了的意思,顧名思義,frozenset就是凍結的集合。frozenset不能增長或者更新元素,刪除或者清除元素,相似於只讀文件。並集,交集,差集,對稱差的操做對於frozenset一樣適用。以下:

>>> a=frozenset([1,2,3])   #建立一個frozenset
>>> print(a)
frozenset({1, 2, 3})
>>> a.add(4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'frozenset' object has no attribute 'add'
>>> a.clear()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'frozenset' object has no attribute 'clear'
>>> b=frozenset((4,5,3))
>>> a.union(b)
frozenset({1, 2, 3, 4, 5})
>>>

2.6.簡化內置的help函數,獲取python中各數據類型內置的方法及其簡要說明

在python中,有個help()內置函數能夠獲取關於對象的說明文檔。但說明文檔中有關於該對象實現的細節,當咱們只是想知道各數據類型的經常使用內置方法及其使用時,就會變得很不方便。我寫了一個很短的程序,運行該程序能夠將help函數的輸出簡化(刪去實現對象的相關細節)並存入當前目錄新建的一個文本文件中,而且該程序還有將方法的解釋翻譯成中文的可選功能。下面是該程序內容:

#!/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):
    #把函數的標準輸出存入文件中
    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)
    
    #把文件中的內容分行存入內存
    with open("simpler_help_{}.txt".format(object),"r") as r:
        lines=r.readlines()
    
    #將內存中的內容修改後寫入同名文件,覆蓋原有文件
    #mark1,爲了將帶有「__」的特殊函數不要寫入,並將其下面的解釋一併略過
    mark1=False
    #mark2,爲了將「---」下面的內容略過
    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
                

            #將「---」下面的內容略過
            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)

#若是想將解釋翻譯成中文,能夠把下面的函數註釋取消                
"""
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)+"\n")
                else:
                    line_process=line.replace("|","")
                    w.write(" |        "+translator.translate(line_process)+"\n")
            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")

將上面內容複製粘貼,並命名爲simpler_help.py。舉個例子,若是想知道列表中的內置方法,則在命令行中經過相似這樣的python3 simpler_help.py list命令來運行,特別注意須要在文件名空一格後寫上須要查詢的數據類型名稱。下面是程序建立的簡化版的列表的help函數輸出,保存在當前目錄的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*.
 |

經過程序建立的文件,能夠大概瞭解各數據類型都有哪些內置方法,以後能夠從網上經過關鍵詞搜索出更爲詳細的介紹跟用例。另外,若是想將方法的解釋翻譯成中文,把simpler_help.py中相關內容的註釋取消,而且首先須要在命令行中執行pip install translate

2.7.關於None

>>> type(None)
<class 'NoneType'>
>>>

None屬於NoneType這個數據類型(對象),是它惟一的值。而且,NoneType對象並不像別的數據類型同樣,能夠有不少實例,而是從始至終都只能有一個實例。

>>> None==False
False
>>> None==1
False
>>> None==""
False
>>> None==0
False
>>> None=={}
False
>>> None==None
True
>>>

從上面的例子(作邏輯判斷)能夠看出來,None不等於其餘任何值,None只等於None。None通常用來表明數據的缺失,看下面的例子:

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應該爲None,None表明函數無法合理的處理參數,只能返回None。

當咱們拿到某個數據,咱們想要判斷它不是None的時候該如何作呢,這時候應該用is,由於None是數據類型,也是對象,因此咱們通常會想要這樣作: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應該爲"a is not None"。

有些時候咱們會在別人的代碼中看到if an_object:這樣的寫法,這種寫法跟上面的判斷某數據(對象)是否是None的寫法沒有什麼聯繫,這種代碼暗示了數據(對象)自己在作邏輯判斷時會另外呈現出或真或假的布爾值,事實上也的確如此,在各類數據類型中,會有某些特殊的值在邏輯判斷的時候布爾值表現爲False,其他值表現爲True。在python中,規定了一套規則,當咱們拿某個數據自己來作邏輯判斷的時候,解釋器如何解肯定其布爾值:

  • 布爾型,False表示False,其餘爲True
  • 整數和浮點數,0表示False,其餘爲True
  • 字符串和類字符串類型(包括bytes和unicode),空字符串表示False,其餘爲True
  • 序列類型(包括tuple,list,dict,set等),空表示False,非空表示True
  • None永遠表示False

舉個例子,當咱們在理論上來講從某函數(方法)返回了一個列表list1,咱們用if list1:來判斷的時候,則會出現三種狀況:list1Nonelist1是空列表,list1是非空列表,這時候list1在第一和第二種狀況下表現出的布爾值爲False,在第三種狀況下表現出的布爾值爲True。以下:

list1=[]

while True:
    if list1:
        print("true")
    else:
        if list1 != None:
            print("false,empty list")
            list1=None
        else:
            print("false,None")
            list1=[]

#OUTPUT應該永遠在None跟空列表之間徘徊。交替打印「false,empty list」和「false,None」。

[歡迎瀏覽個人我的博客,https://diwugebingren.github.io
](https://diwugebingren.github....
歡迎關注個人公衆號

相關文章
相關標籤/搜索