fluent python-第 2 章 2.1 內(nèi)置序列構(gòu)成的數(shù)組

序列類型

Python 標(biāo)準(zhǔn)庫(kù)用 C 實(shí)現(xiàn)了豐富的序列類型, 列舉如下冀瓦。
容器序列
list、 tuple 和 collections.deque 這些序列能存放不同類型的數(shù)據(jù)调鲸。

扁平序列
str、 bytes挽荡、 bytearray藐石、 memoryview 和 array.array, 這類序列只能容納一種類型定拟。

容器序列存放的是它們所包含的任意類型的對(duì)象的引用于微, 而扁平序列
里存放的是值而不是引用
。 換句話說(shuō)青自, 扁平序列其實(shí)是一段連續(xù)的內(nèi)存空間株依。 由此可見(jiàn)扁平序列其實(shí)更加緊湊, 但是它里面只能存放諸如字符延窜、 字節(jié)和數(shù)值這種基礎(chǔ)類型恋腕。序列類型還能按照能否被修改來(lái)分類。

可變序列
list逆瑞、 bytearray荠藤、 array.array、 collections.deque 和
memoryview获高。
不可變序列
tuple哈肖、 str 和 bytes。
圖 2-1 顯示了可變序列( MutableSequence) 和不可變序列
( Sequence) 的差異念秧, 同時(shí)也能看出前者從后者那里繼承了一些方
法牡彻。 雖然內(nèi)置的序列類型并不是直接從 Sequence 和
MutableSequence 這兩個(gè)抽象基類( Abstract Base Class, ABC) 繼承而來(lái)的出爹, 但是了解這些基類可以幫助我們總結(jié)出那些完整的序列類型包含了哪些功能庄吼。


2.2 列表推導(dǎo)和生成器表達(dá)式

說(shuō)白了主要就是 for 循環(huán)


2.2.3 笛卡兒積

>>> colors = ['black', 'white']
>>> sizes = ['S', 'M', 'L']
>>> tshirts = [(color, size) for color in colors for size in sizes] ?
>>> tshirts
[('black', 'S'), ('black', 'M'), ('black', 'L'), ('white', 'S'),
('white', 'M'), ('white', 'L')]
>>> for color in colors: ?
... for size in sizes:
... print((color, size))
...
('black', 'S')
('black', 'M')
('black', 'L')
('white', 'S')
('white', 'M')
('white', 'L')
>>> tshirts = [(color, size) for size in sizes ?
... for color in colors]
>>> tshirts
[('black', 'S'), ('white', 'S'), ('black', 'M'), ('white', 'M'),
('black', 'L'), ('white', 'L')]


2.2.4 生成器表達(dá)式

生成器表達(dá)式的語(yǔ)法跟列表推導(dǎo)差不多, 只不過(guò)把方括號(hào)換成圓括號(hào)而

>>> symbols = '$¢£¥€¤'
>>> tuple(ord(symbol) for symbol in symbols) ?
(36, 162, 163, 165, 8364, 164)
>>> import array
>>> array.array('I', (ord(symbol) for symbol in symbols)) ?
array('I', [36, 162, 163, 165, 8364, 164])


2.3 元組不僅僅是不可變的列表

有些 Python 入門(mén)教程把元組稱為“不可變列表”严就, 然而這并沒(méi)有完全概括
元組的特點(diǎn)总寻。 除了用作不可變的列表, 它還可以用于沒(méi)有字段名的記
錄梢为。 鑒于后者常常被忽略渐行, 我們先來(lái)看看元組作為記錄的功用


2.3.1 元組和記錄
元組其實(shí)是對(duì)數(shù)據(jù)的記錄: 元組中的每個(gè)元素都存放了記錄中一個(gè)字段
的數(shù)據(jù), 外加這個(gè)字段的位置铸董。 正是這個(gè)位置信息給數(shù)據(jù)賦予了意義祟印。

In [210]: lax_coordinates = (33.9425, -118.408056)

In [211]: city, year, pop, chg, area = ('Tokyo', 2003, 32450, 0.66, 8014)

In [212]: traveler_ids = [('USA', '31195855'), ('BRA', 'CE342567'),('ESP', 'XDA2
     ...: 05856')]

In [213]: for passport in sorted(traveler_ids):
     ...:     print('%s/%s' % passport)
     ...:
BRA/CE342567
ESP/XDA205856
USA/31195855

In [214]:  for country, _ in traveler_ids:     # 說(shuō)白了就是花式運(yùn)用拆包特性
     ...:     print(country)      # 上方 _ 為占位符 
     ...:
USA
BRA
ESP


2.3.2 元組拆包

最好辨認(rèn)的元組拆包形式就是平行賦值, 也就是說(shuō)把一個(gè)可迭代對(duì)象里的元素粟害, 一并賦值到由對(duì)應(yīng)的變量組成的元組中蕴忆。

>>> lax_coordinates = (33.9425, -118.408056)
>>> latitude, longitude = lax_coordinates       # ??元組拆包
>>> latitude
33.9425
>>> longitude
-118.408056

另外一個(gè)很優(yōu)雅的寫(xiě)法當(dāng)屬不使用中間變量交換兩個(gè)變量的值:

>>> b, a = a, b

在 Python 中, 函數(shù)用 *args 來(lái)獲取不確定數(shù)量的參數(shù)算是一種經(jīng)典寫(xiě)
法了悲幅。

>>> a, b, *rest = range(5)
>>> a, b, rest
(0, 1, [2, 3, 4])
>>> a, b, *rest = range(3)
>>> a, b, rest
(0, 1, [2])
>>> a, b, *rest = range(2)
>>> a, b, rest
(0, 1, [])


2.3.3 嵌套元組拆包

In [221]: a,b,c = (1,2,(3,4))

In [222]: a
Out[222]: 1

In [223]: c
Out[223]: (3, 4)


2.3.4 具名元組

collections.namedtuple 是一個(gè)工廠函數(shù)套鹅, 它可以用來(lái)構(gòu)建一個(gè)帶
字段名的元組和一個(gè)有名字的類——這個(gè)帶名字的類對(duì)調(diào)試程序有很大
幫助站蝠。
用 namedtuple 構(gòu)建的類的實(shí)例所消耗的內(nèi)存跟元組是一樣的, 因?yàn)樽侄蚊急淮嬖趯?duì)應(yīng)的類里面卓鹿。 這個(gè)實(shí)例跟普通的對(duì)象實(shí)例比起來(lái)也要小一些菱魔, 因?yàn)?Python 不會(huì)用 __dict__ 來(lái)存放這些實(shí)例的屬性。

Card = collections.namedtuple('Card', ['rank', 'suit'])   # 構(gòu)建范例

如何用具名元組來(lái)記錄一個(gè)城市的信息:

>>> from collections import namedtuple
>>> City = namedtuple('City', 'name country population coordinates') ?
>>> tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667)) ?
>>> tokyo
City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722,
139.691667))
>>> tokyo.population ?
36.933
>>> tokyo.coordinates
(35.689722, 139.691667)
>>> tokyo[1]
'JP

? 創(chuàng)建一個(gè)具名元組需要兩個(gè)參數(shù)吟孙, 一個(gè)是類名澜倦, 另一個(gè)是類的各個(gè)
字段的名字。 后者可以是由數(shù)個(gè)字符串組成的可迭代對(duì)象杰妓, 或者是由空格分隔開(kāi)的字段名組成的字符串藻治。

? 存放在對(duì)應(yīng)字段里的數(shù)據(jù)要以一串參數(shù)的形式傳入到構(gòu)造函數(shù)中
( 注意, 元組的構(gòu)造函數(shù)卻只接受單一的可迭代對(duì)象)
? 你可以通過(guò)字段名或者位置來(lái)獲取一個(gè)字段的信息稚失。


建立由列表組成的列表

有時(shí)我們會(huì)需要初始化一個(gè)嵌套著幾個(gè)列表的列表栋艳, 譬如一個(gè)列表可能需要用來(lái)存放不同的學(xué)生名單恰聘, 或者是一個(gè)井字游戲板 上的一行方
塊句各。 想要達(dá)成這些目的, 最好的選擇是使用列表推導(dǎo)晴叨。

一個(gè)包含 3 個(gè)列表的列表凿宾, 嵌套的 3 個(gè)列表各自有 3 個(gè)元素來(lái)代表井字游戲的一行方塊 :

>>> board = [['_'] * 3 for i in range(3)]  ?
>>> board
[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
>>> board[1][2] = 'X'  ?
>>> board
[['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]

? 建立一個(gè)包含 3 個(gè)列表的列表, 被包含的 3 個(gè)列表各自有 3 個(gè)元
素兼蕊。 打印出這個(gè)嵌套列表初厚。
? 把第 1 行第 2 列的元素標(biāo)記為 X, 再打印出這個(gè)列表孙技。


2.6 序列的增量賦值

+= 背后的特殊方法是 __iadd__ ( 用于“就地加法”) 产禾。 但是如果一個(gè)類
沒(méi)有實(shí)現(xiàn)這個(gè)方法的話, Python 會(huì)退一步調(diào)用 __add__ 牵啦。 考慮下面這
個(gè)簡(jiǎn)單的表達(dá)式:

>>> a += b

如果 a 實(shí)現(xiàn)了 __iadd__ 方法亚情, 就會(huì)調(diào)用這個(gè)方法。 同時(shí)對(duì)可變序列
( 例如 list哈雏、 bytearray 和 array.array) 來(lái)說(shuō)楞件, a 會(huì)就地改動(dòng), 就
像調(diào)用了 a.extend(b) 一樣裳瘪。 但是如果 a 沒(méi)有實(shí)現(xiàn) __iadd__的話土浸, a += b 這個(gè)表達(dá)式的效果就變得跟 a = a + b 一樣了: 首先計(jì)算 a + b, 得到一個(gè)新的對(duì)象彭羹, 然后賦值給 a黄伊。 也就是說(shuō), 在這個(gè)表達(dá)式中派殷,變量名會(huì)不會(huì)被關(guān)聯(lián)到新的對(duì)象毅舆, 完全取決于這個(gè)類型有沒(méi)有實(shí)現(xiàn)
__iadd__這個(gè)方法西篓。

總體來(lái)講, 可變序列一般都實(shí)現(xiàn)了__iadd__方法憋活, 因此 += 是就地加法岂津。 而不可變序列根本就不支持這個(gè)操作, 對(duì)這個(gè)方法的實(shí)現(xiàn)也就無(wú)從談起悦即。

接下來(lái)有個(gè)小例子吮成, 展示的是 *= 在可變和不可變序列上的作用:

>>> l = [1, 2, 3]
>>> id(l)
4311953800 ?
>>> l *= 2
>>> l
[1, 2, 3, 1, 2, 3]
>>> id(l)
4311953800 ?
>>> t = (1, 2, 3)
>>> id(t)
4312681568 ? # 元組不可變,一變內(nèi)存地址就變
>>> t *= 2
>>> id(t)     #   對(duì)不可變序列進(jìn)行重復(fù)拼接操作的話辜梳, 效率會(huì)很低
4301348296 ? 運(yùn)用增量乘法后粱甫, 新的元組被創(chuàng)建


??一個(gè)關(guān)于+=的謎題 (有趣)

In [257]: t = (1,2,[30,40])

In [258]: t[2]
Out[258]: [30, 40]

In [259]: type(t)
Out[259]: tuple

In [260]: type(t[2])
Out[260]: list

In [262]: t[2] += [50,60]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-262-b483a1b4fe1d> in <module>()
----> 1 t[2] += [50,60]
TypeError: 'tuple' object does not support item assignment

結(jié)果:
t 變成 (1, 2, [30, 40, 50, 60])
因?yàn)?tuple 不支持對(duì)它的元素賦值, 所以會(huì)拋出 TypeError 異常

教訓(xùn):不要把可變對(duì)象放在元組里面


2.7 list.sort方法和內(nèi)置函數(shù)sorted

list.sort 方法會(huì)就地排序列表作瞄, 也就是說(shuō)不會(huì)把原列表復(fù)制一份茶宵。 這
也是這個(gè)方法的返回值是 None 的原因, 提醒你本方法不會(huì)新建一個(gè)列
表宗挥。 在這種情況下返回 None 其實(shí)是 Python 的一個(gè)慣例.

與 list.sort 相反的是內(nèi)置函數(shù) sorted乌庶, 它會(huì)新建一個(gè)列表作為返回值。 這個(gè)方法可以接受任何形式的可迭代對(duì)象作為參數(shù)契耿, 甚至包括不可變序列或生成器


2.10 本章小結(jié)

要想寫(xiě)出準(zhǔn)確瞒大、 高效和地道的 Python 代碼, 對(duì)標(biāo)準(zhǔn)庫(kù)里的序列類型的掌握是不可或缺的搪桂。

Python 序列類型最常見(jiàn)的分類就是可變和不可變序列透敌。 但另外一種分類方式也很有用, 那就是把它們分為扁平序列和容器序列踢械。 前者的體積更小酗电、 速度更快而且用起來(lái)更簡(jiǎn)單, 但是它只能保存一些原子性的數(shù)據(jù)内列,比如數(shù)字撵术、 字符和字節(jié)。 容器序列則比較靈活德绿, 但是當(dāng)容器序列遇到可變對(duì)象時(shí)荷荤, 用戶就需要格外小心了, 因?yàn)檫@種組合時(shí)常會(huì)搞出一些“意外”移稳, 特別是帶嵌套的數(shù)據(jù)結(jié)構(gòu)出現(xiàn)時(shí)蕴纳, 用戶要多費(fèi)一些心思來(lái)保證代碼的正確

列表推導(dǎo)和生成器表達(dá)式則提供了靈活構(gòu)建和初始化序列的方式个粱, 這兩個(gè)工具都異常強(qiáng)大古毛。 如果你還不能熟練地使用它們, 可以專門(mén)花時(shí)間練習(xí)一下。 它們其實(shí)不難稻薇, 而且用起來(lái)讓人上癮元組在 Python 里扮演了兩個(gè)角色嫂冻, 它既可以用作無(wú)名稱的字段的記錄,又可以看作不可變的列表塞椎。 當(dāng)元組被當(dāng)作記錄來(lái)用的時(shí)候桨仿, 拆包是最安全可靠地從元組里提取不同字段信息的方式。 新引入的*句法讓元組拆包的便利性更上一層樓案狠, 讓用戶可以選擇性忽略不需要的字段服傍。 具名元組也已經(jīng)不是一個(gè)新概念了, 但它似乎沒(méi)有受到應(yīng)有的重視骂铁。 就像普通元組一樣吹零, 具名元組的實(shí)例也很節(jié)省空間, 但它同時(shí)提供了方便地通過(guò)名字來(lái)獲取元組各個(gè)字段信息的方式拉庵, 另外還有個(gè)實(shí)用的 ._asdict()方法來(lái)把記錄變成 OrderedDict 類型灿椅。

Python 里最受歡迎的一個(gè)語(yǔ)言特性就是序列切片, 而且很多人其實(shí)還沒(méi)完全了解它的強(qiáng)大之處钞支。 比如茫蛹, 用戶自定義的序列類型也可以選擇支持NumPy 中的多維切片和省略( ...) 。 另外伸辟, 對(duì)切片賦值是一個(gè)修改可變序列的捷徑麻惶。

重復(fù)拼接 seq * n 在正確使用的前提下馍刮, 能讓我們方便地初始化含有
不可變?cè)氐亩嗑S列表信夫。 增量賦值 += 和 *= 會(huì)區(qū)別對(duì)待可變和不可變序列。 在遇到不可變序列時(shí)卡啰, 這兩個(gè)操作會(huì)在背后生成新的序列静稻。 但如果被賦值的對(duì)象是可變的, 那么這個(gè)序列會(huì)就地修改——然而這也取決于序列本身對(duì)特殊方法的實(shí)現(xiàn)匈辱。

序列的 sort方法和內(nèi)置的 sorted函數(shù)雖然很靈活振湾, 但是用起來(lái)都不
難。 這兩個(gè)方法都比較靈活亡脸, 是因?yàn)樗鼈兌冀邮芤粋€(gè)函數(shù)作為可選參數(shù)來(lái)指定排序算法如何比較大小押搪, 這個(gè)參數(shù)就是 key 參數(shù)。 key 還可以被用在 min 和 max 函數(shù)里浅碾。 如果在插入新元素的同時(shí)還想保持有序序列的順序大州, 那么需要用到bisect.insortbisect.bisect的作用則是快速查找垂谢。

除了列表和元組厦画, Python 標(biāo)準(zhǔn)庫(kù)里還有 array.array。 另外, 雖然NumPy 和 SciPy 都不是 Python 標(biāo)準(zhǔn)庫(kù)的一部分根暑, 但稍微學(xué)習(xí)一下它們力试,會(huì)讓你在處理大規(guī)模數(shù)值型數(shù)據(jù)時(shí)如有神助。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末排嫌,一起剝皮案震驚了整個(gè)濱河市畸裳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌淳地,老刑警劉巖躯畴,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異薇芝,居然都是意外死亡蓬抄,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)夯到,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嚷缭,“玉大人,你說(shuō)我怎么就攤上這事耍贾≡乃” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵荐开,是天一觀的道長(zhǎng)付翁。 經(jīng)常有香客問(wèn)我,道長(zhǎng)晃听,這世上最難降的妖魔是什么百侧? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮能扒,結(jié)果婚禮上佣渴,老公的妹妹穿的比我還像新娘。我一直安慰自己初斑,他們只是感情好辛润,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著见秤,像睡著了一般砂竖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鹃答,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天乎澄,我揣著相機(jī)與錄音,去河邊找鬼挣跋。 笑死三圆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播舟肉,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼修噪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了路媚?” 一聲冷哼從身側(cè)響起黄琼,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎整慎,沒(méi)想到半個(gè)月后脏款,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡裤园,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年撤师,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拧揽。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡剃盾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出淤袜,到底是詐尸還是另有隱情痒谴,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布铡羡,位于F島的核電站积蔚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏烦周。R本人自食惡果不足惜尽爆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望论矾。 院中可真熱鬧教翩,春花似錦杆勇、人聲如沸贪壳。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)闰靴。三九已至,卻和暖如春钻注,著一層夾襖步出監(jiān)牢的瞬間蚂且,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工幅恋, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杏死,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像淑翼,于是被迫代替她去往敵國(guó)和親腐巢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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