成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Python 列表推導(dǎo)及優(yōu)先級隊(duì)列的實(shí)現(xiàn)

darkerXi / 3168人閱讀

摘要:主要介紹列表列表推導(dǎo)有關(guān)的話題,最后演示如何用列表實(shí)現(xiàn)一個(gè)優(yōu)先級隊(duì)列。笛卡爾積列表推導(dǎo)還可以生成兩個(gè)或以上的可迭代類型的笛卡爾積。兩個(gè)優(yōu)先級相同的元素和,操作按照它們被插入到隊(duì)列的順序返回。變量的作用是保證同等優(yōu)先級元素的正確排序。

這一篇是《流暢的 python》讀書筆記。主要介紹列表、列表推導(dǎo)有關(guān)的話題,最后演示如何用列表實(shí)現(xiàn)一個(gè)優(yōu)先級隊(duì)列。

Python 內(nèi)置序列類型

Python 標(biāo)準(zhǔn)庫用 C 實(shí)現(xiàn)了豐富的序列類型:

容器序列:

list、tuple和 collections.deque 這些序列能存放不同類型的數(shù)據(jù)。

扁平序列:

str、bytes、bytearray、memoryview 和 array.array,這類序列只能容納一種類型。

容器序列存放的是它們所包含的任意類型的對象的引用,而扁平序列里存放的是值而不是引用(也可以說扁平序列其實(shí)存放的是一段連續(xù)的內(nèi)存空間)。

如果按序列是否可被修改來分類,序列分為可變序列不可變序列:

可變序列

list、bytearray、array.array、collections.deque 和 memoryview。

不可變序列

tuple、str和 bytes。

下圖顯示了可變序列(MutableSequence)和不可變序列(sequence)的差異:

從這個(gè)圖可以看出,可變序列從不可變序列那里繼承了一些方法。

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

列表(list)是 Python 中最基礎(chǔ)的序列類型。list 是一個(gè)可變序列,并且能同時(shí)存放不同類型的元素。
列表的基礎(chǔ)用法這里就不再介紹了,這里主要介紹一下列表推導(dǎo)。

列表推導(dǎo)和可讀性

列表推導(dǎo)是構(gòu)建列表的快捷方式,并且有更好的可讀性。
先看下面兩段代碼:

#1. 把一個(gè)字符串變成 unicode 碼位的列表

>>> symbols = "$&@#%^&*"
>>> codes = []
>>> for symbol in symbols:
        codes.append(ord(symbol))

>>> codes
[36, 38, 64, 35, 37, 94, 38, 42]

#2. 把一個(gè)字符串變成 unicode 碼位的列表 使用列表推導(dǎo)

>>> symbols = "$&@#%^&*"
>>> codes = [ord(s) for s in symbols]
>>> codes
[36, 38, 64, 35, 37, 94, 38, 42]

對比發(fā)現(xiàn),如果理解列表推導(dǎo)的話,第二段代碼比第一段更簡潔可讀性也更好。
當(dāng)然,列表推導(dǎo)也不應(yīng)該被濫用,通常的原則是只用列表推導(dǎo)來創(chuàng)建新的列表,并且盡量保持簡短。
如果列表推導(dǎo)超過兩行,就應(yīng)該考慮要不要使用 for 循環(huán)重寫了。

NOTE

在 Python2 中列表推導(dǎo)有變量泄露的問題

#Python2 的例子

>>> x = "my precious"
>>> dummy = [x for x in "ABC"]
>>> x
"C"

這里 x 原來的值被取代了,變成了列表推導(dǎo)中的最后一個(gè)值,需要避免這個(gè)問題。好消息是 Python3解決了這個(gè)問題。

#Python3 的例子

>>> x = "ABC"
>>> dummy = [ord(x) for x in x]
>>> x 
"ABC"
>>> dummy
[65, 66, 67]

可以看到,這里 x 原有的值被保留了,列表推導(dǎo)也創(chuàng)建了正確的列表。

笛卡爾積

列表推導(dǎo)還可以生成兩個(gè)或以上的可迭代類型的笛卡爾積。

笛卡爾積是一個(gè)列表,列表里的元素是由輸入的可迭代類型的元素對構(gòu)成的元組,因此笛卡爾積列表的長度等于輸入變量的長度的成績,如圖所示:

# 使用列表推導(dǎo)計(jì)算笛卡爾積代碼如下

>>> suits = ["spades", "diamonds", "clubs", "hearts"]
>>> nums = ["A", "K", "Q"]
>>> cards = [(num, suit) for num in nums for suit in suits]
>>> cards
[("A", "spades"),
 ("A", "diamonds"),
 ("A", "clubs"),
 ("A", "hearts"),
 ("K", "spades"),
 ("K", "diamonds"),
 ("K", "clubs"),
 ("K", "hearts"),
 ("Q", "spades"),
 ("Q", "diamonds"),
 ("Q", "clubs"),
 ("Q", "hearts")]

這里得到的結(jié)果是先按數(shù)字排列,再按圖案排列。如果想先按圖案排列再按數(shù)字排列,只需要調(diào)整 for 從句的先后順序。

過濾序列元素

問題:你有一個(gè)數(shù)據(jù)序列,想利用一些規(guī)則從中提取出需要的值或者是縮短序列

最簡單的過濾序列元素的方法是使用列表推導(dǎo)。比如:

>>> mylist = [1, 4, -5, 10, -7, 2, 3, -1]
>>> [n for n in mylist if n >0]
[1, 4, 10, 2, 3]

使用列表推導(dǎo)的一個(gè)潛在缺陷就是若干輸入非常大的時(shí)候會(huì)產(chǎn)生一個(gè)非常大的結(jié)果集,占用大量內(nèi)存。這個(gè)時(shí)候,使用生成器表達(dá)式迭代產(chǎn)生過濾元素是一個(gè)好的選擇。

生成器表達(dá)式

生成器表達(dá)式遵守了迭代器協(xié)議,可以逐個(gè)產(chǎn)出元素,而不是先建立一個(gè)完整的列表,然后再把這個(gè)列表傳遞到某個(gè)構(gòu)造函數(shù)里。

生成器表達(dá)式的語法跟列表推導(dǎo)差不多,只需要把方括號換成圓括號。

# 使用生成器表達(dá)式創(chuàng)建列表

>>> pos = (n for n in mylist if n > 0)
>>> pos
 at 0x1006a0eb0>
>>> for x in pos:
... print(x) 
...
1
4
10 
2 
3

如果生成器表達(dá)式是一個(gè)函數(shù)調(diào)用過程中唯一的參數(shù),那么不需要額外再用括號把它圍起來。例如:

tuple(n for n in mylist)

如果生成器表達(dá)式是一個(gè)函數(shù)調(diào)用過程中其中一個(gè)參數(shù),此時(shí)括號是必須的。比如:

>>> import array
>>> array.array("list", (n for n in mylist))
array("list", [1, 4, 10, 2, 3])
實(shí)現(xiàn)一個(gè)優(yōu)先級隊(duì)列 問題

怎么實(shí)現(xiàn)一個(gè)按優(yōu)先級排序的隊(duì)列?并在這個(gè)隊(duì)列上每次 pop 操作總是返回優(yōu)先級最高的那個(gè)元素

解決方法

利用 heapq 模塊

heapq 是 python 的內(nèi)置模塊,源碼位于 Lib/heapq.py ,該模塊提供了基于堆的優(yōu)先排序算法。

堆的邏輯結(jié)構(gòu)就是完全二叉樹,并且二叉樹中父節(jié)點(diǎn)的值小于等于該節(jié)點(diǎn)的所有子節(jié)點(diǎn)的值。這種實(shí)現(xiàn)可以使用 heap[k] <= heap[2k+1] 并且 heap[k] <= heap[2k+2] (其中 k 為索引,從 0 開始計(jì)數(shù))的形式體現(xiàn),對于堆來說,最小元素即為根元素 heap[0]。

可以通過 list 對 heap 進(jìn)行初始化,或者通過 api 中的 heapify 將已知的 list 轉(zhuǎn)化為 heap 對象。

heapq 提供的一些方法如下:

heap = [] #創(chuàng)建了一個(gè)空堆

heapq.heappush(heap, item):向 heap 中插入一個(gè)元素

heapq.heappop(heap):返回 root 節(jié)點(diǎn),即 heap 中最小的元素

heapq.heappushpop(heap, item):向 heap 中加入 item 元素,并返回 heap 中最小元素

heapq.heapify(x)

heapq.nlargest(n, iterable, key=None):返回可枚舉對象中的 n 個(gè)最大值,并返回一個(gè)結(jié)果集 list,key 為對該結(jié)果集的操作

heapq.nsmallest(n, iterable, key=None):同上相反

實(shí)現(xiàn)如下:

import heapq
class PriorityQueue: 
    def __init__(self):
        self._queue = []
        self._index = 0
        
    def push(self, item, priority):
        heapq.heappush(self._queue, (-priority, self._index, item)) 
        self._index += 1
        
    def pop(self):
        return heapq.heappop(self._queue)[-1]

下面是它的使用方法:

>>> class Item:
        def __init__(self, name):
            self.name = name
        def __repr__(self):
            return "Item({!r})".format(self.name)
            
>>> q = PriorityQueue()
>>> q.push(Item("foo"), 1)
>>> q.push(Item("bar"), 5)
>>> q.push(Item("spam"), 4)
>>> q.push(Item("grok"), 1)
>>> q.pop()
Item("bar") 
>>> q.pop() 
Item("spam") 
>>> q.pop() 
Item("foo") 
>>> q.pop() 
Item("grok")

通過執(zhí)行結(jié)果我們可以發(fā)現(xiàn),第一個(gè) pop() 操作返回優(yōu)先級最高的元素。兩個(gè)優(yōu)先級相同的元素(foo 和 grok),pop 操作按照它們被插入到隊(duì)列的順序返回。

函數(shù) heapq.heappush() 和 heapq.heappop() 分別在隊(duì)列 queue 上插入和刪除第一個(gè)元素,并且隊(duì)列 queue 保證 第一個(gè)元素?fù)碛凶钚?yōu)先級。 heappop() 函數(shù)總是返回 最小的 的元素,這就是保證隊(duì)列 pop 操作返回正確元素的關(guān)鍵。另外,由于 push 和 pop 操作時(shí)間復(fù)雜度為 O(log N),其中 N 是堆的大小,因此就算是 N 很大的時(shí)候它們 運(yùn)行速度也依舊很快。
在上面代碼中,隊(duì)列包含了一個(gè) (-priority, index, item) 的元組。優(yōu)先級為負(fù) 數(shù)的目的是使得元素按照優(yōu)先級從高到低排序。這個(gè)跟普通的按優(yōu)先級從低到高排序的堆排序恰巧相反。
index 變量的作用是保證同等優(yōu)先級元素的正確排序。通過保存一個(gè)不斷增加的 index 下標(biāo)變量,可以確保元素按照它們插入的順序排序。而且, index 變量也在相 同優(yōu)先級元素比較的時(shí)候起到重要作用。

實(shí)現(xiàn)上邊排序的關(guān)鍵是 元組是支持比較的:

>>> a = (1, Item("foo")) 
>>> b = (5, Item("bar")) 
>>> a < b
True
>>> c = (1, Item("grok"))
>>> a < c
Traceback (most recent call last):
File "", line 1, in  
TypeError: unorderable types: Item() < Item()

當(dāng)?shù)谝粋€(gè)值大小相等時(shí),由于Item 并不支持比較會(huì)拋出 TypeError。為了避免上述錯(cuò)誤,我們引入了index(不可能用兩個(gè)元素有相同的 index 值), 變量組成了(priority, index, item) 三元組?,F(xiàn)在再比較就不會(huì)出現(xiàn)上述問題了:

>>> a = (1, 0, Item("foo")) 
>>> b = (5, 1, Item("bar")) 
>>> c = (1, 2, Item("grok")) 
>>> a < b
True
>>> a < c 
True

主要介紹列表、列表推導(dǎo)有關(guān)的話題,最后演示如何用heapq列表實(shí)現(xiàn)一個(gè)優(yōu)先級隊(duì)列。下一篇介紹元組

參考鏈接

Heap queue algorithm


最后,感謝女朋友支持。

歡迎關(guān)注(April_Louisa) 請我喝芬達(dá)

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/40812.html

相關(guān)文章

  • Python學(xué)習(xí)之路21-序列構(gòu)成數(shù)組

    摘要:第行把具名元組以的形式返回。對序列使用和通常號兩側(cè)的序列由相同類型的數(shù)據(jù)所構(gòu)成當(dāng)然不同類型的也可以相加,返回一個(gè)新序列。從上面的結(jié)果可以看出,它雖拋出了異常,但仍完成了操作查看字節(jié)碼并不難,而且它對我們了解代碼背后的運(yùn)行機(jī)制很有幫助。 《流暢的Python》筆記。接下來的三篇都是關(guān)于Python的數(shù)據(jù)結(jié)構(gòu),本篇主要是Python中的各序列類型 1. 內(nèi)置序列類型概覽 Python標(biāo)準(zhǔn)庫...

    ralap 評論0 收藏0
  • Python 進(jìn)階之路 (八) 最用心推導(dǎo)式詳解 (附簡單實(shí)戰(zhàn)源碼)

    摘要:什么是推導(dǎo)式大家好,今天為大家?guī)韱栁易钕矚g的推導(dǎo)式使用指南,讓我們先來看看定義推導(dǎo)式是的一種獨(dú)有特性,推導(dǎo)式是可以從一個(gè)數(shù)據(jù)序列構(gòu)建另一個(gè)新的數(shù)據(jù)序列的結(jié)構(gòu)體。 什么是推導(dǎo)式 大家好,今天為大家?guī)韱栁易钕矚g的Python推導(dǎo)式使用指南,讓我們先來看看定義~ 推導(dǎo)式(comprehensions)是Python的一種獨(dú)有特性,推導(dǎo)式是可以從一個(gè)數(shù)據(jù)序列構(gòu)建另一個(gè)新的數(shù)據(jù)序列的結(jié)構(gòu)體。...

    hufeng 評論0 收藏0
  • 流暢python

    摘要:流暢的中有很多奇技淫巧,整本書都在強(qiáng)調(diào)如何最大限度地利用標(biāo)準(zhǔn)庫。常見的扁平序列包括,,等。數(shù)組支持所有跟可變序列有關(guān)的操作,包括和。和用于指定列表的區(qū)間,默認(rèn)是使用整個(gè)列表。但是元組的賦值不被允許,當(dāng)異發(fā)生時(shí) 流暢的python中有很多奇技淫巧,整本書都在強(qiáng)調(diào)如何最大限度地利用Python 標(biāo)準(zhǔn)庫。介紹了很多python的不常用的數(shù)據(jù)類型、操作、庫等,對于入門python后想要提升對p...

    Alan 評論0 收藏0
  • 流暢 Python - 1. 序列類型

    摘要:后面的先出場的是關(guān)于已經(jīng)有序的序列的操作。這次使用的模塊是,可以用來搜索,返回的是該元素應(yīng)該插入在序列的哪個(gè)位置。但不可否認(rèn),數(shù)組對于一些固定類型的元素,操作效率更高。最后出場的是隊(duì)列了。 今天看第二章,但是沒看完,被其他事纏住了。 首先登場的是 Python 的內(nèi)置序列類型,對此我并不陌生,但也有幾個(gè)生面孔,但是基本的操作我想應(yīng)該是一樣的,只是類型不同。 對列表的操作中,經(jīng)常用到的就...

    IntMain 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<