python 装饰器 decorator 是以函数为参数并返回函数的高阶函数。
正如字面含义,装饰意思即对其他函数进行修饰,限制等等。
装饰器仅仅是语法糖。 没有装饰器,一切照样可以实现,但需要多几行代码。 使用装饰器则可以帮助你编写更简洁的代码。
如下的代码:delete_database() 是删除数据库的函数,因为是危险的操作,所以要对其进行修饰(/限制),只允许管理员进行这项操作。
user = {'id': 1,'name': 'a_user','role': 'admin'}
def delete_database():
# perform deletion
print('Database deleted!')
def check_permission(func):
def wrapper():
if user.get('role') == 'admin':
return func()
else:
raise PermissionError('Not allowed, you are not an admin!')
return wrapper
secure_delete_database = check_permission(delete_database)
check_permission(func) 即是装饰器,如果是管理员,返回函数,允许操作,否则引发异常。
@ 的语法user = {'id': 1,'name': 'a_user','role': 'admin'}
def check_permission(func):
def wrapper():
'''Hi, this is wrapper'''
if user.get('role') == 'admin':
return func()
else:
raise PermissionError('Not allowed, you are not an admin!')
return wrapper
@check_permission # 使用 @ 语法!
def delete_database():
'''Delete the whole database'''
# perform deletion
print('Database deleted!')
print(delete_database.__name__)
print(delete_database.__doc__)
上面的代码存在一个问题,就是最后两个print 输出的都是 wrapper 函数的信息:
wrapper
Hi, this is wrapper
解决方法就是 decorator 里再加 decorator:
import functools # 1 导入 package
user = {'id': 1,'name': 'a_user','role': 'admin'}
def check_permission(func):
@functools.wraps(func) # 2 对 wrapper 加修饰器
def wrapper():
'''Hi, this is wrapper'''
if user.get('role') == 'admin':
return func()
else:
raise PermissionError('Not allowed, you are not an admin!')
return wrapper
@check_permission
def delete_database():
'''Delete the whole database'''
# perform deletion
print('Database deleted!')
print(delete_database.__name__)
print(delete_database.__doc__)
最后两行变为正确的输出:
delete_database
Delete the whole database
装饰带有参数的函数写法例子,这种方法不通用,其他函数想要使用此装饰器,也必须一样加参数:
import functools
user = {'user_name':'jose123', 'access_level':'admin'}
def user_has_permission(func):
@functools.wraps(func)
def secure_func(arg):
"""Hi, this is wrapper secure_func!"""
if user.get('access_level') == 'admin':
return func(arg)
return secure_func
@user_has_permission
def my_function(panel):
'''retrieves admin password'''
return f'Password for {panel} panel is 1234.'
print(my_function("Shipping"))
使用 *args 和 **kwargs 两种可变参数,使用单个星号参数,可以接受任意数量的位置参数,使用双星号,可以接受任意数量的命名参数:
import functools
user = {'user_name':'jose123', 'access_level':'admin'}
def user_has_permission(func):
@functools.wraps(func)
def secure_func(*args, **kwargs):
"""Hi, this is wrapper secure_func!"""
if user.get('access_level') == 'admin':
return func(*args, **kwargs)
return secure_func
@user_has_permission
def my_function(panel):
'''retrieves admin password'''
return f'Password for {panel} panel is 1234.'
@user_has_permission
def another():
pass
print(my_function("Shipping")) # Password for Shipping panel is 1234.
print(another()) # None
import functools
user = {'user_name':'jose123', 'access_level':'user'}
def user_has_permission(access_level):
def my_decorator(func):
@functools.wraps(func)
def secure_func(panel):
"""Hi, this is wrapper secure_func!"""
if user.get('access_level') == access_level:
return func(panel)
return secure_func
return my_decorator
@user_has_permission('user')
def my_function(panel):
'''retrieves admin password'''
return f'Password for {panel} panel is 1234.'
print(my_function("Shipping"))