我以为我理解装饰者但不再了解。装饰器只在创建函数时起作用吗?
我想创建一系列函数,这些函数都有一个名为'ticket_params'的必需参数,这是一个字典。然后用@param_checker(['req_param_1', 'req_param_2'])
之类的东西装饰它们,然后如果'req_param_1'和'req_param_2'不在字典中,则引发一个自定义的Exception子类。我觉得这一切都错了吗?
在调用代码中会出现类似的情况:
@param_checker(['req_param_1', 'req_param_2'])
def my_decorated_function(params):
# do stuff
params = {'req_param_1': 'Some Value'}
my_decorated_function(params)
# exception would be raised here from decorator.
答案 0 :(得分:11)
在def
语句后立即应用装饰器;等价是:
@param_checker(['req_param_1', 'req_param_2'])
def my_decorated_function(params):
# do stuff
与完全相同:
def my_decorated_function(params):
# do stuff
my_decorated_function = param_checker(['req_param_1', 'req_param_2'])(my_decorated_function)
因此param_checker
的工作是返回一个函数,该函数将要装饰的函数作为其参数,并返回另一个执行所需操作的函数。好了到目前为止?
修改:所以,这是一个实现......:
import functools
def param_checker(reqs):
reqs = set(reqs)
def middling(f):
@functools.wraps(f)
def wrapper(params):
missing = reqs.difference(params)
if missing:
raise TypeError('Missing parms: %s' % ', '.join(sorted(missing)))
return f(params)
return wrapper
return middling
答案 1 :(得分:5)
装饰器只在函数上调用一次,也就是说,解析def
语句时是这样的:
@mydecorator
def myfunction(): ...
我认为你的意思是这样的:
class param_checker:
def __init__(self, l):
self.l = l
def __call__(self, functionToBeDecorated):
def wrapper(*args, **kwargs):
if any(necessary not in kwargs["ticket_params"] for necessary in self.l):
raise MyCustomException
return functionToBeDecorated(*args, **kwargs)
return wrapper
如果您不理解,请告诉我;)
答案 2 :(得分:3)
这是一个基于@AndiDog的例子的完整示例。记住任何可调用的都可以用作装饰器,它不必是类。
class MyCustomException(Exception):
pass
# The decorator - instances of this class are callable as it implements __call__
class param_checker:
# In this example l is the parameter you pass to the decorator.
# For example, l could be ['req_param_1', 'req_param_2'].
def __init__(self, l):
self.l = l
# This makes the instance callable
def __call__(self, functionToBeDecorated):
def wrapper(*args, **kwargs):
# For the successful call below args = () and
# kwargs = {'ticket_params': {'req_param_1': 'param_1', 'req_param_2': 'param_2'}}
if "ticket_params" not in kwargs or any(necessary not in kwargs["ticket_params"] for necessary in self.l):
# if the ticket params parameter has not been specified, or if
# any of the required parameters are not present raise an exception
raise MyCustomException
return functionToBeDecorated(*args, **kwargs)
return wrapper
@param_checker(['req_param_1', 'req_param_2'])
def myfunction(ticket_params=None):
# if the two required params are present this will print
print "params ", ticket_params
if __name__ == "__main__":
try:
myfunction()
except MyCustomException:
print "all required params not supplied"
try:
myfunction(ticket_params={'req_param_1': 'param_1'})
except MyCustomException:
print "all required params not supplied"
myfunction(ticket_params={'req_param_1': 'param_1', 'req_param_2': 'param_2'})
答案 3 :(得分:2)
检查Alex的答案以了解python装饰器;顺便说一下:
1)你不了解装饰师的什么?难道你不把装饰器理解为一般概念,还是Python装饰器?请注意,“经典”装饰模式,java注释和python装饰器是不同的东西。
2)python装饰器应该总是返回一个函数,例如在你的代码中,param_checker([...])的返回值应该是一个函数,它接受一个函数作为param(要修饰的func),并返回一个与my_decorated_function具有相同签名的函数。看一下下面的例子;装饰器函数只执行一次(创建类时),然后在每次调用时执行装饰的函数。在这个具体的例子中,它然后调用原始的func,但这不是一个要求。
def decorator(orig_func):
print orig_func
def decorated(self, a):
print "aahahah", orig_func(self, a)
return decorated
class Example(object):
@decorator
def do_example(self, a):
return 2 * a
m = Example()
m.do_example(1)
3)你可能没有像使用装饰器一样做得最好。当一些概念与你实际编程的内容完全正交时,通常应该使用它们,并且可以重复使用它们 - 它本质上是执行AOP的Python方式。你的param_checker可能不是那么正交 - 如果你的装饰器只被使用了一次,那么它根本不是一个使用装饰器的好地方。您的param_checker似乎就是这种情况 - 它假设装饰的func采用单个arg这是一个字典 - 您的代码中是否有许多具有这种签名和行为的函数?如果答案是“否”,只需在func的开头检查params,如果它们丢失则引发异常。
答案 4 :(得分:0)
对于那些提出同样问题的人来说,这是一个很好的解释:
# This does nothing.
class donothing(object):
def __init__(self, func):
"""
The 'func' argument is the function being decorated because in this
case, we're not instantiating the decorator class. Instead we are just
using the class object as a callable (a class is always callable as this
is how an instance is returned) to use as a decorator, which means that
it is being instantiated upon definition of the decorated function and
the decorated function is being passed in as an argument to the class's
__init__ method.
"""
self.func = func
def __call__(self, *args, **kwargs):
"""
The __call__ function is called when the decorated function is called
because the function has be eaten by the decorator class. Now it's up to
the this method to return a call to the original function. The arguments
are passed in as args, kwargs to be manipulated.
"""
# Returns original function call with original arguments.
return self.func(*args, **kwargs)
@donothing
def printer(text):
print(text)
printer('hello world')
# The printer function is now an alias for the donothing instance created, so
# the preceding was the same as:
#
# instance = donothing(printer)
# instance('hello world')
#
# Next example:
class checkforkeysinparams(object):
def __init__(self, required):
self.required = set(required)
def __call__(self, params):
def wrapper(params):
missing = self.required.difference(params)
if missing:
raise TypeError('Missing from "params" argument: %s' % ', '.join(sorted(missing)))
return wrapper
# Apply decorator class, passing in the __init__'s 'required' argument.
@checkforkeysinparams(['name', 'pass', 'code'])
def complex_function(params):
# Obviously these three are needed or a KeyError will be raised.
print(params['name'])
print(params['pass'])
print(params['code'])
# Create params to pass in. Note, I've commented out one of the required params.
params = {
'name': 'John Doe',
'pass': 'OpenSesame',
#'code': '1134',
}
# This call will output: TypeError: Missing from "params" argument: code
complex_function(params=params)