由於公眾號改版不再按照作者的發布時間進行推送,為防止各位朋友錯過月來客棧推送的最新文章,大家可以手動將公眾號設置為「星標⭐」以第一時間獲得推送內容,感謝各位~
各位朋友大家好,歡迎來到月來客棧,我是掌柜空字符。
不知道各位客官在行走江湖的過程中有沒有遇到類似這樣的情景:
場景一:
假如現在有一個元素全為自然數的列表,需要你將其按奇數和偶數的方式把這些自然數分成兩類,並存放到一個字典中。對於這個場景,掌柜相信每個客官看完之後就能寫出類似如下的代碼:
1if__name__=='__main__':2num=[0,1,2,3,4,5,6,7,8,9]3result={'odd':[],'even':[]}4forninnum:5ifn%2!=0:6result['odd'].append(n)7else:8result['even'].append(n)9print(result)1011#{'odd':[1,3,5,7,9],'even':[0,2,4,6,8]}在上述代碼中,我們首先定義了一個包含有兩個key的字典;然後依次遍歷列表中的每一個數並按條件放到字典中的不同地方。
再例如現在有一個元素全為自然數的列表,需要你將其中相同的元素放到同一個列表中。對於這種類似場景,最直接的寫法就是:
1if__name__=='__main__':2num=[0,0,1,5,2,3,1,6,5,7,9]3result={}4forninnum:5ifninresult:6result[n].append(n)7else:8result[n]=[n]9print(result)1011#{0:[0,0],1:[1,1],5:[5,5],2:[2],3:[3],6:[6],7:[7],9:[9]}可以看到,雖然在上述兩個場景中我們都能夠以最直接的方式給實現出來,但總覺得繁雜了一點。那有沒有更簡單的方式呢?
場景二:
在數據預處理的過程中(又尤其是NLP)通常都會返回包含一串元素的數據類型。類似某數據預處理後返回的類型為一個二維列表,列表中的每一個元素對應的都是一個樣本(列表),每個樣本裡面又包含了該樣本對應的信息。例如SQuAD數據預處理後每個樣本就會返回類似如下的字段:
[[example_id,feature_id,input_ids,seg,start_position,end_position,answer_text,question_id,input_tokens],[],[],...]對於這樣的形式,通常在後續都會以元素索引的方式來取每個樣本中的對應內容。但是這樣做面臨的一個問題就是,當整個數據樣本作為參數傳到不同(多個)函數之後,我們往往就會忘了每個索引所指代的元素到底是什麼含義,又需要跳轉到最開始的地方數一數來確定。而這為我們寫代碼帶來了極大的不便。那有沒有什麼好的方法能夠解決這個問題呢?
2 解決方案2.1 默認字典defaultdict對於場景一當中的問題,我們可以通過collections包中的defaultdict模塊來實現。從模塊的名字可以看出,它返回的仍舊是一個字典,只是我們在定義這個字典的時候可以指定它的默認類型。例如可以通過defaultdict(list)來得到一個默認元素為list的字典。
有了defaultdict後,場景一中的第二個示例我們便可以通過如下代碼來實現:
1if__name__=='__main__':2num=[0,0,1,5,2,3,1,6,5,7,9]3result=collections.defaultdict(list)4forninnum:5result[n].append(n)6print(result)7print(result[0])8#defaultdict(<class'list'>,{0:[0,0],1:[1,1],5:[5,5],2:[2],3:[3],6:[6],7:[7],9:[9]})9#[0,0]從上面的輸出結果可以看出,defaultdict返回字典和普通的dict字典在通過Key取Value的用法都一樣。同時也可以通過同dict一樣的方式來遍歷defaultdict字典:
1fork,yinresult.items():2print(k,y)3#40[0,0]51[1,1]65[5,5]72[2]83[3]96[6]107[7]119[9]當然,對於defaultdict來說,其默認指定的類型還可以是str、dict等任何你所需要的類型。
2.2 命名體元組namedtuple對於場景二中的問題,我們可以藉助collections包中的namedtuple模塊來完成。從名字來看namedtuple返回的本質上是一個元組,只是元組裡面的元素可以通過其對應的名稱以類成員變量的方式來進行訪問與使用。
因此,對於類似場景二中的問題,我們便可以通過如下所示代碼來進行解決:
1importcollections2importnumpyasnp34if__name__=='__main__':5Point=collections.namedtuple("point",['x','y'])6X=np.random.randint(low=0,high=10,size=5)7Y=np.random.randint(low=0,high=10,size=5)8points=[]9forx,yinzip(X,Y):10points.append(Point(x=x,y=y))11print(points)1213forpinpoints:14print(f"{p.x}+{p.y}={p.x+p.y}")15#16[point(x=2,y=9),point(x=1,y=9),point(x=2,y=3),point(x=9,y=4),point(x=9,y=1)]172+9=11181+9=10192+3=5209+4=13219+1=10在上述代碼中,第5行用來定義一個包含有兩個元素的元組,且該元組的名稱為point,兩個元素對應的名稱分別為x和y;第9-10行則是分別將這5個點依次保存到列表points中,可以看到此時points中的每一個元素都是一個命名體元組了;第13-14行則是遍歷列表中的每一個元素,然後通過以元組中元素名稱的方式來訪問對應的值。
所以,在有了命名體元組這一利器之後,對於場景二中的問題便可以通過定義一個相應的namedtuple來進行解決。
本次內容就到此結束,感謝您的閱讀!如果你覺得上述內容對你有所幫助,歡迎點讚轉發分享三連!若有任何疑問與建議,請添加掌柜微信nulls8或加群進行交流。青山不改,綠水長流,我們月來客棧見!