简体   繁体   English

将实例变量传递给装饰器

[英]Passing instance variables to a decorator

I found this useful decorator that allows you to pass in some optional arguments我发现这个有用的装饰器允许你传入一些可选的 arguments

def mlflow_experiment(
    _func=None,
    *,
    experiment_name=None
  ):
      def experiment_decorator(func):
          @functools.wraps(func)
          def experiment_wrapper(self, *args, **kwargs):
              nonlocal experiment_name

              experiment_id = (
                  mlflow.set_experiment(experiment_name)
                  if experiment_name is not None
                  else None
              )
                ...

              value = func(self, *args, **kwargs)

              return value

          return experiment_wrapper

      if _func is None:
          return experiment_decorator
      else:
          return experiment_decorator(_func)

So in a use case like this where I just pass in a string to experiment_name , the code works flawlessly.因此,在这样的用例中,我只需将字符串传递给experiment_name ,代码就可以完美运行。

@mlflow_experiment(autolog=True, experiment_name = 'blarg')    
def train_mlflow(self, maxevals=50, model_id=0):
  ...

I've always had a hard time figuring out scope in decorators but I wasn't surprised that using passing an instance variable defined in __init__ does NOT work.我一直很难在装饰器中弄清楚 scope ,但我并不惊讶使用传递__init__中定义的实例变量不起作用。

class LGBOptimizerMlfow:
    def __init__(self, arg):
        self.arg = arg

    @mlflow_experiment(autolog=True, experiment_name = self.arg)    
    def train_mlflow(self, maxevals=50, model_id=0):
        ...

>>> `NameError: name 'self' is not defined`

Just to see if scoping was an issue, I declared the variable outside the class and it worked.只是为了看看范围界定是否是一个问题,我在 class 之外声明了变量并且它起作用了。

And just for the heck of it I decided to declare a global variable inside the class which also works but its less than ideal, especially if I want to pass it into the class or a method as a optional argument.只是为了它,我决定在 class 中声明一个全局变量,它也可以工作,但它不太理想,特别是如果我想将它传递给 class 或作为可选参数的方法。

class LGBOptimizerMlfow:
    global arg
    arg = 'hi'

    @mlflow_experiment(autolog=True, experiment_name = arg)    
    def train_mlflow(self, maxevals=50, model_id=0):
    ...
  

Any help to revise the code so that the decorator accepts an instance variable would be lovely.修改代码以使装饰器接受实例变量的任何帮助都会很可爱。

Thank you!谢谢!

Decorators are called while the class is being defined , and self is simply a parameter used for each instance method, not something the class itself provides.定义class 时调用装饰器,而self只是用于每个实例方法的参数,而不是 class 本身提供的参数。 So self is not defined at the time you need it to be for use as an argument to your decorator.因此,当您需要将self用作装饰器的参数时,并未对其进行定义。

You need to modify experiment_wrapper to take a name directly from its self argument, rather than from an argument to mflow_experiment .您需要修改mflow_experiment以直接从其self参数中获取名称,而不是从experiment_wrapper的参数中获取名称。 Something like就像是

def mlflow_experiment(
    _func=None,
    *,
    
  
  
    experiment_name=None, 
  
    tracking_uri=None,
    autolog=False,
    run_name=None,
    tags=None,
  ):
      def experiment_decorator(func):
          @functools.wraps(func)
          def experiment_wrapper(self, *args, **kwargs):
              nonlocal tracking_uri

              experiment_name = getattr(self, 'experiment_name', None)
              experiment_id = (
                  mlflow.set_experiment(experiment_name)
                  if experiment_name is not None
                  else None
              )
                ...

              with mlflow.start_run(experiment_id=experiment_id
                                  , run_name=run_name
                                  , tags=tags):
              value = func(self, *args, **kwargs)

              return value

          return experiment_wrapper

      if _func is None:
          return experiment_decorator
      else:
          return experiment_decorator(_func)

Then you need to make sure that each instance has an experiment name (or None) associated with it.然后,您需要确保每个实例都有一个与之关联的实验名称(或无)。

class LGBOptimizerMlfow:
    def __init__(self, arg, experiment_name=None):
        self.arg = arg
        self.experiment_name = experiment_name

    @mlflow_experiment(autolog=True
  
  
    , experiment_name = self.arg 
  )    
    def train_mlflow(self, maxevals=50, model_id=0):
        ...

Another alternative is to make experiment_name an argument to train_mflow , making it easier to create different names with the same method.另一种选择是让experiment_name成为train_mflow的参数,从而更容易使用相同的方法创建不同的名称。 (This may be closer to what you were intending.) (这可能更接近您的意图。)

class LGBOptimizerMlfow:
    def __init__(self, arg):
        self.arg = arg

    @mlflow_experiment(autolog=True)    
    def train_mlflow(self, maxevals=50, model_id=0, experiment_name=None): if experiment_name is None: self.experiment_name = self.arg
        ...

The definition of the decorator remains the same as shown above.装饰器的定义和上面一样。

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

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