首页 > 解决方案 > Python 类中的装饰器

问题描述

对不起我的英语不好。我想创建一个装饰器方法,它可以检查每个步骤方法并将其写入数据库。

这是我的方法:

class Test:

    @StepStatusManager.logger_steps("GET_LIST") # TypeError: logger_steps() missing 1 required positional argument: 'type'
    def get_mails(self):
       print("GET_MAIL")    

这是我的装饰类:

class StepStatusManager:

    def __init__(self):
        self.db = DB()

    def logger_steps(self, type):
        def logger_steps(func):
            @functools.wraps(func)
            def wrapper(*args):
                try:
                    func(*args)
                    self.db.setStatus(type)
                except BaseException as e:
                    print(e)

            return wrapper

        return logger_steps

标签: pythondecorator

解决方案


您正在尝试logger_steps直接从类调用实例方法 ,StepStatusManager而 Python 将值"GET_LIST"作为self参数而不是type。您应该创建一个实例,StepStatusManager然后让装饰器调用实例的方法。它可以很简单:

manager = StepStatusManager()

class Test:
    @manager.logger_steps("GET_LIST")
    def get_mails(self):
       print("GET_MAIL")

这现在是创建类的实例,然后调用实例上的方法,而不是尝试直接从类中调用方法。您现在可以根据manager需要使用来装饰任意数量的方法。此外,这将使所有装饰方法都使用相同的StepStatusManager,但如果您愿意,您可以创建不同的实例并使用它们来装饰不同的方法;如果需要,这将允许您对self.db不同的方法使用不同的方法。

另一种方法可能是db在类中包含变量,并改为创建logger_steps一个类方法:

class StepStatusManager:

    db = DB()

    @classmethod
    def logger_steps(cls, type):
        def logger_steps(func):
            @functools.wraps(func)
            def wrapper(*args):
                try:
                    func(*args)
                    cls.db.setStatus(type)
                except BaseException as e:
                    print(e)

            return wrapper

        return logger_steps

class Test:
    @StepStatusManager.logger_steps("GET_LIST")
    def get_mails(self):
       print("GET_MAIL")

但是请注意,这不太灵活,因为它不允许您使用不同的管理器装饰方法,如果您需要的话。此外,这几乎等同于拥有一个模块而不是一个类,StepStatusManager其中db是一个模块变量并且logger_steps是一个模块函数,如果您想要这个功能,这可能会更清楚:

# StepStatusManager.py

# ...

db = DB()

def logger_steps(type):
    def logger_steps(func):
        @functools.wraps(func)
        def wrapper(*args):
            try:
                func(*args)
                cls.db.setStatus(type)
            except BaseException as e:
                print(e)

        return wrapper

    return logger_steps

# test.py

import StepStatusManager

class Test:
    @StepStatusManager.logger_steps("GET_LIST")
    def get_mails(self):
       print("GET_MAIL")

同样,作为您提出的第一个基于类的解决方案,这可能更直接但不太灵活。


编辑:

只是为了完整性和比较,这里还有另一个版本,类似于 with 的版本@classmethod,但@staticmethod改为使用(要了解这两个装饰器之间的细微差别,请检查有关它的许多 SO 问题之一,例如What is the difference between @staticmethod和@classmethod?@classmethod 和@staticmethod 对初学者的意义?):

class StepStatusManager:

    db = DB()

    @staticmethod
    def logger_steps(type):
        def logger_steps(func):
            @functools.wraps(func)
            def wrapper(*args):
                try:
                    func(*args)
                    StepStatusManager.db.setStatus(type)
                except BaseException as e:
                    print(e)

            return wrapper

        return logger_steps

class Test:
    @StepStatusManager.logger_steps("GET_LIST")
    def get_mails(self):
       print("GET_MAIL")

由于@classmethod和经常发生这种情况@staticmethod,因此差异非常小。如果您使用继承,或者使用元类、装饰器或类似的东西,它们的行为可能会有所不同,但除此之外它们几乎相同。


推荐阅读