Python基礎(chǔ)手冊12——序列(類型操作)

四、序列類型操作符


1、索引和切片操作符 ( [],[:],[::] )

序列類型的元素被順序放置,這種方式允許通過指定下標(biāo)的方式來獲得某一個(gè)數(shù)據(jù)元素, 或者通過指定下標(biāo)范圍來獲得一組序列的元素,這種訪問序列單個(gè)元素的方式叫做索引,獲取子序列的方式叫做切片。

序列支持的索引操作:

sequence[index]
<meta charset="utf-8">

sequence 是序列的名字,index 是想要訪問的元素對應(yīng)的偏移量。偏移量可以是正值,范圍從 0 到偏移最大值(比序列長度少1),可以使用內(nèi)建函數(shù) len() 返回序列的元素個(gè)數(shù), 所以:0 <= index <= len(sequece) - 1 。另外,也可以使用負(fù)索引,范圍是 -1 到序列的負(fù)長度:-len(sequence),所以:-len(sequence) <= index <= -1。

正負(fù)索引的區(qū)別在于正索引以序列的開始為起點(diǎn),負(fù)索引以序列的結(jié)束為起點(diǎn)。從技術(shù)上講,一個(gè)負(fù)偏移與這個(gè)字符串的常度相加后得到這個(gè)字符串的正的偏移量。我們可以理解為當(dāng)Python去所以一個(gè)負(fù)偏移量的時(shí)候,會將其負(fù)偏移量加上序列的長度后得到對應(yīng)的正偏移量,再去索引。

試圖訪問一個(gè)越界的索引會引發(fā)一個(gè)如下的異常:

因?yàn)?Python 是面向?qū)ο蟮模?所以你可以像下面這樣直接訪問一個(gè)序列的元素(不用先把它賦值給一個(gè)變量):

由于列表、元組中可以包含列表和元組(或其他對象),有時(shí)需要將幾次索引 操作連在一起使用來深入到數(shù)據(jù)結(jié)構(gòu)中去。


序列支持的切片操作:

sequence[starting_index:ending_index:step]

當(dāng)使用一對以冒號分隔的偏移來索引序列類型對象時(shí),我們可以得到從起始索引(starting_index)到結(jié)束索引(ending_index)(不包括結(jié)束索引對應(yīng)的元素)之間的所有元素。起始索引和結(jié)束索引都是可選的, 如果沒有提供或者用 None 作為索引值,其實(shí)索引和結(jié)束索引會默認(rèn)為0和分片對象的長度。

對序列對象進(jìn)行分片操作,會返回一個(gè)新的同樣類型的對象。

序列的切片操作的第三個(gè)索引值(step)被用做步長參數(shù),用作步進(jìn),默認(rèn)為1。我們可以使用負(fù)數(shù)作為步進(jìn)參數(shù),這表示Python會從右至左進(jìn)行分片,而不是通常的從左至右,從而實(shí)現(xiàn)序列的反轉(zhuǎn)。

索引和分片的賦值操作都是原處修改,對于不可變對象,會引發(fā)錯(cuò)誤異常。他們會對可變對象直接進(jìn)行修改,而不是生成一個(gè)新的對象作為結(jié)果。

分片賦值時(shí),插入元素的個(gè)數(shù)不需要與刪除的數(shù)目相匹配。實(shí)際上,分片賦值是一次性替換整個(gè)片段或“欄”,因?yàn)橘x值的序列長度不一定要與被賦值的分片的長度相匹配,所以分片賦值能夠用來替換(覆蓋)、增長(插入)、縮短(刪除)主列表。


2、成員關(guān)系操作符 (in, not in)

成員關(guān)系操作符使用來判斷一個(gè)元素是否屬于一個(gè)序列的。比如對字符串類型來說,就是判斷一個(gè)字符是否屬于這個(gè)字符串,對和元組類型來說,就代表了一個(gè)對象是否屬于該元組。in/not in 操作符的返回值一般來講就是 True/False,滿足成員關(guān)系就返回 True,否則返回 False。

obj [not] in sequence


3、連接操作符( + )

這個(gè)操作符允許我們把一個(gè)序列和另一個(gè)相同類型的序列做連接。

sequence1 + sequence2

該表達(dá)式的結(jié)果是一個(gè)包含 sequence1 和 sequence2 的內(nèi)容的新序列。

注意:這種方式看起來似乎實(shí)現(xiàn)了把兩個(gè)序列內(nèi)容合并的概念,但是這個(gè)操作不是最快或者說最有效的。對字符串來說,這個(gè)操作不如把所有的子字符串放到一個(gè)列表或可迭代對象中,然后調(diào)用一個(gè) join() 方法來把所有的內(nèi)容連接在一起節(jié)約內(nèi)存。類似地,對列表來說,我們推薦讀者用列表類型的 extend() 方法來把兩個(gè)或者多個(gè)列表對象合并。當(dāng)你需要簡單地把兩個(gè)對象的內(nèi)容合并, 或者說不能依賴于可變對象的那些沒有返回值(實(shí)際上它返回一個(gè) None)的內(nèi)建方法來完成的時(shí)候時(shí),連接操作符還是很方便的一個(gè)選擇。


4、重復(fù)操作符 ( * )

當(dāng)你需要需要一個(gè)序列的多份拷貝時(shí),重復(fù)操作符非常有用。

sequence * copies_int

copies_int 必須是一個(gè)整數(shù),像連接操作符一樣,該操作符返回一個(gè)新的包含多份原對象拷貝的對象。




五、序列可用的內(nèi)建函數(shù)

在講解序列類型的內(nèi)建函數(shù)之前,有一點(diǎn)需要說明,序列本身就內(nèi)含了迭代的概念,之所以會這樣,是因?yàn)榈@個(gè)概念就是從序列,迭代器,或者其他支持迭代操作的對象中泛化得來的。由于 Python 的 for 循環(huán)可以遍歷所有的可迭代類型,在(非純序列對象上)執(zhí)行 for 循環(huán)時(shí)就像在一個(gè)純序列對象上執(zhí)行一樣。 而且 Python 的很多原來只支持序列作為參數(shù)的內(nèi)建函數(shù)現(xiàn)在也開始支持迭代器或者或類迭代器了。我們把這些類型統(tǒng)稱為"可迭代對象"。

1、類型轉(zhuǎn)換函數(shù) list(),tuple() 和 str() (repr())

內(nèi)建函數(shù) list(),str()tuple()[圖片上傳中...(13.png-ff9688-1515593009797-0)]
被用做在各種序列類型之間轉(zhuǎn)換。這些轉(zhuǎn)換實(shí)際上是工廠函數(shù),將對象作為參數(shù),并將其內(nèi)容(淺)拷貝到新生成的對象中。一旦一個(gè) Python 的對象被建立,我們就不能更改其身份或類型了。如果你把一個(gè)列表對象傳給 list() 函數(shù),便會創(chuàng)建這個(gè)對象的一個(gè)淺拷貝,然后將其插入新的列表中。同樣地,在做連接操作和重復(fù)操作時(shí),我們也會這樣處理。

所謂淺拷貝就是只拷貝了對象中元素對象的索引,而不是重新建立了對象中所有的元素對象!如果你想完全的拷貝一個(gè)對象(包括遞歸,如果你的對象是一個(gè)包含容器的容器)。你需要用到深拷貝。

list() 和 tuple() 函數(shù)在列表類型和元組類型的互換時(shí)非常有用。 雖然這些函數(shù)也適用于 string 類型(因?yàn)?string 類型也是序列的一種),但是在 string 類型上應(yīng)用 tuple()和 list()函數(shù)卻得不到我們通常希望的結(jié)果。

內(nèi)建函數(shù) str()repr() 可以獲取對象的內(nèi)容、類型、數(shù)值屬性等信息的字符串格式。str() 函數(shù)得到的字符串可讀性好, 而 repr()函數(shù)得到的字符串通??梢杂脕碇匦芦@得該對象, 通常情況下 obj == eval(repr(obj)) 這個(gè)等式是成立的。這兩個(gè)函數(shù)接受一個(gè)對象做為其參數(shù),返回適當(dāng)?shù)淖址?/p>

盡管 str() 和 repr() 在特性和功能方面都非常相似, 事實(shí)上 repr() 返回的是一個(gè)對象的“官方”字符串表示, 也就是說絕大多數(shù)情況下可以通過求值運(yùn)算(使用 eval()內(nèi)建函數(shù))重新得到該對象。但 str()則有所不同,str() 致力于生成一個(gè)對象的可讀性好的字符串表示,它的返回結(jié)果通常無法用于 eval()求值, 但很適合用于 print() 語句輸出。

需要再次提醒一下的是, 并不是所有 repr()返回的字符串都能夠用eval()內(nèi)建函數(shù)得到原來的對象。

也就是說 repr() 輸出對 Python 比較友好, 而 str()的輸出對人比較友好。雖然如此,很多情況下這二者的輸出仍然都是完全一樣的。

2、返回對象的長度 len()

內(nèi)建函數(shù) len() 可以返回序列對象的長度(元素個(gè)數(shù))。


3、返回最大值和最小值 max() 和 min()

對于 string 類型它們能返回最大或者最小的字符(按照ASCII碼中的字符的先后順序比較)。對于元組和列表來說,當(dāng)其元素都為字符串或者數(shù)字對象時(shí),才可以返回正確結(jié)果。對于混合對象,結(jié)構(gòu)越復(fù)雜返回的結(jié)構(gòu)準(zhǔn)確性就越差。




六、拷貝 Python 對象淺拷貝和深拷貝

對象賦值實(shí)際上是簡單的對象引用。也就是說當(dāng)你創(chuàng)建一個(gè)對象,然后把它賦給另一個(gè)變量的時(shí)候,Python 并沒有拷貝這個(gè)對象,而是拷貝了這個(gè)對象的引用給變量。

假設(shè)你想創(chuàng)建一對小夫妻的通用檔案,名為 person。然后你分別為他倆拷貝一份。在下面的例子中,我們展示了兩種拷貝對象的方式,一種使用了切片操作,另一種用了工廠方法。為了區(qū)分出三個(gè)不同的對象,我們使用 id()內(nèi)建函數(shù)來顯示每個(gè)對象的標(biāo)識符。(我們還可以用 is 操作符來做相同的事情)

為他們創(chuàng)建了初始有100 的個(gè)人存款帳戶。用戶名改為定制的名字。但是,當(dāng)丈夫取走 50后,他的行為影響到了他妻子的賬戶,雖然我們進(jìn)行了分開的拷貝作(當(dāng)然,前提是我們希望他們每個(gè)人都擁有自己單獨(dú)的帳號,而不是一個(gè)單一的聯(lián)合帳號)。

原因是我們僅僅做了一個(gè)淺拷貝。對一個(gè)對象進(jìn)行淺拷貝其實(shí)是新創(chuàng)建了一個(gè)類型跟原對象一樣,其內(nèi)容是原來對象內(nèi)容的拷貝(其實(shí)都是原對象中元素的引用),換句話說,這個(gè)拷貝的對象本身是新的,但是它的內(nèi)容不是。序列類型對象的淺拷貝是默認(rèn)類型拷貝,并可以以下幾種方式實(shí)施:(1)完全切片操作[:],(2)利用工廠函數(shù),比如 list(),dict()等,(3)使用 copy 模塊的 copy() 函數(shù)。

你的下一個(gè)問題可能是:當(dāng)妻子的名字被賦值,為什么丈夫的名字沒有受到影響?難道它們的名字現(xiàn)在不應(yīng)該都是'jane'了嗎?為什么名字沒有變成一樣的呢?怎么會是這樣呢?這是因?yàn)樵谶@兩個(gè)列表的兩個(gè)對象中,第一個(gè)對象是不可變的(是個(gè)字符串類型),而第二個(gè)是可變的(一個(gè)列表)。正因?yàn)槿绱?,?dāng)進(jìn)行淺拷貝時(shí),字符串對象和列表對象的引用都被進(jìn)行拷貝到了新的列表中,正如上圖我們看到的,剛拷貝完,person、hubby、wifey三個(gè)對象的id不同,但是其內(nèi)部元素的id都是相同的,正是因?yàn)樗麄儍?nèi)部的元素都指向了相同的對象。而當(dāng)我們對hubby和wifey第一個(gè)元素進(jìn)行賦值時(shí),由于字符串是不可改變對象,所以他們的第一個(gè)元素都指向了各自新創(chuàng)建的字符串對象。由于列表是可變對象。所以hubby對第二個(gè)元素列表中的數(shù)字進(jìn)行了修改之后,由于wifey的第二個(gè)元素也是指向的同一個(gè)列表對象,所以我們看wifey中的錢也發(fā)生了變化。

假設(shè)我們要給這對夫妻創(chuàng)建一個(gè)聯(lián)合賬戶,那這是一個(gè)非常棒的方案,但是,如果需要的是兩個(gè)分離賬戶,就需要作些改動了。要得到一個(gè)完全拷貝或者說深拷貝--創(chuàng)建一個(gè)新的容器對象,包含原有對象元素以及所有嵌套元素全新的拷貝--需要 copy.deepcopy()函數(shù)。我們使用深拷貝來重寫整個(gè)例子。

注意: 上面示例中你可能認(rèn)為深拷貝的列表中的第一個(gè)元素和原列表的id值不應(yīng)該一樣。首先肯定的是你的理解是沒有問題的,只是Python為了優(yōu)化效率,在解釋器內(nèi)提前初始化了一些常用的字符串,包括 'name',所以person、hubby、wifey的第一個(gè)元素都指向了Python解釋器提前初始化的'name'對象。如果是更復(fù)雜的字符串,其id值就會不一樣了。

注意: 第一,非容器類型(比如數(shù)字,字符串和其他"原子"類型的對象,像代碼,類型和 xrange 對象等)沒有被拷貝一說,淺拷貝是用完全切片操作來完成的。第二,如果元組變量只包含原子類型對象,對它的深拷貝將不會進(jìn)行。如果我們把賬戶信息改成元組類型,那么即便按我們的要求使用深拷貝操作也只能得到一個(gè)淺拷貝。


《Python基礎(chǔ)手冊》系列:

Python基礎(chǔ)手冊 1 —— Python語言介紹
Python基礎(chǔ)手冊 2 —— Python 環(huán)境搭建(Linux)
Python基礎(chǔ)手冊 3 —— Python解釋器
Python基礎(chǔ)手冊 4 —— 文本結(jié)構(gòu)
Python基礎(chǔ)手冊 5 —— 標(biāo)識符和關(guān)鍵字
Python基礎(chǔ)手冊 6 —— 操作符
Python基礎(chǔ)手冊 7 —— 內(nèi)建函數(shù)
Python基礎(chǔ)手冊 8 —— Python對象
Python基礎(chǔ)手冊 9 —— 數(shù)字類型
Python基礎(chǔ)手冊10 —— 序列(字符串)
Python基礎(chǔ)手冊11 —— 序列(元組&列表)
Python基礎(chǔ)手冊12 —— 序列(類型操作)
Python基礎(chǔ)手冊13 —— 映射(字典)
Python基礎(chǔ)手冊14 —— 集合
Python基礎(chǔ)手冊15 —— 解析
Python基礎(chǔ)手冊16 —— 文件
Python基礎(chǔ)手冊17 —— 簡單語句
Python基礎(chǔ)手冊18 —— 復(fù)合語句(流程控制語句)
Python基礎(chǔ)手冊19 —— 迭代器
Python基礎(chǔ)手冊20 —— 生成器
Python基礎(chǔ)手冊21 —— 函數(shù)的定義
Python基礎(chǔ)手冊22 —— 函數(shù)的參數(shù)
Python基礎(chǔ)手冊23 —— 函數(shù)的調(diào)用
Python基礎(chǔ)手冊24 —— 函數(shù)中變量的作用域
Python基礎(chǔ)手冊25 —— 裝飾器
Python基礎(chǔ)手冊26 —— 錯(cuò)誤 & 異常
Python基礎(chǔ)手冊27 —— 模塊
Python基礎(chǔ)手冊28 —— 模塊的高級概念
Python基礎(chǔ)手冊29 —— 包

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容