类中的 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 是未知的
我也尝试过:
@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
扫码咨询,免费领取项目管理大礼包!