2017-06-04 38 views
0

有沒有更好的方法來迭代將函數列表應用於字典?這是我想要做的一個例子。但是這使用遞歸。在Python中有沒有更好的方法來將一個函數列表應用到字典中

def func1(h: dict): 
    h['foo']=10 
    return(h) 

def func2(h: dict): 
    h['bar']=100 
    return(h) 

def func3(h: dict): 
    h['baz']=h['foo']+h['bar'] 
    return(h) 

func3(func2(func1({'firstElement':'good'}))) 

可生產預期輸出:

{'bar': 100, 'baz': 110, 'firstElement': 'good', 'foo': 10} 

欲提供用作陣列和產生相同的輸出。下面是我曾嘗試和作品:

def recApply(flist, h=None): 
    """ 
    Helper Apply the list of functions iteratively over the dictionary passed 
    :obj: function list each will be applied to the dictionary sequentially. 
    """ 
    #if no dictionary passed, then set the dictionary. 
    if(h == None): 
     h = {} 

    #iteratively call functions with dictionary as a passed parameter and returning a derived dictionary 
    for f in flist: 
     h = f(h) 

    return(h) 

flist = [func1,func2,func3] 
recApply(flist,{'firstElement':'good'}) 

這將產生所需的輸出:

{'bar': 100, 'baz': 110, 'firstElement': 'good', 'foo': 10} 

有沒有辦法做到這一點,是更具可讀性,刪除recApply功能,並希望最大限度地減少字典副本?

+0

這不會複製任何字典;對同一個突變字典的引用被傳入並從每個函數返回。 – chepner

回答

2

你不需要來回報您的字典和重新分配的引用 - 可變類型作爲參考就這麼過去了:

def func1(h): 
    h['foo'] = 10 

def func2(h): 
    h['bar'] = 100 

def func3(h): 
    h['baz'] = h['foo'] + h['bar'] 

start_dict = {'firstElement': 'good'} 

for f in (func1, func2, func3): 
    f(start_dict) 

print(start_dict) 
# {'firstElement': 'good', 'baz': 110, 'bar': 100, 'foo': 10} 

將向完美的罰款。

+0

Python中的所有參數都以相同的方式傳遞https://nedbatchelder.com/text/names.html – chthonicdaemon

+0

@chthonicdaemon - true,但我們在這裏討論語義。你總是傳遞一個值的引用,但只有可變類型可以改變,在實踐中產生「傳遞引用」與「傳值」區別(大多數語言都存在)。 – zwer

2

reduce(或Python 3中的functools.reduce)可用於將函數列表組合到一個函數中。這需要你定義的組合物的功能:

def compose(f, g): 
    def _(x): 
     return f(g(x)) 
    return _ 

和身份功能:

def identity(x): 
    return x 

使用這些,可以創建一個功能g,爲了應用於每個功能到初始輸入。

g = reduce(compose, [func3, func2, func1], identity) 
assert (g({'firstElement': 'good'}) == 
     {'firstElement': 'good', 'foo': 10, 'bar': 100, 'baz': 110}) 

注意這個工作,因爲func1func2func3是純粹的功能,你可以使用函數monoid的非常相似。鬆散地說,這隻意味着功能組合是聯想的(compose(f, compose(g, h))compose(compose(f, g), h)相同),並且在組合下(compose(identity, f)compose(f, identity)都與f本身相同)身份函數是中性的。

你的三個函數並不是純粹的函數,他們更像是具有副作用的身份功能。但是,你可以因爲你使用的是他們把他們當作純粹的功能,就像它們被定義爲,例如,

def func1(h): 
    h1 = {} 
    h1.update(h) 
    h1['foo'] = 10 
    return h1 

讀者練習:確定我到reduce調用實際上定義了g(x) = func3(func2(func1(x)))g(x) = func1(func2(func3(x))

1

你也(在Python 3或functools.reduce)與起始字典使用reduce作爲initializer參數:

>>> from functools import reduce # Python 3 
>>> reduce(lambda (x, f): f(x), (func1, func2, func3), {'firstElement':'good'}) 
{'bar': 100, 'baz': 110, 'firstElement': 'good', 'foo': 10} 

這將適用於其他的初始化或以前的結果後的功能之一功能。

你也可以用functools.partial結合此創建,然後可以適用於不同的輸入字典鏈式功能:

>>> from functools import partial 
>>> chain = partial(reduce, lambda (x, f): f(x), (func1, func2, func3)) 
>>> chain({'firstElement':'good'}) 
{'bar': 100, 'baz': 110, 'firstElement': 'good', 'foo': 10} 

而且你可以進一步推廣,使之成爲partial功能的partial ..

>>> chainer = partial(partial, reduce, lambda (x, f): f(x)) 
>>> chain = chainer((func1, func2, func3)) 
>>> chain({'firstElement':'good'}) 
{'bar': 100, 'baz': 110, 'firstElement': 'good', 'foo': 10} 
相關問題