简体   繁体   English

带有在运行时传递的参数的装饰器

[英]Decorator with parameters passed at runtime

There are many questions related to using decorators with or without parameters in python but my case is a bit different. 关于在python中使用带有或不带有参数的装饰器,存在很多问题,但我的情况有些不同。

General Question 一般问题

What I need is a decorator which accepts some parameters besides the function itself (and this functions args and kwargs). 我需要的是一个装饰器,该装饰器除了函数本身(以及args和kwargs函数)之外还接受一些参数。 However these parameters are computed after the decorated function has been declared. 但是,这些参数是在声明装饰函数之后计算的。

My specific case 我的具体情况

I have written a number of functions processing a pd.DataFrame for machine learning, they get an DF as an input and return a DF as an output. 我编写了许多处理pd.DataFrame的函数以进行机器学习,它们获取DF作为输入,并返回DF作为输出。 In many cases I want to call them not on one DF, but on the concatenation of multiple training and test sets. 在许多情况下,我不想将它们称为一个DF,而是将多个训练和测试集串联在一起。 The motivation is that I hate code duplication and the alternative is calling every step twice (once for each set). 这样做的动机是我讨厌重复代码,而替代方法是每个步骤调用两次(每组一次)。 Also the author of a preprocessing function should not care about the fact that users often want to use it on 2 sets at a time. 预处理功能的作者也不必担心用户经常一次要使用2套这样的事实。

The code currently looks like that: 当前代码如下所示:

def train_and_test(preprocess):
  def preprocess_wrapper(**kwargs):
    def concat(train, test):
        train['is_train'] = True
        test['is_train'] = False
        return pd.concat([train, test])

    def split(full):
        train = full[full['is_train']]
        test = full[~full['is_train']]
        train = train.drop('is_train', axis=1)
        test = test.drop('is_train', axis=1)
        assert len(full) == (len(train) + len(test))
        return train, test

    train = kwargs['train']
    test = kwargs['test']
    full = concat(train, test)
    processed = preprocess(full)
    return split(processed)

return preprocess_wrapper

@train_and_test # I would like to pass train and test as arguments here but these are loaded by client code
def my_preprocessor(df):
  preprocessed = do_something_smart(df)
  return preprocessed

What I want 我想要的是

I would like clients of this code to be able to call functions defined on one DF by passing the train and test sets to the decorator. 我希望此代码的客户能够通过将火车和测试集传递给装饰器来调用在一个DF上定义的函数。 Something like this: 像这样:

train, test = pd.read_csv('data/train.csv'), pd.read_csv('data/test.csv')

train, test = preprocess(train, test) # In reality preprocess signature expects one DF but user can now treat it like it accepted 2

We need to go deeper! 我们需要更深入!

A decorator is just a function that operates on a function. 装饰器只是在一个函数上运行的一个函数。

Well, why not write a function that returns a function operating on a function? 那么,为什么不编写一个返回对函数进行操作的函数的函数呢?

def paramizer(*args):
    def normal_decorator(f):
        ...stuff that uses the decorator args to customize behavior...
        def wrapper():
            ...stuff decorating f...
            return f()
        return wrapper
    return normal_decorator

@paramizer(arg1, arg2) #=> returns a customized decorator and applies it to function
def function()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM