寫在前面
「 傍晚時分,你坐在屋檐下,看著天慢慢地黑下去,心里寂寞而凄涼,感到自己的生命被剝奪了。當時我是個年輕人,但我害怕這樣生活下去,衰老下去。在我看來,這是比死亡更可怕的事。--------王小波」 數(shù)據(jù)結(jié)構(gòu)和算法Python內(nèi)置了許多非常有用的數(shù)據(jù)結(jié)構(gòu),比如列表(list)、集合(set)以及字典(dictionary)、元組(tuple)。在collections模塊中也包含了針對各種數(shù)據(jù)結(jié)構(gòu)的解決方案。 將序列分解為單獨的變量「我們有一個包含N個元素的元組或序列,現(xiàn)在想將它分解為N個單獨的變量。」 ┌──(liruilong?Liruilong)-[/mnt/e/docker]└─$ python3Python 3.9.10 (main, Jan 16 2022, 17:12:18)[GCC 11.2.0] on linuxType 'help', 'copyright', 'credits' or 'license' for more information.>>> p = (4,5)>>> x,y = p>>> x4>>> y5>>> 居然可以這樣,長見識了,類似于JavaScript ES6中的解構(gòu)賦值,如果當成函數(shù)對象來看,可以看做是拆包
當然,也支持深層次解構(gòu) >>> data = [ 'ACME',50,91.1,(2012,12,21)]>>> name,shares,price,(year,mon,day)=data>>> year2012>>> mon,day(12, 21)>>> 如果元素的數(shù)量不匹配,將得到一個錯誤提示。
實際上不僅僅只是元組或列表,只要對象恰好是可迭代的,那么就可以執(zhí)行分解操作。這包括字符串、文件、迭代器以及生成器。 >>> s = 'love'>>> a,b,c,d = s>>> a,b('l', 'o')>>> d'e'>>> 當做分解操作時,想丟棄某些特定的值??梢杂?'_'充當占位符,,這個在JavaScript ES6和Golang也都支持。
從任意長度的可迭代對象中分解元素「需要從某個可選代對象中分解出N個元素,但是這個可迭代對象的長度可能超過N,這會導致出現(xiàn)分解的值過多(too many values to unpack)的異常?!?/span> >>> record=('Dave','davedexample.com','773-555-1212','1847-555-1212')>>> name,email,*phone_numbers = record>>> name'Dave'>>> phone_numbers['773-555-1212', '1847-555-1212']>>> 類似于Java方法傳值的可變參數(shù)一樣,但是要比java高級的多,java的可變參數(shù)只能最后一個,python 則可以在任意位置
* 式的語法在選代一個變長的元組序列時尤其有用 records = [ ('foo', 1, 2), ('bar', 'hello'), ('foo', 3, 4),]def do_foo(x, y): print('foo', x, y)def do_bar(s): print('bar', s)for tag, *args in records: if tag == 'foo': do_foo(*args) elif tag == 'bar': do_bar(*args) 字符串拆分是的使用 : ''.split('')
也可 '*' 和 '_' 兩個連用,丟棄多個變量 >>> uname,*_,homedir,sh=line.split(':')>>> sh'/usr/bin/false'>>> 求和遞歸
保存最后N個元素(隊列)「我們希望在迭代或是其他形式的處理過程中對最后幾項記錄做一個有限的歷史記錄統(tǒng)計?!?/span> 保存有限的歷史記錄可算是collections.deque的完美應(yīng)用場景了 打印滿足條件的最后5條記錄 #deque(maxlen=N)創(chuàng)建了一個固定長度的雙端隊列from collections import dequedef search(lines, pattern, history=5): previous_lines=deque(maxlen=history) for line in lines: if pattern in line: # 生成器 yield line, previous_lines previous_lines.append(line)# Example use on a fileif __name__ == '__main__': with open('somefile.txt') as f: for line, prevlines in search(f,'python',5): for pline in prevlines: print(pline, end='') print(line, end='') print('-1'*20) deque(maxlen=N)創(chuàng)建了一個固定長度的雙端隊列
盡管可以在列表上手動完成這樣的操作(append、del),但隊列這種解決方案要優(yōu)雅得多,運行速度也快得多。從隊列兩端添加或彈出元素的復(fù)雜度都是O(1)。這和列表不同,當從列表的頭部插入或移除元素時,列表的復(fù)雜度為O(N) 找到最大或最小的N個元素「我們想在某個集合中找出最大或最小的N個元素?!?/span> heapq模塊中有兩個函數(shù)nlargest()和nsmallest(),當所要找的元素數(shù)量相對較小時,函數(shù)nlargest()和nsmallest()才是最適用的,nlargest()和nsmallest()的實際實現(xiàn)會根據(jù)使用它們的方式而有所不同,可能會相應(yīng)作出一些優(yōu)化措施(比如,當N的大小同輸入大小很接近時,就會采用排序的方法)。 >>> import heapq>>> nums=[1,8,2,23,7,-4,18,23,42,37,2]>>> heapq.nlargest(3,nums)[42, 37, 23]>>> heapq.nsmallest(3,nums)[-4, 1, 2]>>> 這兩個函數(shù)都可以接受一個參數(shù)key,從而允許它們工作在更加復(fù)雜的數(shù)據(jù)結(jié)構(gòu)之上
[{'name': 'YHOO', 'shares': 45, 'price': 16.35}, {'name': 'FB', 'shares': 200, 'price': 21.09}][{'name': 'FB', 'shares': 200, 'price': 21.09}, {'name': 'ACME', 'shares': 75, 'price': 115.65}] 如果正在尋找最大或最小的N個元素,且同集合中元素的總數(shù)目相比,N很小,那么heapq.heapify(heap)函數(shù)可以提供更好的性能。這些函數(shù)首先會在底層將數(shù)據(jù)轉(zhuǎn)化成列表,且元素會以堆的順序排列。
堆最重要的特性就是heap[0]總是最小那個的元素。此外,接下來的元素可依次通過heapq.heappop方法輕松找到。該方法會將第一個元素(最小的)彈出,然后以第二小的元素取而代之(這個操作的復(fù)雜度是O(logN),N代表堆的大小) 想找到最小或最大的元素(N=1時),那么用min()和max)會更加快。 >>> min(heap)2>>> max(heap)42>>> heap[2, 7, 8, 23, 42, 37, 18, 23]>>> 如果N和集合本身的大小差不多大,通常更快的方法是先對集合排序,然后做切片操作,使用sorted(items)[:N]或者sorted(items)[-N:])。 實現(xiàn)優(yōu)先級隊列「我們想要實現(xiàn)一個隊列,它能夠以給定的優(yōu)先級來對元素排序,且每次pop操作時都會返回優(yōu)先級最高的那個元素?!?/span> heapq模塊提供了堆排序算法的實現(xiàn),heapq有兩種方式創(chuàng)建堆,
隊列以元組(-priority,index,item)的形式組成。把priority取負值是為了讓隊列能夠按元素的優(yōu)先級從高到低的順序排列。一般情況下是最小堆。 變量index的作用是為了將具有相同優(yōu)先級的元素以適當?shù)捻樞蚺帕?。通過維護一個不斷遞增的索引,元素將以它們?nèi)?span>隊列時的順序來排列。沒有哪兩個元組會有相同的index值(一旦比較操作的結(jié)果可以確定,Python就不會再去比較剩下的元組元素了) 如果想將這個隊列用于線程間通信,還需要增加適當?shù)逆i和信號機制 在字典中將鍵映射到多個值上「我們想要一個能將鍵(key)映射到多個值的字典(即所謂的一鍵多值字典[multidict])」 字典是一種關(guān)聯(lián)容器,每個鍵都映射到一個單獨的值上。如果想讓鍵映射到多個值,需要將這多個值保存到另一個容器如列表或集合中。 為了能方便地創(chuàng)建這樣的字典,可以利用collections模塊中的defaultdict類。defaultdict的一個特點就是它會自動初始化第一個值,這樣只需關(guān)注添加元素即可。 defaultdict(<class 'list'>, {'a': [1, 2], 'b': [4]})>>> from collections import defaultdict>>> d = defaultdict(list)>>> d['a'].append(1)>>> d['a'].append(2)>>> d['b'].append(4)>>> ddefaultdict(<class 'list'>, {'a': [1, 2], 'b': [4]})
用于分組,有一種java8里面Stream的味道 d=defaultdict(list)for key, value in pairs: d[key]. append(value) 讓字典保持有序要控制字典中元素的順序,可以使用collections模塊中的OrderedDict類。當對字典做迭代時,它會嚴格按照元素初始添加的順序進行。例如:
當想構(gòu)建一個映射結(jié)構(gòu)以便稍后對其做序列化或編碼成另一種格式時,OrderedDict就顯得特別有用。例如,如果想在進行JSON編碼時精確控制各字段的順序,那么只要首先在OrderedDict中構(gòu)建數(shù)據(jù)就可以了。 >>> import json>>> json.dumps(d)'{'1': 1, '2': 2, '3': 3}'>>> OrderedDict 內(nèi)部維護了一個雙向鏈表,它會根據(jù)元素加入的順序來排列鍵的位置。第一個新加入的元素被放置在鏈表的末尾。接下來對已存在的鍵做重新賦值不會改變鍵的順序。 OrderedDict的大小是普通字典的2倍多,這是由于它額外創(chuàng)建的鏈表所致。因此,如果打算構(gòu)建一個涉及大量OrderedDict實例的數(shù)據(jù)結(jié)構(gòu)(例如從CSV文件中讀取100000行內(nèi)容到OrderedDict列表中),那么需要認真對應(yīng)用做需求分析,是否可以用內(nèi)存換便利 與字典有關(guān)的計算問題「我們想在字典上對數(shù)據(jù)執(zhí)行各式各樣的計算(比如求最小值、最大值、排序等)。」 通常會利用zip()將字典的鍵和值反轉(zhuǎn)過來
同樣,要對數(shù)據(jù)排序只要使用zip()再配合sorted()就可以了 >>> sorted(zip(prices.values(),prices.keys()))[(10.75, 'FB'), (37.2, 'HPQ'), (45.23, 'ACME'), (205.55, 'IBM1'), (612.78, 'AAPL')]>>> zip()創(chuàng)建了一個迭代器,它的內(nèi)容只能被消費一次,嘗試對字典的值做計算??梢岳米值涞?span>values()方法來解決這個問題:但是對于K的獲取并不方便。 在計算min()和max()時,如果碰巧value的值相同,則將返回擁有最小或最大key值的那個條目。 在兩個字典中尋找相同點(交集)「有兩個字典,我們想找出它們中間可能相同的地方(相同的鍵、相同的值等)。」 關(guān)于字典的鍵有一個很少有人知道的特性,那就是它們也支持常見的集合操作,比如求并集、交集和差集。 如果需要對字典的鍵做常見的集合操作,那么就能直接使用keys-view對象而不必先將它們轉(zhuǎn)化為集合。獲取到迭代器,可以直接對字典進行運算交集差集
字典的items()方法返回由(key,value)對組成的items-view對象。這個對象支持類似的集合操作,可用來完成找出兩個字典間有哪些鍵值對有相同之處的操作。 >>> a.items() & b.items(){('y', 2)}>>> 也可以對字典進行過濾。
字典的values()方法并不支持集合操作。部分原因是因為在字典中鍵和值是不同的,從值的角度來看并不能保證所有的值都是唯一的。 從序列中移除重復(fù)項且保持元素間順序不變「我們想去除序列中出現(xiàn)的重復(fù)元素,但仍然保持剩下的元素順序不變。」 如果序列中的值是可哈希(hashable)的,那么這個問題可以通過使用集合和生成器輕松解決。 def dedupe(items): seen = set() for item in items: if item not in seen: yield item #生成有序列表 seen.add(item)a = [1, 5, 2, 1, 9, 1, 5, 10]print(list(dedupe(a)))==========[1, 5, 2, 9, 10] 如果沒辦法生成哈希值,如果一個對象是可哈希的,那么在它的生存期內(nèi)必須是不可變的,它需要有一個hash()方法。 整數(shù)、浮點數(shù)、字符串、元組都是不可變的。
這里參數(shù)key的作用是指定一個函數(shù)用來將序列中的元素轉(zhuǎn)換為可哈希的類型,這么做的目的是為了檢測重復(fù)項。即行為參數(shù)化,lambda表達式的使用。 對切片命名「我們的代碼已經(jīng)變得無法閱讀,到處都是硬編碼的切片索引,我們想將它們清理干凈」 即通過對切片變量的定義,把可變的部分抽離出來。一般來說,內(nèi)置的slice()函數(shù)會創(chuàng)建一個切片對象,可以用在任何允許進行切片操作的地方。 >>> recode='343534534532423435346547543534534534534564634534'>>> int(recode[12:14]) * float(recode[10:13])13608.0>>> a = slice(12,14)>>> b = slice(10,13)>>> int(recode[a]) * float(recode[b])13608.0 查看切片對象屬性
通過使用indices(size)方法將切片映射到特定大小的序列上。 >>> s = 'liruilong'>>> a.indices(len(s))(9, 9, 1)>>> 找出序列中出現(xiàn)次數(shù)最多的元素「怎樣找出一個序列中出現(xiàn)次數(shù)最多的元素呢?」 collections.Counter 類就是專門為這類問題而設(shè)計的,它甚至有一個有用的most_common()方法直接給了你答案。 出現(xiàn)頻率最高的 3 個單詞
作為輸入,Counter 對象可以接受任意的hashable序列對象。在底層實現(xiàn)上,一個Counter對象就是一個字典,將元素映射到它出現(xiàn)的次數(shù)上 >>> word_countsCounter({'eyes': 8, 'the': 5, 'look': 4, 'into': 3, 'my': 3, 'around': 2, 'not': 1, 'don't': 1, 'you're': 1, 'under': 1})>>> 可以整合其他的容器來統(tǒng)計數(shù)據(jù),下面為在原有基礎(chǔ)上整合一個列表
當然,也可以通過update()方法,我們可以看到,update方法并沒有替換原來的value,而是進行了累加,和字典的update方法有區(qū)別 >>> word_counts.update(morewords)>>> word_countsCounter({'eyes': 10, 'my': 5, 'the': 5, 'look': 4, 'into': 3, 'not': 3, 'around': 2, 'why': 2, 'are': 2, 'you': 2, 'looking': 2, 'in': 2, 'don't': 1, 'you're': 1, 'under': 1})>>> Counter生成的數(shù)據(jù)字典可以進行數(shù)據(jù)運算,只是針對相同Key的Value而言
通過某個關(guān)鍵字排序一個字典列表「你有一個字典列表,你想根據(jù)某個或某幾個字典字段來排序這個列表?!?/span> 通過使用 operator 模塊的 itemgetter 函數(shù),可以非常容易的排序這樣的數(shù)據(jù)結(jié)構(gòu)。感覺和heapq模塊中的函數(shù)nlargest()和nsmallest()有些類似 >>> rows = [... {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003},... {'fname': 'David', 'lname': 'Beazley', 'uid': 1002},... {'fname': 'John', 'lname': 'Cleese', 'uid': 1001},... {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}... ]>>> from operator import itemgetter>>> sorted(rows, key=itemgetter('fname'))[{'fname': 'Big', 'lname': 'Jones', 'uid': 1004}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}, {'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'John', 'lname': 'Cleese', 'uid': 1001}] rows 被傳遞給接受一個關(guān)鍵字參數(shù)的sorted()內(nèi)置函數(shù),參數(shù)是 callable 類型,從rows中接受一個單一元素,然后返回被用來排序的值,itemgetter() 函數(shù)就是負責創(chuàng)建這個 callable 對象的。
itemgetter() 函數(shù)也支持多個 keys,如果你傳入多個索引參數(shù)給 itemgetter() ,它生成的 callable 對象會返回一個包含所有元素值的元組,并且 sorted() 函數(shù)會根據(jù)這個元組中元素順序去排序比如下面的代碼 >>> sorted(rows, key=itemgetter('lname','fname'))[{'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'John', 'lname': 'Cleese', 'uid': 1001}, {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}]>>> itemgetter() 有時候也可以用 lambda 表達式代替
使用 itemgetter() 方式會運行的稍微快點。同樣適用于 min() 和 max() 等函數(shù) >>> import heapq>>> heapq.nsmallest(2,rows,key=itemgetter('fname'))[{'fname': 'Big', 'lname': 'Jones', 'uid': 1004}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}]>>> min(rows,key=itemgetter('fname')){'fname': 'Big', 'lname': 'Jones', 'uid': 1004}>>> 排序不支持原生比較的對象「你想排序類型相同的對象,但是他們不支持原生的比較操作」 內(nèi)置的 sorted() 函數(shù)有一個關(guān)鍵字參數(shù) key ,可以傳入一個 callable 對象給它,這個 callable 對象對每個傳入的對象返回一個值,這個值會被 sorted 用來排序
另外一種方式是使用 operator.attrgetter() 來代替 lambda 函數(shù): from operator import attrgetter print(sorted(users, key=attrgetter('user_id'))) attrgetter() 函數(shù)通常會運行的快點,并且還能同時允許多個字段進行比較
跟 operator.itemgetter() 函數(shù)作用于字典類型很類似 ,同樣適用于像 min() 和 max() 之類 通過某個字段將記錄分組「你有一個字典或者實例的序列,然后你想根據(jù)某個特定的字段比如 date 來分組迭代訪問?!?/span> itertools.groupby() 函數(shù)對于這樣的數(shù)據(jù)分組操作非常實用。為了演示,假設(shè)你已經(jīng)有了下列的字典列表 >>> rows = [... {'address': '5412 N CLARK', 'date': '07/01/2012'},... {'address': '5148 N CLARK', 'date': '07/04/2012'},... {'address': '5800 E 58TH', 'date': '07/02/2012'},... {'address': '2122 N CLARK', 'date': '07/03/2012'},... {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'},... {'address': '1060 W ADDISON', 'date': '07/02/2012'},... {'address': '4801 N BROADWAY', 'date': '07/01/2012'},... {'address': '1039 W GRANVILLE', 'date': '07/04/2012'},... ]>>> from operator import itemgetter>>> from itertools import groupby>>> rows.sort(key=itemgetter('date'))>>> for date, items in groupby(rows, key=itemgetter('date')):... print(date)... for i in items:... print(' ', i)...07/01/2012 {'address': '5412 N CLARK', 'date': '07/01/2012'} {'address': '4801 N BROADWAY', 'date': '07/01/2012'}07/02/2012 {'address': '5800 E 58TH', 'date': '07/02/2012'} {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'} {'address': '1060 W ADDISON', 'date': '07/02/2012'}07/03/2012 {'address': '2122 N CLARK', 'date': '07/03/2012'}07/04/2012 {'address': '5148 N CLARK', 'date': '07/04/2012'} {'address': '1039 W GRANVILLE', 'date': '07/04/2012'}>>> groupby()函數(shù)掃描整個序列并且查找連續(xù)相同值(或者根據(jù)指定key函數(shù)返回值相同)的元素序列。在每次選代的時候,它會返回一個值和一個選代器對象,這個選代器對象可以生成元素值全部等于上面那個值的組中所有對象。 「一個非常重要的準備步驟是要根據(jù)指定的字段將數(shù)據(jù)排序」 。因為groupby()僅僅檢查連續(xù)的元素 如果你僅僅只是想根據(jù)date字段將數(shù)據(jù)分組到一個大的數(shù)據(jù)結(jié)構(gòu)中去,并且允許隨機訪問,最好使用defaultdict()來構(gòu)建一個多值字典
過濾序列元素「你有一個數(shù)據(jù)序列,想利用一些規(guī)則從中提取出需要的值或者是縮短序列」 最簡單的過濾序列元素的方法就是使用列表推導 >>> mylist = [1, 4, -5, 10, -7, 2, 3, -1]>>> [n for n in mylist if n > 0][1, 4, 10, 2, 3]>>> [n for n in mylist if n < 0][-5, -7, -1]>>> 潛在缺陷就是如果輸入非常大的時候會產(chǎn)生一個非常大的結(jié)果集,占用大量內(nèi)存,可以用生成器表達式迭代產(chǎn)生過濾的元素。
過濾規(guī)則比較復(fù)雜,將過濾代碼放到一個函數(shù)中,然后使用內(nèi)建的filter()函數(shù) values = ['1', '2', '-3', '-', '4', 'N/A', '5']def is_int(val): try: x = int(val) return True except ValueError: return False# filter() 函數(shù)創(chuàng)建了一個迭代器ivals = list(filter(is_int, values))print(ivals) 在過濾的時候轉(zhuǎn)換數(shù)據(jù)
過濾操作的一個變種就是將不符合條件的值用新的值代替, >>> [n if n > 0 else 0 for n in mylist][1, 4, 0, 10, 0, 2, 3, 0]>>> [n if n < 0 else 0 for n in mylist][0, 0, -5, 0, -7, 0, 0, -1]>>> itertools.compress(),它以一個 iterable 對象和一個相對應(yīng)的 Boolean 選擇器序列作為輸入?yún)?shù).然后輸出 iterable 對象中對應(yīng)選擇器為 True 的元素當你需要用另外一個相關(guān)聯(lián)的序列來過濾某個序列的時候,這個函數(shù)是非常有用的
compress() 也是返回的一個迭代器 從字典中提取子集「你想構(gòu)造一個字典,它是另外一個字典的子集」 是使用字典推導 >>> prices = {... 'ACME': 45.23,... 'AAPL': 612.78,... 'IBM': 205.55,... 'HPQ': 37.20,... 'FB': 10.75... }>>> {key: value for key, value in prices.items() if value > 200}{'AAPL': 612.78, 'IBM': 205.55}>>> tech_names = {'AAPL', 'IBM', 'HPQ', 'MSFT'}>>> {key: value for key, value in prices.items() if key in tech_names}{'AAPL': 612.78, 'IBM': 205.55, 'HPQ': 37.2}>>> 字典推導能做到的,通過創(chuàng)建一個元組序列然后把它傳給 dict()也能實現(xiàn),字典推導方式表意更清晰,并且實際上也會運行的更快些
將名稱映射到序列的元素中「你有一段通過下標訪問列表或者元組中元素的代碼,但是這樣有時候會使得你的代碼難以閱讀,于是你想通過名稱來訪問元素?!?/span> collections.namedtuple() 函數(shù)通過使用一個普通的元組對象來幫你解決這個問題。 >>> from collections import namedtuple>>> Subscriber = namedtuple('Subscriber', ['addr', 'joined'])>>> sub = Subscriber('jonesy@example.com', '2012-10-19')>>> subSubscriber(addr='jonesy@example.com', joined='2012-10-19') namedtuple() 返回 Python 中標準元組類型子類的一個工廠方法,傳遞一個類型名和你需要的字段給它,然后它就會返回一個類,雖然看起來像一個類實例,但是是可交換的。
命名元組的一個主要用途是將你的代碼從下標操作中解脫出來,數(shù)據(jù)查詢中返回很大一個元組,通過下標去操作其中的元素有很多可變性,如果使用元組命名則不用考慮 def compute_cost(records): total = 0.0 for rec in records: total += rec[1] * rec[2] return totalfrom collections import namedtupleStock = namedtuple('Stock', ['name', 'shares', 'price'])def compute_cost(records): total = 0.0 for rec in records: s = Stock(*rec) total += s.shares * s.price return total 命名元組另一個用途就是作為字典的替代,字典存儲需要更多的內(nèi)存空間 需要構(gòu)建一個非常大的包含字典的數(shù)據(jù)結(jié)構(gòu),那么使用命名元組會更加高效 是需要注意一個命名元組是不可更改的.如果你真的需要改變后的屬性,那么可以使用命名元組實例的replace()方法
它會創(chuàng)建一個全新的命名元組并將對應(yīng)的字段用新的值取代 _replace() 方法還有一個很有用的特性就是當你的命名元組擁有可選或者缺失字段時候,它是一個非常方便的填充數(shù)據(jù)的方法。你可以先創(chuàng)建一個包含缺省值的原型元組,然后使用 _replace() 方法創(chuàng)建新的值被更新過的實例,類似于類實例的初始化調(diào)用構(gòu)造函數(shù) >>> from collections import namedtuple>>> Stock = namedtuple('Stock', ['name', 'shares', 'price', 'date', 'time'])>>> stock_prototype = Stock('', 0, 0.0, None, None)>>> def dict_to_stock(s):... return stock_prototype._replace(**s)...>>> a = {'name': 'ACME', 'shares': 100, 'price': 123.45}>>> dict_to_stock(a)Stock(name='ACME', shares=100, price=123.45, date=None, time=None)>>> b = {'name': 'ACME', 'shares': 100, 'price': 123.45, 'date': '12/17/2012'}>>> dict_to_stock(b)Stock(name='ACME', shares=100, price=123.45, date='12/17/2012', time=None)>>> 如果你的目標是定義一個需要更新很多實例屬性的高效數(shù)據(jù)結(jié)構(gòu),那么命名元組并不是你的最佳選擇。這時候你應(yīng)該考慮定義一個包含 slots 方法的類 同時對數(shù)據(jù)做轉(zhuǎn)換和換算「我們需要調(diào)用一個換算(reduction)函數(shù)(例如sumO、min)、max)),但首先得對數(shù)據(jù)做轉(zhuǎn)換或篩選。」
將多個映射合并為單個映射「我們有多個字典或映射,想在邏輯上將它們合并為一個單獨的映射結(jié)構(gòu),以此執(zhí)行某些特定的操作,比如查找值或檢查鍵是否存在?!?/span> 一種簡單的萬法是利用collections模塊中的ChainMap類來解決 >>> a={'x':1,'z':3}>>> b={'y':2,'z':4}>>> from collections import ChainMap>>> ChainMap(a,b)ChainMap({'x': 1, 'z': 3}, {'y': 2, 'z': 4})>>> ChainMap可接受多個映射然后在邏輯上使它們表現(xiàn)為一個單獨的映射結(jié)構(gòu)。但是,這些映射在字面上并不會合并在一起。相反,ChainMap只是簡單地維護一個記錄底層映射關(guān)系的列表,然后重定義常見的字典操作來掃描這個列表。大部分的操作都能正常工作。
如果有重復(fù)的鍵,那么這里會采用第一個映射中所對應(yīng)的值。修改映射的操作總是會作用在列出的第一個映射結(jié)構(gòu)上。 ChainMap與帶有作用域的值,比如編程語言中的變量(即全局變量、局部變量等)一起工作時特別有用。 >>> v = ChainMap()>>> v['x'] = 1>>> v = v.new_child()>>> v['x'] = 2>>> v = v.new_child()>>> v['x'] = 3>>> vChainMap({'x': 3}, {'x': 2}, {'x': 1})>>> v['x']3>>> v = v.parents>>> v['x']...............2>>> v = v.parents>>> v['x']1>>> vChainMap({'x': 1})>>> 作為ChainMap的替代方案,我們可能會考慮利用字典的update()方法將多個字典合并在一起。但是破壞了原始的數(shù)據(jù)結(jié)構(gòu),而ChainMap使用的就是原始的字典,因此它不會產(chǎn)生這種令人不悅的行為。
|
|