带有自参数的类方法装饰器?
Posted
技术标签:
【中文标题】带有自参数的类方法装饰器?【英文标题】:Class method decorator with self arguments? 【发布时间】:2020-04-18 13:56:43 【问题描述】:如何将类字段作为参数传递给类方法上的装饰器?我想做的是:
class Client(object):
def __init__(self, url):
self.url = url
@check_authorization("some_attr", self.url)
def get(self):
do_work()
它抱怨 self 不存在将self.url
传递给装饰器。有没有办法解决这个问题?
【问题讨论】:
这是您可以控制的自定义装饰器,还是您无法更改的自定义装饰器? 它是我的装饰器,所以我可以完全控制它 它在初始化之前被调用我认为是问题...... 问题是 self 在函数定义时不存在。你需要把它变成一个偏函数。 【参考方案1】:你不能。类主体中没有self
,因为不存在实例。您需要传递它,例如,包含要在实例上查找的属性名称的 str
,然后返回的函数可以执行此操作,或者完全使用不同的方法。
【讨论】:
【参考方案2】:是的。不要在类定义时传入实例属性,而是在运行时检查它:
def check_authorization(f):
def wrapper(*args):
print args[0].url
return f(*args)
return wrapper
class Client(object):
def __init__(self, url):
self.url = url
@check_authorization
def get(self):
print 'get'
>>> Client('http://www.google.com').get()
http://www.google.com
get
装饰器拦截方法参数;第一个参数是实例,因此它从中读取属性。如果您不想硬编码属性名称,您可以将属性名称作为字符串传递给装饰器并使用getattr
:
def check_authorization(attribute):
def _check_authorization(f):
def wrapper(self, *args):
print getattr(self, attribute)
return f(self, *args)
return wrapper
return _check_authorization
【讨论】:
有没有办法直接在装饰器中传递@staticmethod? (一般来说)。我发现我们不能在装饰器中引用 Even 类。 @ShivKrishnaJaiswal 直接在装饰器中传递@staticmethod
到底是什么意思?您可以通过使用 @staticmethod
装饰器来摆脱对象引用要求,但是,它不会解决 OP 的问题......当然,您可以在装饰器中将包装器装饰为 @staticmethod
并且如果使用它应该可以工作正确(在 python 3.9 上测试),但我认为没有理由这样做。这样的装饰器将无法在没有类的函数上使用。此外,如果需要,您甚至可以在已经修饰的方法上使用@staticmethod
...【参考方案3】:
from re import search
from functools import wraps
def is_match(_lambda, pattern):
def wrapper(f):
@wraps(f)
def wrapped(self, *f_args, **f_kwargs):
if callable(_lambda) and search(pattern, (_lambda(self) or '')):
f(self, *f_args, **f_kwargs)
return wrapped
return wrapper
class MyTest(object):
def __init__(self):
self.name = 'foo'
self.surname = 'bar'
@is_match(lambda x: x.name, 'foo')
@is_match(lambda x: x.surname, 'foo')
def my_rule(self):
print 'my_rule : ok'
@is_match(lambda x: x.name, 'foo')
@is_match(lambda x: x.surname, 'bar')
def my_rule2(self):
print 'my_rule2 : ok'
test = MyTest()
test.my_rule()
test.my_rule2()
输出: my_rule2:好的
【讨论】:
@raphael 在此设置中,我似乎无法访问 _lambda 或模式。我该如何补救。 @Raphael:我怎样才能对类方法做同样的事情,因为这里所有的方法都是实例方法。【参考方案4】:一个更简洁的例子可能如下:
#/usr/bin/env python3
from functools import wraps
def wrapper(method):
@wraps(method)
def _impl(self, *method_args, **method_kwargs):
method_output = method(self, *method_args, **method_kwargs)
return method_output + "!"
return _impl
class Foo:
@wrapper
def bar(self, word):
return word
f = Foo()
result = f.bar("kitty")
print(result)
将打印的内容:
kitty!
【讨论】:
IMO,这优于***.com/a/11731208/257924。它演示了内部函数_impl
如何访问self
以出于任何目的操纵self
。我需要构建一个简单的方法装饰器,它在类中方法的子集 上增加self.id
,并且只在类中应用了“@”装饰语法的那些方法。与***.com/a/56322968/257924 相比,语法糖将其支付给我的未来自我,后者放弃了糖并要求我深入了解__init__
方法。
这对我很有帮助。谢谢。【参考方案5】:
另一种选择是放弃语法糖并在类的__init__
中进行装饰。
def countdown(number):
def countdown_decorator(func):
def func_wrapper():
for index in reversed(range(1, number+1)):
print(index)
func()
return func_wrapper
return countdown_decorator
class MySuperClass():
def __init__(self, number):
self.number = number
self.do_thing = countdown(number)(self.do_thing)
def do_thing(self):
print('im doing stuff!')
myclass = MySuperClass(3)
myclass.do_thing()
会打印出来
3
2
1
im doing stuff!
【讨论】:
这个实用多了。例如。投票最多的示例将“url”属性硬编码到装饰器定义中。【参考方案6】:我知道这是一个老问题,但尚未提及此解决方案,希望即使在 8 年后的今天,它也能对某人有所帮助。
那么,包装一个包装器怎么样?让我们假设一个人不能change the decorator 也不能decorate those methods in init (他们可能是@property 装饰或其他)。总是有可能创建自定义的、特定于类的装饰器,该装饰器将捕获自身并随后调用原始装饰器,将运行时属性传递给它。
这是一个工作示例 (f-strings require python 3.6):
import functools
# imagine this is at some different place and cannot be changed
def check_authorization(some_attr, url):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"checking authorization for 'url'...")
return func(*args, **kwargs)
return wrapper
return decorator
# another dummy function to make the example work
def do_work():
print("work is done...")
###################
# wrapped wrapper #
###################
def custom_check_authorization(some_attr):
def decorator(func):
# assuming this will be used only on this particular class
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
# get url
url = self.url
# decorate function with original decorator, pass url
return check_authorization(some_attr, url)(func)(self, *args, **kwargs)
return wrapper
return decorator
#############################
# original example, updated #
#############################
class Client(object):
def __init__(self, url):
self.url = url
@custom_check_authorization("some_attr")
def get(self):
do_work()
# create object
client = Client(r"https://***.com/questions/11731136/class-method-decorator-with-self-arguments")
# call decorated function
client.get()
输出:
checking authorisation for 'https://***.com/questions/11731136/class-method-decorator-with-self-arguments'...
work is done...
【讨论】:
【参考方案7】:我知道这个问题已经很老了,但是之前没有提出过以下解决方法。这里的问题是你不能在类块中访问self
,但你可以在类方法中。
让我们创建一个虚拟装饰器来重复一个函数。
import functools
def repeat(num_rep):
def decorator_repeat(func):
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
for _ in range(num_rep):
value = func(*args, **kwargs)
return
return wrapper_repeat
return decorator_repeat
class A:
def __init__(self, times, name):
self.times = times
self.name = name
def get_name(self):
@repeat(num_rep=self.times)
def _get_name():
print(f'Hi self.name')
_get_name()
【讨论】:
以上是关于带有自参数的类方法装饰器?的主要内容,如果未能解决你的问题,请参考以下文章