如何使用装饰器

时间:2015-06-30 15:00:08

标签: python pandas decorator python-3.4 function-calls

所以我有一堆处理函数,所有这些函数都使用了(因为没有更好的词)' master'功能。这个主函数基本上是一个大的AND操作,它根据一堆布尔或字符串列的值从pandas数据帧返回相关的行(顺便说一下,数据是关于啮齿动物的行为)。

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

处理器功能准备通过相应的绘图功能绘制数据,即,性能返回(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函数添加默认值而不是完全明确的,即在所有函数中暴露init,气味,动作......,也不是完全通用的,即现在​​使用** 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的内部范围locals()中声明的变量组。

到目前为止我尝试了什么:

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 dict(在示例中为**默认值)。这意味着我必须在函数的内部范围内使用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

1 个答案:

答案 0 :(得分:0)

如果你想要的只是具有不同默认参数的同一函数的不同版本的Python函数,你可以使用functools.partial轻松创建它们,而不需要装饰器。

所以,如果你有def trial_selector(par1=..., par2=..., ...): 并且想要为各种参数设置具有不同默认设置的可调用对象,您可以这样声明它们:

来自functools的

导入部分

search1 = partial(trial_decorator,par1 =“ABCD”,par2 =“EFGH”) search2 = partial(trial_decorator,par1 = None,par3 =“XZY”,... 0

只需调用searchN函数,只需要关心新函数 您想要再次覆盖的参数或参数。

现在,如果您需要其他功能的装饰器,那么您的代码还有一些额外的评论:

您可能不知道的是,如果您使用**kwargs来调用函数,则函数签名本身不需要使用kwargs。

因此,对于你的内部函数,你可以拥有一个完整的explict参数列表(比如你的第一个listng -

,而不是仅仅def example_func(*args, **kwargs):作为签名。
def trial_selector(session,
                   init='NSWE', odor='ABCD', action='LRFB',
                   action_choice='LRFB', goal='NSWE', qNa='both',
                   cl=False, invalid=False):

仍然用一个将kwargs传递给它的装饰器包装它,正如你在“我尝试过的”代码中所做的那样。而且,在你的示例装饰器上,你已经重新创建了一个相当复杂的字典“更新”方法 - 它可以写成:

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中的参数 - 您可以根据需要创建尽可能多的参数。