将实例变量传递给装饰器

2024-06-28 09:39:08 发布

您现在位置:Python中文网/ 问答频道 /正文

我发现了这个有用的装饰器,它允许您传入一些可选参数

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)

因此,在这样的用例中,我只需将字符串传递给experiment_name,代码就能完美地工作

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

我一直很难理解decorators中的作用域,但使用传递在__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`

为了确定作用域是否是一个问题,我在类外声明了变量,它起了作用

只是为了好玩,我决定在类中声明一个全局变量,它也可以工作,但不太理想,特别是如果我想将它作为可选参数传递到类或方法中

class LGBOptimizerMlfow:
    global arg
    arg = 'hi'

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

修改代码以使装饰器接受实例变量的任何帮助都将非常有用

谢谢大家!


Tags: nameselfnoneidtruereturnisdef
1条回答
网友
1楼 · 发布于 2024-06-28 09:39:08

在定义类时调用Decorators,而self只是用于每个实例方法的参数,而不是类本身提供的参数。因此self在需要用作装饰器的参数时没有定义

您需要修改experiment_wrapper以直接从self参数获取名称,而不是从参数获取mflow_experiment。差不多

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)

然后,您需要确保每个实例都有一个与之关联的实验名称(或没有)

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):
        ...

另一种选择是将experiment_name作为train_mflow的参数,这样可以更容易地使用相同的方法创建不同的名称。(这可能更接近您的预期。)

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
        ...

装饰器的定义与上面所示的相同

相关问题 更多 >