一道快速考察 Python 基礎的面試題

這是前一陣子羣友發在羣裏的一道面試題,利用 Python 字典的特性,能夠巧妙地使用精簡代碼達成完美解。python

題目

將 data 轉換成 new_data 這種形式,寫出轉換過程。面試

data = {
    'a_b_h':1,
    'a_b_i':2,
    'a_c_j':3,
    'a_d':4,
    'a_c_k':5,
    'a_e':6
}

new_data = {
    'a':{
        'b':{
            'h':1,
            'i':2
        },
        'c':{
            'j':3,
            'k':5
        },
        'd':4,
        'e':6
    }
}

能夠看出,轉換的過程是將 key 的下劃線進行拆分,而後下劃線後邊的字符嵌套在前面字符的值中。編程

感興趣就打開 IDE,本身先試着解一下。微信

解題思路

你應該很快想到,主要思路是將下劃線 split 後,而後依次使用字符生成內層字典,當達到最後一個字符時將數字做爲值。優化

那麼關鍵點在於,如何不斷地得到內層字典去修改呢?實際本題就是考察你是否理解 Python 字典是引用傳遞這個特性。code

什麼是引用傳遞?咱們知道 Python 中字典和列表對象都是可變對象,它們的變量傳遞給另外一個變量後,改變對象元素會使得兩個變量都會同時改變,好比:對象

new_data = {}
tmp = {}
new_data['a'] = tmp
print(new_data)  # {'a': {}}
tmp['b'] = 1
print(new_data)  # {'a': {'b': 1}}

如上,利用這個特性,將內層字典賦值給一箇中間變量,而後改變這個中間變量,便可同步修改最終的 new_data 變量。索引

根據這個思路,初步代碼以下:get

data = {
    'a_b_h':1,
    'a_b_i':2,
    'a_c_j':3,
    'a_d':4,
    'a_c_k':5,
    'a_e':6
}

new_data = {}

for key, value in data.items():
    keys = key.split('_')
    tmp = new_data
    last = len(keys) - 1  # 最後一個 key 的索引值 
    for i, k in enumerate(keys):
        if i == last:
            tmp[k] = value
            continue
        if k not in tmp:
            sub_tmp = {}
            tmp[k] = sub_tmp
            tmp = sub_tmp
        else:
            tmp = tmp[k]

這也是羣友給出的初版答案,這樣寫並無多大問題,可是代碼比較繁瑣,確定還有優化空間。同步

咱們能夠只使用一箇中間變量便可,進一步優化:

for field, value in data.items(): 
    keys = field.split('_') 
    tmp = new_data 
    last = len(keys) - 1
    for i, k in enumerate(keys): 
        if k not in tmp:
            tmp[k] = {} if i < last else value
        tmp = tmp[k]  # 將內層 dict 傳給 tmp

上面這個代碼看似很簡潔了,可是仍然還有兩個 if 判斷,若是不是使用了三元表達式的話,還會更多行。

因此能夠進一步優化:

for field, value in data.items(): 
    keys = field.split('_') 
    tmp = new_data 
    for k in keys[:-1]: 
        tmp = tmp.setdefault(k, {})
    tmp[keys[-1]] = value

咱們省略掉了 last 來判斷最後一個字符的索引,直接經過 keys[:-1] 避開最後一個字符,末尾再單獨生成數字鍵值對。

這裏還使用字典的一個內置方法 —— setdefault

dict.setdefault(key, default=None) 方法和 get 方法相似,只是若是鍵不存在於字典中,不只會返回 default 參數的值,還同時會用該值自動生成一個鍵值對。

if k not in tmp:
    tmp[k] = {}
v = tmp[k]

# 等價於

v = tmp.setdefault(k, {})

最終咱們使用了 6 行代碼就解出該題,這也是接近最簡代碼。

若是使用字典引用的特性是合格的話,那麼當你用出 setdefault 這個方法後,面試官已經給你打了優秀,因此必定要熟悉這些數據對象的全部內置方法


本文屬於原創,首發於微信公衆號「面向人生編程」,如需轉載請後臺留言。

相關文章
相關標籤/搜索