2015-06-30 34 views
0

所以我有一堆處理函數,並且它們都使用(缺少更好的單詞)'主'功能。這個主函數基本上是一個很大的AND操作,根據一堆布爾值或字符串列的值(數據是關於齧齒動物的行爲)從熊貓數據框返回相關行。如何使用裝飾器添加任意kwargs和默認功能

def trial_selector(session, 
        init='NSWE', odor='ABCD', action='LRFB', 
        action_choice='LRFB', goal='NSWE', qNa='both', 
        cl=False, invalid=False): 
    trials = load_trials(session) # Wrapper load func that is somwhere else 
    # input checks transform str in lists... ugly but needed for now 
    if type(init) == str: 
     init = [init] if len(init) == 1 else [x for x in init] 
    if type(odor) == str: 
     odor = [odor] if len(odor) == 1 else [x for x in odor] 
     mapping = {'A': 1, 'B': 2, 'C': 3, 'D': 4} 
     odor = [mapping[x] for x in odor] 
    if type(action) == str: 
     action = [action] if len(action) == 1 else [x for x in action] 
    if type(action_choice) == str: 
     action_choice = [action_choice] if len(action_choice) == 1 else [x for x in action_choice] 
    if type(goal) == str: 
     goal = [goal] if len(goal) == 1 else [x for x in goal] 

    # init odor action action_choice goal selection 
    tr = trials[trials.init.isin(init) & trials.valve_number.isin(odor) 
       & trials.action.isin(action) 
       & trials.action_choice.isin(action_choice) 
       & trials.goal_choice.isin(goal)] 
    # TODO: Invalid, correction loop and trial type (not complete) 
    if not invalid: 
     tr = tr[tr.valid] 
    if not cl: 
     tr = tr[~tr.correction_loop] 
    if qNa == 'both': 
     tr = tr 
    elif qNa == 'q': 
     tr = tr[~tr.solution] 
    elif qNa == 'a': 
     tr = tr[tr.solution] 

    return tr 

處理器功能準備的數據,以通過相應的繪圖功能被繪製,即tperformance返回(X,Y,yerr)及其使用tplot_performance。

# tsargs are the arguments of the trial_selector function 
def tperformance_uni(sessionName, **tsargs): 
    trials = trial_selector(sessionName, **tsargs) 
    x = trials.correct.dropna().index 
    y = trials.correct.dropna().values 

    return (x, y) 


@check_session 
def tperformance(sessionList, smooth=False, **tsargs): 
    temp = [] 
    out = pd.DataFrame() 
    for session in sessionList: 
     x, y = tperformance_uni(session, **tsargs) 
     temp.append(pd.Series(y, index=x, name=session)) 

    out = pd.concat(temp, axis=1,) 
    x = out.mean(axis=1).index 
    y = out.mean(axis=1).values 
    yerr = out.std(axis=1)/np.sqrt(len(out.columns)) 
    yerr[yerr.isnull()] = 0 
    if not smooth or type(smooth) is bool: 
     win = win_size(x) 
    else: 
     win = win_size(x, default=smooth) 

    ysmooth = sm(y, win) 
    yerrsmooth = sm(yerr, win) 
    if len(x) != len(ysmooth): 
     ysmooth = ysmooth[1:] 
    if len(x) != len(yerrsmooth): 
     yerrsmooth = yerrsmooth[1:] 

    return (x, y, yerr) if not smooth else (x, ysmooth, yerrsmooth) 

而繪圖的功能,例如是:

def tplot_performance(sessionName, ax=False, decor=False, err=False, 
         c='b', ls='-', m='', 
         smooth=False, 
         **tsargs): 
    """ 
    Plots correct across trials 
    """ 
    if not ax: 
     ax = plt.subplot2grid((1, 1), (0, 0), rowspan=1, colspan=1) 
# --- 
    x, y, yerr = tperformance(sessionName, smooth=smooth, **tsargs) 
# --- 
    ax.plot(x, y, ls=ls, marker=m, color=c, linewidth=2) 
    if err: 
     ax.fill_between(x, y-yerr, y+yerr, color='gray', alpha=0.25) 
    if decor: 
     tplot_performance_template(sessionName, ax=ax) 

    return (x, y, yerr) 

我設法成功地實現使用的裝飾,基本上保證了會議的參數檢查@check_session是字符串列表。

def check_session(func): 
    """Ensures session is of type list, if string will make list of one value 
    """ 
    def wrapper(session, **kwargs): 
     session = [session] if type(session) is str else session 
     return func(session, **kwargs) 
    return wrapper 

到目前爲止好。 現在,我想添加trial_selector函數的默認值,而不是完全明確的,即在所有函數中暴露初始化,異常,動作......,也不是完全通用的,即現在​​使用** tsargs的方式。

基本上我想使用像@tsargs_defaults這樣的裝飾器,這樣我就可以在處理函數中使用默認值來做東西。我可以有輸入參數模塊,讓我來聲明是這樣的:

@defalut_bla 
@tsargs_defaults 
def example_func(*args, **kwargs): 
    if init == 'N': 
     do something 
    if var_in_defalut_bla == someVal: 
     do something else 

的裝飾應該添加的那些在FUNC的範圍內當地人()聲明的變量組。

我試過到目前爲止:

def tsargs_defaults(func): 
    """Adds trial_selector arguments and their defaults to function 
    tsargs = init='NSWE', odor='ABCD', action='LRFB', action_choice='LRFB', 
    goal='NSWE', qNa='both', cl=False, invalid=False, 
    """ 
    def wrapper(*args, **kwargs): 
     defaults = {'init': 'NSWE', 
        'odor': 'ABCD', 
        'action': 'LRFB', 
        'action_choice': 'LRFB', 
        'goal': 'NSWE', 
        'qNa': 'both', 
        'cl': False, 
        'invalid': False} 
     for k in kwargs: 
      if k in defaults: 
       defaults[k] = kwargs[k] 
      elif k not in defaults: 
       defaults.update({k: kwargs[k]}) 
     return func(*args, **defaults) 
    return wrapper 

然而,這會增加我想要的不是局部範圍內,但爲kwargs字典(**在本例中的默認值)。這意味着我必須在函數的內部範圍內使用kwargs['init'] == 'N'而不是init == 'N'

據我所知,這是一個非問題的巨大解釋,因爲這類代碼的工作類型,但我有一堆處理和繪圖功能,使用暴露的默認參數做不同的事情,並希望避免重構所有的。 也許有沒有辦法,或者我的問題是病態提出記住它的第一次嘗試使用Python裝飾器。無論如何我想多瞭解一點。 任何幫助表示讚賞! 感謝

BTW:我使用python 3.4

TL; DR

# some_kwargs {'one': 1, 'two': 2} 
# some_other_kwargs {'three': 3, 'four': 4} 

@some_other_kwargs 
@some_kwargs 
def example_func(*args, **kwargs): 
    print(one, two, three, four) # does not work 
    print(kwargs['one'], kwargs['two'], kwargs['three'], kwargs['four']) # works 
+2

這不是*完全清楚,但你是否在尋找'kwargs.get(鍵,默認[鍵])'?你能把這個簡化爲一個簡單的例子嗎? – jonrsharpe

+0

倒數第二個代碼塊是一個簡單的例子,其中kwarg _init_從* @ tsargs_defaults *裝飾器添加到內部作用域,而_var_in_default_bla_由* @ default_bla *裝飾器添加。現在我可以檢查init =='N'的唯一方法是使用'if kwargs ['init'] =='N'' – nico

+1

否;如果你使用任意'kwargs',你必須從字典中獲得它們(除非你像更新'locals'那樣做一些hacky)。還要注意,你應該使用'isinstance'而不是'type'。 – jonrsharpe

回答

0

如果你想要的是Python函數的不同版本具有不同的默認參數相同的功能,您可以使用functools.partial輕鬆創建它們,而無需裝飾器。

所以,如果你有def trial_selector(par1=..., par2=..., ...): 並希望與不同的默認設置的各種參數的可調用對象,你可以宣佈他們是這樣的:

從functools進口部分

搜索1 =部分(trial_decorator,PAR1 = 「ABCD」,PAR2 = 「EFGH」) 搜索2 =部分(trial_decorator,PAR1 =無,PAR3 = 「XZY」,... 0

並調用searchN功能僅不必關心新 參數或參數你想重新覆蓋。


現在,如果你需要其他的功能裝飾比 - 大約有你的代碼一些額外的言論:

你可能不知道的是,如果你使用的**kwargs調用一個函數,函數簽名本身不需要使用kwargs。

因此,對於你的內部函數,而不是havign只是def example_func(*args, **kwargs):作爲簽名,你可以有你的第一個listng的顯式參數的完整列表(如 -

def trial_selector(session, 
        init='NSWE', odor='ABCD', action='LRFB', 
        action_choice='LRFB', goal='NSWE', qNa='both', 
        cl=False, invalid=False): 

,仍然具有裝飾把它包並且在你的「我嘗試過的」代碼中做了這些,而且,在你的示例裝飾器中,你已經以相當複雜的方式重新創建了一個字典「更新」方法 - 它可以被寫成如下形式:

def tsargs_defaults(func): 
    """Adds trial_selector arguments and their defaults to function 
    tsargs = init='NSWE', odor='ABCD', action='LRFB', action_choice='LRFB', 
    goal='NSWE', qNa='both', cl=False, invalid=False, 
    """ 
    def wrapper(*args, **kwargs): 
     defaults = {'init': 'NSWE', 
        'odor': 'ABCD', 
        ... 
        'invalid': False} 
     defaults.update(kwargs) 
     return func(*args, **defaults) 
    return wrapper 

如果這就是你所有噸,這就是你需要的一切。此外,修飾語法是爲了幫助 - 在這種情況下,它看起來像你可以利用幾個這樣的「默認ARGS」裝飾器不使用修飾語法 - 你可以寫它就像:

def full_search_function(all, default, parameters, ...): 
    ... 

def decorator_for_type1_search(...): 
    ... 
type1_search = decorator_for_type1_search(full_Search_function) 

在這一點上,您有type1_search作爲一個函數與decorator_for_type1_search中添加的參數 - 你可以創建儘可能多的那些你想要的。

+0

謝謝我一定會看看部分,不知道它存在:)。對於裝飾者,我只有一件事要說,那就是......我一定是在睡覺......當然,更新與kwargs的默認值將會起作用....你的答案的最後一部分我不明白,可以你詳細說明一下? – nico

+0

在最後一部分,名爲「type1_search」的變量將成爲一個新函數 - 就像它是一個具有與'@ decorator_for_type1_search'裝飾的'full_Search_function'一樣的函數。但是通過這種方式,原來的'full_search_function'仍然是未修飾的,並且可以用'type2_search = decorator_for_type2(full_search_function)'來創建更多的函數' – jsbueno