装饰师在输入中采取类和不同的参数

时间:2016-04-18 17:23:35

标签: python python-decorators

我想创建一个装饰器,将记录器添加到任何装饰类。我成功了:

def logged(cls_to_decorate):
    log_name = cls_to_decorate.__module__
    logger = logging.getLogger(log_name)

    setattr(cls_to_decorate, 'logger', logger)
    return cls_to_decorate

用法:

@logged
class TestFoo(TestCase):
    def test_foo_function(self):
         self.logger.debug("ciao!!")

现在让我们假设我想向这个装饰器传递一个额外的参数,所以要按如下方式使用它:

@logged(logging_level=logging.DEBUG)
class TestFoo(TestCase):
    pass

我试图将装饰器的语法用于函数/方法(带有包装函数),但当然因为我们所说的类作为参数它不起作用。

装饰者应该是这样的:

...
def logged(cls_to_decorate, logging_level=None):
    logging.basicConfig(level=logging_level)

    log_name = cls_to_decorate.__module__
    logger = logging.getLogger(log_name)

    setattr(cls_to_decorate, 'logger', logger)
    return cls_to_decorate
...

注意:要装饰的对象是而不是功能

解决方案python decorators with parameters应该适用,但我尝试了以下内容:

def logged(logging_level=None):
    def class_decorator(cls_to_decorate):

        # Do something with arguments
        logging.basicConfig(level=logging_level)
        log_name = cls_to_decorate.__module__
        logger = logging.getLogger(log_name)

        setattr(cls_to_decorate, 'logger', logger)
        return cls_to_decorate
    return class_decorator

但是每次我把一个论坛包起来时,我都会出错:

E               TypeError: class_decorator() takes exactly 1 argument (2 given)

感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

the answer here对于类作为函数的工作方式完全相同,创建一个嵌套的工厂函数:

def decorator(argument):
    def real_decorator(class):
        <DECORATOR CODE HERE>
    return real_decorator

所以你会这样做:

def logged(logging_level):
    def class_decorator(cls_to_decorate):
        log_name = cls_to_decorate.__module__
        logger = logging.getLogger(log_name)

        setattr(cls_to_decorate, 'logger', logger)
        return cls_to_decorate
    return class_decorator

请注意,这意味着您必须调用装饰器才能使用它,只需使用@logged就会认为该类是logging_level参数,这是没有意义的,你需要至少做@logged()才能使它发挥作用。

虽然因为您仍在使用关键字,但我希望在通话缺失信息时使用functools.partial提供替代方案:

def logger(cls_to_decorate=None, logging_level=logging.DEBUG):
    if cls_to_decorate is None:
        return functools.partial(logger, logging_level=logging_level)

    ... #other code here

当你执行logger(existing_cls)时它正常工作,当你@logger(KEYWORDS)它正常工作且只有@logged正常工作时,唯一的缺陷就是你不小心指定了选项该课程。

@logger(logging.INFO) #on no we passed a positional argument!!
class Test():pass

Traceback (most recent call last):
  File "/Users/Tadhg/Documents/codes/test.py", line 25, in <module>
    @logged(logging.INFO)
  File "/Users/Tadhg/Documents/codes/test.py", line 19, in logged
    log_name = cls_to_decorate.__module__
AttributeError: 'int' object has no attribute '__module__'

虽然可以通过在某个时候添加assert inspect.isclass(cls_to_decorate)来改进,但这不是最具信息量的错误消息。