如何将类字段作为参数传递给类方法上的装饰器?我想做的是:
class Client(object):
def __init__(self, url):
self.url = url
@check_authorization("some_attr", self.url)
def get(self):
do_work()
它抱怨自我不存在是为了传递自我。指向装饰器的Url。有办法解决这个问题吗?
如何将类字段作为参数传递给类方法上的装饰器?我想做的是:
class Client(object):
def __init__(self, url):
self.url = url
@check_authorization("some_attr", self.url)
def get(self):
do_work()
它抱怨自我不存在是为了传递自我。指向装饰器的Url。有办法解决这个问题吗?
当前回答
另一种选择是放弃语法糖,并在类的__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!
其他回答
我知道这个问题已经很老了,但是下面的解决方法以前没有人提过。这里的问题是你不能在类块中访问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()
你不能。类主体中没有self,因为不存在实例。您需要向它传递一个包含属性名称的str,以便在实例上进行查找,然后由返回的函数执行,或者使用完全不同的方法。
是的。不是在类定义时传入实例属性,而是在运行时检查它:
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
我知道这是一个老问题,但这个解决方案还没有被提及,希望它可以帮助到今天的人,8年后。
那么,如何包装包装?让我们假设不能改变装饰器,也不能装饰init中的这些方法(它们可能是@property decoration或其他)。总是有可能创建自定义的、特定于类的装饰器,该装饰器将捕获self并随后调用原始的装饰器,并将运行时属性传递给它。
下面是一个工作示例(f-string需要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://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments")
# call decorated function
client.get()
输出:
checking authorisation for 'https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments'...
work is done...
另一种选择是放弃语法糖,并在类的__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!