2016-02-12 24 views
0

的基本情況:拆分一個快譯通到許多類型的字典由startswith條件

我有一本字典:

kwargs = {'scale_a': 10, 'scale_b': 20, 
      'shift_a': 15, 'shift_x_and_y': (1,5)} 

,我想將它們添加到另一個字典,而是那本詞典具有基於startstring分字典的kwargs。所以結果應該是:

kwds = {'scale': {'a': 10, 'b': 20}, 
     'shift': {'a': 15, 'x_and_y': (1, 5)}} 

我目前使用稍長的循環:

kwds = {'scale': {}, 'shift': {}} 
for i in kwargs: 
    splitted = i.split('_', 1) 
    try: 
     # This will fail if the prefix is not initialized in the dict with a KeyError. 
     kwds[splitted[0]][splitted[1]] = kwargs[i] 
    except KeyError: 
     raise KeyError('Unknown prefix {0} for parameter {1}' 
         ''.format(splitted[0], i)) 

,它是工作,但我在想,如果可以改寫,作爲一個字典,理解。或者以其他方式縮短它。我想:

kwds = {key.split('_', 1)[1]: kwargs[key] for key in kwargs} 

但只是給了我

{'a': 15, 'b': 20, 'x_and_y': (1, 5)} 

這不是我想要的。有沒有辦法在字典裏面找到字典?


一些解釋我爲什麼要這麼做:

我使用Python3,我寫結合幾種不同的功能,因爲一些功能是從圖書館numpyastropy和功能其他人我使用**kwargs作爲參數,並希望分配功能內的值。

有一些缺點:包改變它們的API並且根據函數,甚至可能有兩次相同的參數名。所以我不想硬編碼參數。

我會用一個簡單的例子來說明吧:

我有幾個圖片,我想將它們結合起來。步驟如下:

  • 縮放圖像:功能需要一個可調用的參數和調用需要一些論據
  • 移圖片:調用帶參數
  • 堆棧的圖像:np.dstack所以沒有必要爭論
  • 拒絕分:再次函數接受一個arbitary調用這個函數希望一些輸入
  • 結合圖片:再次調用,但這次是因爲我想限制他們numpy的沿軸= 2
  • 功能,它需要沒有參數
  • 創建偏差圖像:查看點中的方差。這是另一個可能需要一些附加參數的可調用對象,例如np.std可以使用ddof參數。

所以我想最簡單的方式做這將是希望用戶使用與scale_開始,如果他們應該被傳遞給scale_funcshift_如果他們應該傳遞給shift_func關鍵詞等這是爲了避免這兩個函數的同名參數會成爲問題,並允許以最合適的方式處理每個函數(也就是說,如果我只有numpy 1.7,我只能指定函數的參數正在接受)。

我喜歡這樣快,但我無法弄清楚我該怎麼做。

我的解決方案(而縮短,只是爲了看看我的意思):

def combine(images, scale_func, shift_func, reject_func, comb_func, dev_func, **kwargs): 
    # Prefix should be scale_ for parameters passed to scale_func, shift_ for shift_func, ... 

    kwds = {j.split('_', 0)[1]: kwargs[j] for j in kwargs} 

    for i in range(len(images)): 
     images[i] = shift_func(images[i], **kwds['shift']) 
     images[i] = scale_func(images[i], **kwds['scale']) 
... 

kwds[i] = {j.split('_', 1): kwargs[j] for j in kwargs}就是不工作。直到現在我用

kwds = {'scale': {}, 'shift': {}, 'reject': {}, 'dev': {}} 
for i in kwargs: 
    splitted = i.split('_', 1) 
    try: 
     # This will fail if the prefix is not initialized in the dict with a KeyError. 
     kwds[splitted[0]][splitted[1]] = kwargs[i] 
    except KeyError: 
     raise KeyError('Unknown prefix {0} for parameter {1}' 
         ''.format(splitted[0], i)) 

但這在我看來不是很pythonic,不是很快。我想知道是否可以通過詞典理解或其他一些簡短而快速的方式來解決這個問題。

任何幫助表示讚賞。

+0

什麼是你的錯誤? 'AttributeError:類型對象'str'沒有屬性'lsplit''? – gil

+0

我在頂部添加了一個小例子,我想要的是什麼,以及我用dict理解的東西。 「lsplit」只是一個錯字,糾正了它。 – MSeifert

+0

我明白了。如果您不需要無效密鑰來引發異常(我們簡單忽略它們),我的雙線應該可以工作。如果你想「提高」,那麼無論如何都不可能在一個字典中這樣做,因爲你不能在理解中使用語句。 – gil

回答

1

編輯:更正了原來的答案並添加了一個較短的版本。

不知道你得到了什麼錯誤。可能是由於lsplit(只有splitrsplit,lsplit)。但在任何情況下,我會建議融通一些邏輯出一個輔助功能:

# A helper function which validates and splits a keyword 
def split_key(key): 
    if not key.startswith(('scale_', 'shift_', 'reject_', 'dev_')): 
     raise KeyError('unknown key', key) 
    return key.split('_', maxsplit=1) 

# Then call it from combine 
def combine(..., **kwargs) 
    kwds = {} 
    for key, value in kwargs.items(): 
     major_key, minor_key = split_key(key) 
     kwds.setdefault(major_key, {})[minor_key] = value 
    ... 

這可能是有點清潔(不要太多,我怕),更容易理解,與combine專注於主業務和split_key照顧細節(並提高無效密鑰KeyError)。

如果不需要驗證,那麼下面的班輪將做

acceptable = 'scale_', 'shift_', 'reject_', 'dev_' 
kwds = {key[:-1]: {k.split('_', 1)[-1]: v for k, v in kwargs.items() if k.startswith(key)} for key in acceptable} 

雖然我不能建議使用它,如果有人將讀取你的代碼:

+0

謝謝。我實際上已經使用了幫助函數,但爲了使問題最小化,我將所有內容放在一個函數中。由於它通過每個「可接受」元素的''kwargs''並且花費了將近7倍的時間(在我的函數中),我希望我能夠獲得速度提升以及縮短碼。 – MSeifert

0

我打破了功能下降爲你和你試圖做什麼做出的假設:

檢查與特殊值['scale_', 'shift_', 'reject_', 'dev_']

[kwargs[j] for j in kwargs if j.startswith(i)] #returns dict value 

的一個關鍵是否開始單獨承擔上述特殊值並使其的關鍵字典

j.split('_', maxsplit=1)[0] # Corrected 

構建一個字典

kwds[i] = {j.split('_', maxsplit=1)[0]: kwargs[j] for j in kwargs if j.startswith(i)} 
+0

雖然我必須承認,我不確定爲什麼你甚至需要執行_j.split('_',maxsplit = 1)[0] _,因爲_i [: - 1] _會有我想的相同的最終結果。 –

+0

我可能一直在混淆這個例子。我希望字典的鍵是分離鍵的'[0]''部分,但是新鍵的值應該是帶有分割鍵''[1]''-part的另一個字典。 – MSeifert