类中的 Python 装饰器

2025-02-27 09:05:00
admin
原创
72
摘要:问题描述:可以写类似的东西吗:class Test(object): def _decorator(self, foo): foo() @self._decorator def bar(self): pass 失败:@self 中的 self 是未知的...

问题描述:

可以写类似的东西吗:

class Test(object):
    def _decorator(self, foo):
        foo()

    @self._decorator
    def bar(self):
        pass

失败:@self 中的 self 是未知的

我也尝试过:

@Test._decorator(self)

也失败了:测试未知

我想暂时更改装饰器中的某些实例变量,然后运行装饰方法,然后再将它们改回来。


解决方案 1:

这样的事情能满足您的需要吗?

class Test(object):
    def _decorator(foo):
        def magic( self ) :
            print "start magic"
            foo( self )
            print "end magic"
        return magic

    @_decorator
    def bar( self ) :
        print "normal call"

test = Test()

test.bar()

这避免了调用 self 来访问装饰器,并将其作为常规方法隐藏在类命名空间中。

>>> import stackoverflow
>>> test = stackoverflow.Test()
>>> test.bar()
start magic
normal call
end magic
>>> 

编辑以回答评论中的问题:

如何在另一个类中使用隐藏装饰器

class Test(object):
    def _decorator(foo):
        def magic( self ) :
            print "start magic"
            foo( self )
            print "end magic"
        return magic

    @_decorator
    def bar( self ) :
        print "normal call"

    _decorator = staticmethod( _decorator )

class TestB( Test ):
    @Test._decorator
    def bar( self ):
        print "override bar in"
        super( TestB, self ).bar()
        print "override bar out"

print "Normal:"
test = Test()
test.bar()
print

print "Inherited:"
b = TestB()
b.bar()
print

输出:

Normal:
start magic
normal call
end magic

Inherited:
start magic
override bar in
start magic
normal call
end magic
override bar out
end magic

解决方案 2:

您想要做的事情是不可能的。例如,以下代码是否有效:

class Test(object):

    def _decorator(self, foo):
        foo()

    def bar(self):
        pass
    bar = self._decorator(bar)

当然,它无效,因为self此时尚未定义。 也是如此Test,因为直到类本身被定义(它正在定义)时它才会被定义。 我向您展示此代码片段是因为这是您的装饰器片段转换成的内容。

因此,正如您所看到的,访问像这样的装饰器中的实例实际上是不可能的,因为装饰器是在定义它们所附加到的任何函数/方法期间应用的,而不是在实例化期间应用的。

如果您需要类级访问,请尝试以下操作:

class Test(object):

    @classmethod
    def _decorator(cls, foo):
        foo()

    def bar(self):
        pass
Test.bar = Test._decorator(Test.bar)

解决方案 3:

import functools


class Example:

    def wrapper(func):
        @functools.wraps(func)
        def wrap(self, *args, **kwargs):
            print("inside wrap")
            return func(self, *args, **kwargs)
        return wrap

    @wrapper
    def method(self):
        print("METHOD")

    wrapper = staticmethod(wrapper)


e = Example()
e.method()

解决方案 4:

self这是从同一个类内部定义访问(并使用)的一种方法decorator

class Thing(object):
    def __init__(self, name):
        self.name = name

    def debug_name(function):
        def debug_wrapper(*args):
            self = args[0]
            print 'self.name = ' + self.name
            print 'running function {}()'.format(function.__name__)
            function(*args)
            print 'self.name = ' + self.name
        return debug_wrapper

    @debug_name
    def set_name(self, new_name):
        self.name = new_name

输出(已测试Python 2.7.10):

>>> a = Thing('A')
>>> a.name
'A'
>>> a.set_name('B')
self.name = A
running function set_name()
self.name = B
>>> a.name
'B'

上面的例子很愚蠢,但它有效。

解决方案 5:

以下是 Michael Speer 的答案的扩展,以进一步说明:

一个实例方法装饰器,它接受参数并对具有参数和返回值的函数进行操作。

class Test(object):
    "Prints if x == y. Throws an error otherwise."
    def __init__(self, x):
        self.x = x

    def _outer_decorator(y):
        def _decorator(foo):
            def magic(self, *args, **kwargs) :
                print("start magic")
                if self.x == y:
                    return foo(self, *args, **kwargs)
                else:
                    raise ValueError("x ({}) != y ({})".format(self.x, y))
                print("end magic")
            return magic

        return _decorator

    @_outer_decorator(y=3)
    def bar(self, *args, **kwargs) :
        print("normal call")
        print("args: {}".format(args))
        print("kwargs: {}".format(kwargs))

        return 27

进而

In [2]:

    test = Test(3)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    normal call
    args: (13, 'Test')
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
    27
In [3]:

    test = Test(4)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-3-576146b3d37e> in <module>()
          4     'Test',
          5     q=9,
    ----> 6     lollipop=[1,2,3]
          7 )

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
         11                     return foo(self, *args, **kwargs)
         12                 else:
    ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
         14                 print("end magic")
         15             return magic

    ValueError: x (4) != y (3)

解决方案 6:

我在研究一个非常相似的问题时发现了这个问题。我的解决方案是将问题分成两部分。首先,您需要捕获要与类方法关联的数据。在这种情况下,handler_for 将把 Unix 命令与该命令输出的处理程序关联起来。

class OutputAnalysis(object):
    "analyze the output of diagnostic commands"
    def handler_for(name):
        "decorator to associate a function with a command"
        def wrapper(func):
            func.handler_for = name
            return func
        return wrapper
    # associate mount_p with 'mount_-p.txt'
    @handler_for('mount -p')
    def mount_p(self, slurped):
        pass

现在我们已经将一些数据与每个类方法关联起来,我们需要收集这些数据并将其存储在类属性中。

OutputAnalysis.cmd_handler = {}
for value in OutputAnalysis.__dict__.itervalues():
    try:
        OutputAnalysis.cmd_handler[value.handler_for] = value
    except AttributeError:
        pass

解决方案 7:

我在一些调试情况下使用这种类型的装饰器,它允许通过装饰来覆盖类属性,而无需找到调用函数。

class myclass(object):
    def __init__(self):
        self.property = "HELLO"

    @adecorator(property="GOODBYE")
    def method(self):
        print self.property

这是装饰器代码

class adecorator (object):
    def __init__ (self, *args, **kwargs):
        # store arguments passed to the decorator
        self.args = args
        self.kwargs = kwargs

    def __call__(self, func):
        def newf(*args, **kwargs):

            #the 'self' for a method function is passed as args[0]
            slf = args[0]

            # replace and store the attributes
            saved = {}
            for k,v in self.kwargs.items():
                if hasattr(slf, k):
                    saved[k] = getattr(slf,k)
                    setattr(slf, k, v)

            # call the method
            ret = func(*args, **kwargs)

            #put things back
            for k,v in saved.items():
                setattr(slf, k, v)

            return ret
        newf.__doc__ = func.__doc__
        return newf 

注意:因为我使用了类装饰器,所以您需要使用带括号的@adecorator()来装饰函数,即使您没有向装饰器类构造函数传递任何参数。

解决方案 8:

简单的方法就是把装饰器方法放在类的外面,你仍然可以在里面使用它。

def my_decorator(func):
    #this is the key line. There's the aditional self parameter
    def wrap(self, *args, **kwargs):
        # you can use self here as if you were inside the class
        return func(self, *args, **kwargs)
    return wrap

class Test(object):
    @my_decorator
    def bar(self):
        pass

解决方案 9:

在内部类中声明。此解决方案非常可靠且值得推荐。

class Test(object):
    class Decorators(object):
    @staticmethod
    def decorator(foo):
        def magic(self, *args, **kwargs) :
            print("start magic")
            foo(self, *args, **kwargs)
            print("end magic")
        return magic

    @Decorators.decorator
    def bar( self ) :
        print("normal call")

test = Test()

test.bar()

结果:

>>> test = Test()
>>> test.bar()
start magic
normal call
end magic
>>> 

解决方案 10:

装饰器似乎更适合修改整个对象(包括函数对象)的功能,而不是通常依赖于实例属性的对象方法的功能。例如:

def mod_bar(cls):
    # returns modified class

    def decorate(fcn):
        # returns decorated function

        def new_fcn(self):
            print self.start_str
            print fcn(self)
            print self.end_str

        return new_fcn

    cls.bar = decorate(cls.bar)
    return cls

@mod_bar
class Test(object):
    def __init__(self):
        self.start_str = "starting dec"
        self.end_str = "ending dec" 

    def bar(self):
        return "bar"

输出为:

>>> import Test
>>> a = Test()
>>> a.bar()
starting dec
bar
ending dec

解决方案 11:

我有一个装饰器实现可能会有帮助

    import functools
    import datetime


    class Decorator(object):

        def __init__(self):
            pass


        def execution_time(func):

            @functools.wraps(func)
            def wrap(self, *args, **kwargs):

                """ Wrapper Function """

                start = datetime.datetime.now()
                Tem = func(self, *args, **kwargs)
                end = datetime.datetime.now()
                print("Exection Time:{}".format(end-start))
                return Tem

            return wrap


    class Test(Decorator):

        def __init__(self):
            self._MethodName = Test.funca.__name__

        @Decorator.execution_time
        def funca(self):
            print("Running Function : {}".format(self._MethodName))
            return True


    if __name__ == "__main__":
        obj = Test()
        data = obj.funca()
        print(data)

解决方案 12:

使用静态方法并在装饰器的内部函数(包装器)中包含一个附加参数(self)。

class Test:

    @staticmethod
    def _decorator(f):

        @functools.wraps(f)
        def _wrapper(self, *args, **kwargs):
            # do some serious decorating (incl. calls to self!)
            print(self)
            return f(self, *args, **kwargs)

        return _wrapper

    @_decorator
    def bar(self):
        return 42

解决方案 13:

你可以装饰装饰器:

import decorator

class Test(object):
    @decorator.decorator
    def _decorator(foo, self):
        foo(self)

    @_decorator
    def bar(self):
        pass

解决方案 14:

由于您正在调用类函数,因此解包中的第一个参数args将是对的引用Self@MyClass。您可以从中调用必要的函数。

def dec(key: str):
    def decorator(function):
        def wrapper(*args, **kwargs):
            self: MyClass = args[0] # Create the reference to self
            print("{} is {}".format(key, self.__getattribute__(key)))
            result = function(*args, **kwargs)
            return result
        return wrapper
    return decorator
class MyClass:
    alive = False
    def __init__(self) -> None:
        pass
    @dec("alive")
    def ping(self):
        print("pong")
dt = MyClass() 
dt.ping()

解决方案 15:

对于 Python 3 和 linters

def methoddecorator(deco: Callable[[Any, Callable], Callable]):
"""
Decorator to implement method decorators in the same class

Example of usage:

    class A:
        @methoddecorator
        def my_methods_deco(self, method):
            @wraps(method)
            def wrapper(this: 'A', *args, **kwargs):
                # do smth
                # N.B. for instance access use this, not self!
                return method(this, *args, **kwargs)
            return wrapper

        @my_methods_deco
        def my_method(self, a, b):
            ...

"""
@functools.wraps(deco)
def wrapped_deco(method):
    return deco(NotImplemented, method)
return wrapped_deco

使用这个超级装饰器来修补类别。

顺便说一句,此代码不支持像这样的装饰器参数@deco(param=...),但更复杂的代码支持。

def methoddecorator(deco):
"""
Decorator to implement method decorators in the same class
Supports optionally parametrized decorators

Example of usage:

    class A:
        @methoddecorator
        def my_methods_deco(self, _method=None, param1=None, param2=None):
            @wraps(method)
            def wrapper(this: 'A', *args, **kwargs):
                # do smth
                # deco params are also available here
                return method(this, *args, **kwargs)
            return wrapper

        @my_methods_deco
        def my_method1(self, a, b):
            ...

        @my_methods_deco(param1=11, param2=12)
        def my_method2(self, a, b):
            ...

"""
@wraps(deco)
def wrapped_deco(_method=None, **kwargs):
    return (
        deco(NotImplemented, _method)
        if _method is not None
        else partial(deco, NotImplemented, **kwargs)
    )
return wrapped_deco
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3878  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2714  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   49  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   49  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   49  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用