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

Python 类中的装饰器:不能在装饰函数内使用self,python,decorator,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): @functoo

我正试图实现一个带有装饰器的自定义记录器,该装饰器将以以下方式收集异常(稍后保存到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未定义
我还尝试了各种组合以将
self.logger
作为函数的参数传递,但我有点困惑,它们失败了。。。有人有办法解决我的问题吗?

正如您可能故意做的那样,在类a
staticmethod
中创建函数会使其成为“静态”的,从而使其无法访问实例的“self”属性。另外,类的
\uuuuu init\uuuu
从未运行过,因为您从未创建过该类的实例

您也可以通过在子命名空间中创建父实例来执行类似操作:

注意:此方法不涉及继承,如果您认为这是必要的,请尝试@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

正如您可能有意做的那样,在类a
staticmethod
中创建函数会使其成为“静态”函数,从而使其无法访问实例的“self”属性。另外,类的
\uuuuu init\uuuu
从未运行过,因为您从未创建过该类的实例

您也可以通过在子命名空间中创建父实例来执行类似操作:

注意:此方法不涉及继承,如果您认为这是必要的,请尝试@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

你的代码有几个问题。看看评论

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

你的代码有几个问题。看看评论

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

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

这样做意味着您必须使用
@Parent.logging()
调用装饰程序,而不仅仅是
@Parent.logging
。这可以确保创建
Log
实例并将其分配给
self.logger
,其中
self
logging
类的单例实例。请注意,
\uuu call\uu()
不是一个静态方法

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

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

这样做意味着您必须使用
@Parent.logging()
调用装饰程序,而不仅仅是
@Parent.logging
。这可以确保创建
Log
实例并将其分配给
self.logger
,其中
self
logging
类的单例实例。请注意,
\uuu call\uu()
不是一个静态方法

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

猜测一下,日志方法中的
self
没有定义,因为它没有作为参数显式传递给
logging
internal
。您可能需要从(
*args
的第一个元素)获取它。猜测一下,日志方法中的
self
没有定义,因为它没有作为参数显式传递给
logging
internal
。您可能需要从
*args
的(第一个元素)中获取它。您还可以将
内部
定义为
定义内部(self,*args)
,这样您不仅可以免费获得
自我
,而且还可以直接看到decorstor创建了一个方法。@SuperSaiyan Thax这太棒了!有一件事我不明白,那就是那行
super(Child,self)。\uuu init\uuu()
。这是干什么的?@SuperSaiyan实际上我现在明白了,但为什么它比调用Parent好。uu init_uu()您还可以将
internal
定义为
definner(self,*args)
,这样您不仅可以免费获得
self
,而且可以直接看到Decorsortor创建了一个方法。@SuperSaiyan Thax这太棒了!有一件事我不明白,那就是那行
super(Child,self)。\uuu init\uuu()
。这是干什么的?@SuperSaiyan实际上我现在明白了,但为什么它比调用Parent好呢?\uuu init\uuu()@abccd我在使用继承,就像在我的真实代码中一样,我想将记录器应用到不同的类。。。无需复制粘贴everywhere@abccd我正在使用继承,因为在我的真实代码中,我想将记录器应用到不同的类。。。无需复制粘贴到任何地方