类中的装饰器:不能在装饰函数内部使用self

时间:2017-04-21 00:05:13

标签: python decorator

我正在尝试使用装饰器实现一个自定义记录器,该装饰器将以下列方式收集异常(以便将它们保存到db):

import functools

class Log:
    def __init__(self):
        self.mssg = ""
        self.err = ""

class Parent:
    def __init__(self):
        self.logger = Log()

    def logging(fun):
        @functools.wraps(fun)
        def inner(*args):
            try:                
                print(fun.__name__)
                self.logger.mssg += fun.__name__ +" :ok, "  
                return fun(*args)

            except Exception as e:
                self.logger.err += fun.__name__ +": error: "+str(e.args) 
        return inner

    logging = staticmethod(logging)

class Child(Parent):
    def __init__(self, a, b): 
        self.a = a
        self.b = b

    @Parent.logging
    def sum_(self):
        return self.a + self.b
然而,似乎装饰者"打破"方法和实例之间的链接,因为它不能再使用self ...运行时

c = Child(3,6)
c.sum_()

我收到错误消息self is not defined我也尝试了各种组合来传递self.logger作为函数的参数,但我有点困惑,他们失败了......任何人都有一个想法可以解决我的问题吗?

3 个答案:

答案 0 :(得分:1)

您的代码存在一些问题。看看评论。

import functools

class Log:
    def __init__(self):
        self.mssg = ""
        self.err = ""

class Parent(object):
    def __init__(self):
        self.logger = Log()

    @staticmethod   #You can directly use staticmethod decorator!
    def logging(fun):
        @functools.wraps(fun)
        def inner(*args):
            self = args[0]  #Grab the first arg as self.
            try:                
                print(fun.__name__)
                self.logger.mssg += fun.__name__ +" :ok, "  
                return fun(self, *(args[1:]))  # Call the function using
                                               # self that we extracted.
            except Exception as e:
                self.logger.err += fun.__name__ +": error: "+str(e.args) 
        return inner

class Child(Parent):
    def __init__(self, a, b):
        super(Child, self).__init__()   #Don't forget call the parent ctor
        self.a = a
        self.b = b

    @Parent.logging
    def sum_(self):
        return self.a + self.b

c = Child(3,6)
print c.sum_()  #Outputs 9

答案 1 :(得分:0)

正如您可能故意这样做的那样,在staticmethod课程中创建一个函数会使其成为静态",使其无法访问" self"实例的属性。此外,该类的__init__从未运行,因为您从未创建过该类的实例。

您也可以通过在子名称范围中创建父实例来执行此类操作:

注意:此方法不涉及继承,如果您认为有必要,请尝试@SuperSaiyan的回答

import functools

class Log:
    def __init__(self):
        self.mssg = ""
        self.err = ""

class Parent:
    def __init__(self):
        self.logger = Log()

    def logging(self, fun): # include the "self" argument as it is no longer static
        @functools.wraps(fun)
        def inner(*args):
            try:
                print(fun.__name__)
                self.logger.mssg += fun.__name__ +" :ok, "
                return fun(*args)

            except Exception as e:
                self.logger.err += fun.__name__ +": error: "+str(e.args)
        return inner

class Child: # you do not need to inherit the Parent class if it's only used for the decorator

    myparent = Parent() # initiates the logger and create an instance of that class
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @myparent.logging # use myparent instead of Parent
    def sum_(self):
        return self.a + self.b

c = Child(3, 6)
print(c.sum_()) # prints sum_ and 9

答案 2 :(得分:0)

您可以按照以下代码中所示执行此操作,该代码以两种主要方式与您的方法不同。它将logging更改为(嵌套)类并将其实现为单例,因此只会创建它的一个实例。

这样做意味着您必须使用@Parent.logging()而不是@Parent.logging来调用装饰器。这可确保创建Log实例并将其分配给self.logger,其中selflogging类的单例实例。请注意,__call__() 不是静态方法。

import functools

class Log(object):
    def __init__(self):
        self.msg = ""
        self.err = ""

class Parent(object):
    class logging(object):  # singleton decorator class
        def __init__(self):
            self.logger = Log()

        def __new__(cls, *args, **kwargs):
            if '_inst_' not in vars(cls):
                cls._inst = object.__new__(cls)
            return cls._inst

        def __call__(self, fun):
            @functools.wraps(fun)
            def inner(*args, **kwargs):
                try:
                    print(fun.__name__)
                    self.logger.msg += fun.__name__+" :ok, "
                    return fun(*args, **kwargs)
                except Exception as exc:
                    self.logger.err += fun.__name__+": error: "+str(exc.args)
            return inner

class Child(Parent):
    def __init__(self, a, b):
        super(Child, self).__init__()   # initialize Parent
        self.a = a
        self.b = b

    @Parent.logging()  # must call and create decorator instance
    def sum_(self):
        return self.a + self.b

c = Child(3, 6)
print(c.sum_())  # -> 9