python cookbook 讀書筆記--數(shù)據(jù)結構和算法(三)

刪除相同的元素并保持元素原有的順序

  • 官方對于是否可哈希的解釋說明:
    如果一個對象在其生命周期內有一個固定不變的哈希值 (這需要hash()方法) 且可以與其他對象進行比較操作 (這需要eq()方法) ,那么這個對象就是可哈希對象 (hashable) ,可哈希對象必須有相同的哈希值才算作相等。
    由于字典 (dict) 的鍵 (key) 和集合 (set) 內部使用到了哈希值,所以只有可哈希 (hashable) 對象才能被用作字典的鍵和集合的元素。
    所有python內置的不可變對象都是可哈希的,同時,可變容器 (比如:列表 (list) 或者字典 (dict) ) 都是不可哈希的。用戶自定義的類的實例默認情況下都是可哈希的;它們跟其它對象都不相等 (除了它們自己) ,它們的哈希值來自id()方法

eg:

  • 如果值為一個可哈希對像,那么利用集合就可以做到
def deque(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(deque(a))) 
  • 假如值為不可哈希對像時:
def dedupe(items, key=None):
    seen = set()
    for item in items:
        val = item if key is None else key(item)
        if val not in seen:
            yield item
        seen.add(val)
     

a = [ {'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}, {'x':2, 'y':4}]

print(list(dedupe(a, key=lambda d: (d['x'],d['y']))))  
print(list(dedupe(a, key=lambda d: d['x']))) 

命名切片相關

items = [0, 1, 2, 3, 4, 5, 6]
a = slice(2, 4) # 內置slice函數(shù)可以創(chuàng)建一個切片對像,用到任何切片允許使用的地方.
print(items[a])

序列中出現(xiàn)次數(shù)最多的元素

  • 使用collections.Counter 有用的most_common()
words = [ 'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes', 'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', 'the', 'eyes', "don't", 'look', 'around', 'the', 'eyes', 'look', 'into', 'my', 'eyes', "you're", 'under' ]
from collections import Counter
print(Counter(words)) # Counter({'eyes': 8, 'the': 5, 'look': 4, 'into': 3, 'my': 3, 'around': 2, 'not': 1, "don't": 1, "you're": 1, 'under': 1})
print(Counter(words).most_common(3))  #[('eyes', 8), ('the', 5), ('look', 4)]

word_counts = Counter(words) #  Counter可以當作字典來操作.
print(word_counts['eyes'])  # 8  

通過某個關鍵字排序一個字典列表

  • 想根據(jù)一個字典的某個或者幾個字典來排序這個列表,可以使用operatortemgetter函數(shù).
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
rows_by_fname = sorted(rows, key=itemgetter('fname'))
rows_by_uid = sorted(rows, key=itemgetter('uid'))
print(rows_by_fname)
print(rows_by_uid)

上面這種情況也可以使用lambda來完成,比如
rows_by_fname = sorted(rows, key=lambda r: r['fname'])
但使用itemgetter會快一些,看個人愛好與需要.

通過某個字段將紀錄分組

  • 想根據(jù)某個特定的字段來迭代訪問分組,可以使用itertools.groupby
rows =[ {'address': '5412 NCLARK','date':'07/01/2012'}, {'address': '5148 NCLARK','date':'07/04/2012'}, {'address': '5800 E58TH','date':'07/02/2012'}, {'address': '2122 NCLARK','date':'07/03/2012'}, {'address': '5645 NRAVENSWOOD', 'date': '07/02/2012'}, {'address': '1060 WADDISON','date': '07/02/2012'}, {'address': '4801 NBROADWAY','date': '07/01/2012'}, {'address': '1039 WGRANVILLE', 'date': '07/04/2012'}, ]
from operator import itemgetter
from itertools import groupby
rows.sort(key=itemgetter('date'))
# print(rows)
for date, items in groupby(rows, key=itemgetter('date')):
    print(date)
    for i in items:
        print('',i)

輸出為:

07/01/2012
 {'address': '5412 NCLARK', 'date': '07/01/2012'}
 {'address': '4801 NBROADWAY', 'date': '07/01/2012'}
07/02/2012
 {'address': '5800 E58TH', 'date': '07/02/2012'}
 {'address': '5645 NRAVENSWOOD', 'date': '07/02/2012'}
 {'address': '1060 WADDISON', 'date': '07/02/2012'}
07/03/2012
 {'address': '2122 NCLARK', 'date': '07/03/2012'}
07/04/2012
 {'address': '5148 NCLARK', 'date': '07/04/2012'}
 {'address': '1039 WGRANVILLE', 'date': '07/04/2012'}

假如根據(jù)date字段將數(shù)據(jù)分組到一個大的數(shù)據(jù)結構中去,并且允許隨機訪 問, 那么你最好使用 defaultdict() 來構建一個多值字典.

from collections import defaultdict
row_by_date = defaultdict(list)
for row in rows:
    row_by_date[row['date']].append(row)
print(row_by_date)

輸出為:

defaultdict(<class 'list'>, {'07/01/2012': [{'address': '5412 NCLARK', 'date': '07/01/2012'}, {'address': '4801 NBROADWAY', 'date': '07/01/2012'}], '07/02/2012': [{'address': '5800 E58TH', 'date': '07/02/2012'}, {'address': '5645 NRAVENSWOOD', 'date': '07/02/2012'}, {'address': '1060 WADDISON', 'date': '07/02/2012'}], '07/03/2012': [{'address': '2122 NCLARK', 'date': '07/03/2012'}], '07/04/2012': [{'address': '5148 NCLARK', 'date': '07/04/2012'}, {'address': '1039 WGRANVILLE', 'date': '07/04/2012'}]})

假如需要可以根據(jù)
for r in row_by_date['07/01/2012']: print(r) 這種方式來獲取.

defaultdict的主要目的是,當Key不存在時不會報錯,比如下面的情況

a1 = dict()
s = 'mississippi'
# print(a1['a']) ====>TypeError: 'type' object does not support item assignment

d2 = defaultdict(int)
# print(d2['a'])===>運行正常
for k in s:
    d2[k] +=1
print(d2)

過濾序列元素

  • 有一個數(shù)據(jù)序列,想利用一些規(guī)則從中提取出需要的值或者是縮短序列
mylist= [1, 4, -5,10,-7,2,3,-1]
print([n for n in mylist if n > 0])
# 上面的缺點就是輸入非常大的時候會占用非常大的結果集,占用大量內存,這時候可以使用生成器表達式迭代產生過濾元素.
post = (n for n in mylist if n > 0)
for x in post:
    print(x)

復雜情況可以使用filter()函數(shù),filter() 函數(shù)創(chuàng)建了一個迭代器. 所以只需要寫個函數(shù)來驗證值,如下所示

values= ['1', '2', '-3','-','4', 'N/A', '5']
def is_int(val):
    try:
        x = int(val)
        return True
    except ValueError:
        return False
isvals = list(filter(is_int, values))
print(isvals)

但列表推導是首先可以考慮的一種形式

values= ['1', '2', '-3','-','4', 'N/A', '5']
def is_int(val):
    try:
        x = int(val)
        return True
    except ValueError:
        return False
isvals = list(filter(is_int, values))
print(isvals)
clip_neg = [n if n > 0 else 0 for n in mylist]
print(clip_neg)

從字典中提取子集

  • 比較快的情況,列表推導
prices= { 'ACME':45.23, 'AAPL':612.78, 'IBM':205.55, 'HPQ':37.20, 'FB': 10.75 }
# 返回一個價格大于200的字典
p1 = {key:value for key, value in prices.items() if value > 200}
print(p1) #{'AAPL': 612.78, 'IBM': 205.55}
tech_names= {'AAPL', 'IBM', 'HPQ', 'MSFT'}
p2 = {key:value for key,value in prices.items() if key in tech_names}
print(p2) #{'AAPL': 612.78, 'IBM': 205.55, 'HPQ': 37.2}

** 映射名稱到序列元素 **

  • 通過下標訪問列表或者元組中元素的代碼,但是這樣有時候會使得你的代碼難以 閱讀, 于是你想通過名稱來訪問元素
    collections.namedtuple()函數(shù)通過使用一個普通的元組對象來幫你解決這個問題。 這個 函數(shù)實際上是一個返回Python中標準元組類型子類的一個工廠方法。 你需要傳遞一個類 型名和你需要的字段給它,然后它就會返回一個類,你可以初始化這個類,為你定義的字 段傳遞值等。
from collections import namedtuple
Subscriber = namedtuple('Subscriber', ['addr','joined'])
sub = Subscriber('jonesy@example.com', '2012-10-19')
print(sub.addr) # jonesy@example.com

另外一點就是_replace()方法,可以改變它的屬性值.比如下面這樣

Stock= namedtuple('Stock',['name', 'shares', 'price'])
s = Stock('ACME', 100, 123.45)
print(s) # Stock(name='ACME', shares=100, price=123.45)
print(s.shares) #會報錯, 因為命名元組是不可以更改的
# 如果真要更改屬于可以使用_replace()方法
s = s._replace(shares = 75)
print(s) # Stock(name='ACME', shares=75, price=123.45)

_replace在使用nametuple命名元組有缺失字段或者可選字段時它可以很方便的填充數(shù)據(jù), 比如下面這樣

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}
print(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'}
print(dict_to_stock(b)) # Stock(name='ACME', shares=100, price=123.45, date='12/17/2012', time=None)
  • 轉換并且同時計算數(shù)據(jù). 可以考慮推導與min(), max()等,些聚集函數(shù)比如 min() 和 max() 的時候你可能更加傾向于使用生成器版本, 它們接受的一個key關鍵字參數(shù)或許對你很有幫助
import os
files = os.listdir('.')
# for name in files:
#     if name.endswith('.py'):
#         print('aaa')
#     else:
#         print('not python file')
if any(name.endswith('.py')for name in files): 
    print('There be python!') 
else:
    print('Sorry,no python.')
    
    
portfolio =[ {'name':'GOOG', 'shares':50}, {'name':'YHOO', 'shares':75}, {'name':'AOL','shares': 20}, {'name':'SCOX', 'shares':65} ]
min_shares= min(s['shares'] for s in portfolio)
print(min_shares)
print(min(portfolio, key=lambda s:s['shares'])) # {'name': 'AOL', 'shares': 20}

合并多個字典或者映射

  • 現(xiàn)有多個字典或者映射,想從邏輯上合并為一個單一映射后執(zhí)行某些操作,比如查找健值或者健是否存在,可以使用collections 中的ChainMap
    一個 ChainMap 接受多個字典并將它們在邏輯上變?yōu)橐粋€字典。 然后,這些字典并不是真 的合并在一起了, ChainMap 類只是在內部創(chuàng)建了一個容納這些字典的列表 并重新定義了 一些常見的字典操作來遍歷這個列表。大部分字典操作都是可以正常使用的
    另外: 對于字典的更新或刪除操作總是影響的是列表中第一個字典
from collections import ChainMap
a = {'x': 1, 'z':3 }
b = {'y': 2, 'z':4 }
c = ChainMap(a,b)
print(c)
print(c['x'])
print(c['y'])
print(c['z']) # 重復鍵,那么第一次出現(xiàn)的映射值會被返回。
print(len(c))
print(list(c.keys())) #['y', 'z', 'x']
print(list(c.values())) # [2, 3, 1]

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

友情鏈接更多精彩內容