简体   繁体   English

类中的装饰器:无法在装饰函数内部使用self

[英]Decorator in class: cannot use self inside the decorated function

I am trying to implement a custom logger with a decorator that would collect exceptions (to save them to db later) in the following way: 我试图用装饰器实现一个自定义记录器,该装饰器将以以下方式收集异常(以后将它们保存到数据库):

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

However it seems that the decorator "break" the link between the method and the instance, as it cannot use self anymore... when running 但是,装饰器似乎“断开”了方法和实例之间的链接,因为它在运行时无法再使用self ...

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

I receive an error message self is not defined I also tried various combination to pass self.logger as an argument to the function, but I am a bit confused, and they failed... Anyone have an idea that could solve my problem? 我收到一条self is not defined的错误消息self is not defined我也尝试了各种组合以将self.logger作为函数的参数传递,但是我有点困惑,并且它们失败了……任何人都可以解决我的问题?

There were a couple of problems with your code. 您的代码有几个问题。 Look at the comments. 看评论。

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

As you may have done it intentionally, making a function in a class a staticmethod makes it "static", making it not able to access the "self" attribute of the instance. 正如您可能故意做到的那样,将类中的函数设为staticmethod会使它“静态”,从而使其无法访问实例的“自身”属性。 Plus, the __init__ of the class never ran because you never created an instance of the class. 另外,该类的__init__永远不会运行,因为您从未创建该类的实例。

You can alternatively do something like this by creating a Parent instance in the Child namescope: 您也可以通过在Child名称范围中创建Parent实例来执行以下操作:

Note: This method does not involve inheritance, if you believe it is a necessity, try @SuperSaiyan 's answer 注意:此方法不涉及继承,如果您认为有必要,请尝试@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

You could do it as shown in the following code, which variess from your approach in two primary ways. 您可以按照以下代码所示进行操作,这与您的方法有所不同,主要有两种。 It changes logging into a (nested) class and implements it as a singleton so only one instance of it will ever be created. 它将logging更改为(嵌套的)类,并将其实现为单例,因此将仅创建其一个实例。

Doing this means you must invoke the decorator with @Parent.logging() instead of just @Parent.logging . 这样做意味着你必须调用与装饰@Parent.logging()而不是仅仅@Parent.logging This ensures there's that a Log instance is created and assigned to self.logger where self is that the logging class's singleton instance. 这样可以确保创建一个Log实例并将其分配给self.logger ,其中selflogging类的单例实例。 Note that __call__() is not a staticmethod. 请注意__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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在装饰器中使用上下文管理器以及如何将在decorator中创建的对象传递给装饰函数 - How to use a context manager inside a decorator and how to pass an object created in decorator to decorated function 引用装饰器中装饰函数的名称 - referring to the name of the decorated function inside decorator 在课堂上使用带有 self 的装饰器 - use decorator with self in class 使用装饰器将参数添加到装饰函数 - Use decorator to add an argument to the decorated function 使用装饰器更改已装饰 function 的参数 - Use decorator to change argument of a decorated function 在Python装饰器中创建参数以供装饰函数使用 - Creating parameters in a Python decorator for use by the decorated function 用decorator类装饰的方法没有冻结“self”参数 - Methods decorated with a decorator class do not have the “self” argument frozen 如何在类中声明装饰器,以装饰已经装饰的继承方法? - How to declare decorator inside a class, to decorate an already decorated, inherited method? 在Python 3中访问装饰器内部装饰函数的关键字参数失败 - Accessing keyword argument of decorated function inside the decorator fails in Python 3 如何在装饰器中使用 pytest 固定装置而不将其作为装饰 function 的参数 - How to use pytest fixtures in a decorator without having it as argument on the decorated function
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM