【列表解析式】Python的單行操作秀

0x00 前言

items = [line for line in open('test.txt', 'rb')]

通常,我們會在代碼中看到一些形似上面的炫目操作(什麼你們覺得很正常?好嘛是我菜了QvQ)

我一頭猛扎進了python的懷抱,正是因爲“人生苦短,我用python”,可能是相似的原因,我第一眼就看上了這種表達方式,並且樂於在自己的代碼中使用這種方式來更加精簡與清晰地生成所需要的列表。

由於在項目實現過程中產生過大量的問題與需求,在無數次嘗試之後決定總結一下如何使用列表解析式(也可稱作鏈表推導式,本文中使用前者,不爲什麼,因爲覺得這很Coooooool呀~)來解決這些類型的問題。

0x01 兩層列表解析式

個人覺得吧,循序漸進是個必要的過程,啊啊啊我好懶啊,好想直接貼代碼就算了啊……
不行,這麼幹的話以後我自己也看不懂的……
那麼稍微解釋一下:

# 寫法如下
ret = [x for j in L for x in j]

# 這個是不是看起來很頭疼?
# 但是實際上我們本來就是爲了簡短這種寫法會很常見的
# 爲了方便理解大概是下面這種感覺:
ret = [items for sub_list in L for items in sub_list]

# 其含義是:
ret = []
for sub_list in L:
    for items in sub_list:
        ret.append(items)

# 但是明顯解析式會快得多,
# 想要深究原因的話可以試着從append()的實現,
# 與解析式如何生成list的區別這個方向來進行思考。

對於形似如下這種討厭的參差不齊的二維列表,想對其進行Flatten操作的時候可以使用簡單的兩層列表解析式搞定(但是對於形似 L = [1, [2,3]] 這種的是不行的哦)

L = [
    [1,3],
    [3],
    [1,2,3],
    [1,2],
]

0x02 兩層列表解析式·進階

特別的,實際上本身不是個兩層,但是也有類似的需求該如何是好呢?
其實我們可以在解析式中對相關元素進行操作的,先直接上代碼:

# 舉個栗子:
delim = u';'
L = [u'今天是個好天氣!', u'對呀對呀;我也這麼想~']
ret = [x for j in L for x in j.split(delim)]

# 其含義是:
ret = []
delim = u';'
for sentence in L:
    for items in sentence.split(delim):
        ret.append(items)

# 應用場景實例如下,需求爲將傳入句子按分號隔開獲得新的句子列表:
def split_list(L, delim=';'):
    if L.__len__() == 0:
        return []
    if isinstance(L[0], unicode) and isinstance(delim, str):
        delim = delim.decode('utf-8')
    ret = [x for j in L for x in j.split(delim)]
    return filter(lambda x: x != '', ret)

在上述例子中,其實只是一個一維列表,但是我們使用 split(delim) 將一維列表中的每個字符串變成了一個列表,此處不僅僅是split,你們還可以自行設置各種更復雜的函數,只不過記得返回值是可迭代的就可以啦~

0x03 帶判斷的列表解析式

# 舉個栗子:
dic = {
    1:{'index':2, 'data':'klm'},
    2:{'index':7, 'data':'cde'},
}
ran = xrange(1,5)
ret = [(k, v) for (k, v) in dic.items() if v['index'] in ran]

# 其含義是:
ret = []
for (k,v) in dic.items():
    idx = v['index']
    if idx in ran:
        ret.append( (k,v) )

# 應用實例如下,需求爲每次生成滿足條件的子dict,
# 並更新元素中幾個字段表示的在新dict中的位置:
def dict_select(self, dic, lef, rig=None, pos_lef=None):
    """
    return a dict which satisfied the range of current segment.
    :param dic: a dict
    :param lef: left index of word_index
    :param rig: right index of word_index
    :param pos_lef: left index of position
    """
    def change_pos(inp):
        cp = inp.copy()  # MUST BE A COPY for no-modify
        cp[u'word_index'] = inp[u'word_index'] - lef
        cp[u'position'] = inp[u'position'] - pos_lef
        return cp

    if rig:
        ran = xrange(lef, rig)
        ret = [(k, v) for (k, v) in dic.items() if v[u'word_index'] in ran]
    else:
        ret = [(k, v) for (k, v) in dic.items() if v[u'word_index'] >= lef]
    return dict([(k, change_pos(v)) for (k, v) in ret])

0x04 常用而實用的小函數

a = [1,2,3]
b = [4,5,6,7]

def func(x): return x+1

# lambda 爲匿名函數,此處的f和func是一個效果
f = lambda x: x+1
j = lambda x: x%2==1

map(f, a) # 對於list中的每一個元素做函數f操作
# => [2, 3, 4]

zip(a, b) # 按順序組成pair的list,長度爲兩者較短的那個
# => [(1, 4), (2, 5), (3, 6)]

filter(j, a) # 判斷是否滿足條件,輸出滿足條件的list
# => [1, 3]

a.__len__() # 輸出長度,這個要比len()用起來要好些
# => 3

0x05 綜合應用

也就是說,在兩層列表解析式中,x, j, L 你們都可以想想怎麼瞎改,
這裏我只試着對x和j改來改去,因爲我覺得吧——
你要改傳入的L的話……你爲什麼不在外頭改好了再扔進來呢?

# 栗子快舉完了
L = [
    (u'A', u'這個句子大概是A類,我也是A類,還有我A類'),
    (u'B', u'這個句子大概是B類,我纔不告訴你我也是B類'),
    (u'A', u'A類吧大概,哼我是A類'),
]

ret = [x.strip() for j in L for x in zip(
    [j[0]] * len(j[1].split(delim)),
    filter(lambda x: x != '', j[1].split(delim))
)]

# 其含義是:
ret = []
delim = u','
for pair in L:
    label, data = pair
    items = data.split(delim)
    for it in zip([label] * len(items), items):
        ret.append(it)

# 應用場景實例如下,需求爲將傳入的(label, data)中的data按逗號隔開
# 並使用共同的label,以獲得新的句子列表:
def split_pair(self, L, delim=','):
    if L.__len__() == 0:
        return []
    if isinstance(L[0][1], unicode) and isinstance(delim, str):
        delim = delim.decode('utf-8')
    return [x for j in L for x in zip(
        [j[0]] * len(j[1].split(delim)),
        filter(lambda x: x != '', j[1].split(delim))
    )]

0xFF

忙忙碌碌到現在終於有假期了哎~
想着要好好休息休息但是覺得不行趁着餘熱寫點東西要不然我這腦子過兩天就忘乾淨了~

BTW. 啊夏活開始了我去看看twitch休息一下下~
@Kancolle Summer17 | center

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章