如何将相同的装饰器链应用于多个函数
How to apply the same decorator chain to multiple functions
@extend_schema(
methods=['GET'],
responses={(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY})
@extend_schema(
methods=['PUT'],
request={STYLES_MIME_TYPE: OpenApiTypes.BINARY},
responses={(204, 'application/json'): OpenApiResponse(
response={'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
examples=[OpenApiExample(
'Returned style IDs example',
status_codes=['204'],
value=[101, 102, 103])])})
@api_view(['GET', 'PUT'])
@permission_classes([IsAuthenticated|ReadOnly])
@renderer_classes([StylesRenderer, StylesJSONRenderer])
@parser_classes([StylesParser])
def styles(request: Request, pid: int) -> Response:
"""
Get or save styles for a project.
GET - protobuf binary response
POST - returnd IDs for saved styles
"""
try:
project = Project.objects.get(pk=pid)
return _handle_styles(request, project)
except Project.DoesNotExist:
raise Http404()
@extend_schema(
methods=['GET'],
responses={(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY})
@extend_schema(
methods=['PUT'],
request={STYLES_MIME_TYPE: OpenApiTypes.BINARY},
responses={(204, 'application/json'): OpenApiResponse(
response={'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
examples=[OpenApiExample(
'Returned style IDs example',
status_codes=['204'],
value=[101, 102, 103])])})
@api_view(['GET', 'PUT'])
@permission_classes([IsAuthenticated|ReadOnly])
@renderer_classes([StylesRenderer, StylesJSONRenderer])
@parser_classes([StylesParser])
def styles_xref(request: Request, xref: uuid.UUID) -> Response:
"""
Get or save styles for a project.
GET - protobuf binary response
POST - returnd IDs for saved styles
"""
try:
project = Project.objects.get(xref=xref)
return _handle_styles(request, project)
except Project.DoesNotExist:
raise Http404()
这是 Django,显然我想对这 2 个视图使用相同的装饰器。唯一的区别是一个通过 int ID 查找对象,另一个通过 UUID 外部引用字段查找对象。我怎样才能保持干燥?
的修改示例
装饰器本质上是普通函数。因此,您可以使用分部方法为它们提供一组参数,而无需实际调用该方法。
from functools import partial
def decorator(like):
print("Inside decorator")
def inner(func):
print(like)
func()
return inner
premade_decorator = partial(decorator, like = "geeksforgeeks")
@premade_decorator()
def my_func():
print("Inside actual function")
现在我们知道如何 pre-apply 装饰器的参数,我们可以尝试链接它们:
def chain_decorator(dec_list):
def inner(func):
for dec in dec_list:
func = partial(dec(), func)
return func
return inner
@chain_decorator([premade_decorator1, premade_decorator2])
def my_func():
print("Inside actual function")
return 1
print(my_func())
您可以定义一个新装饰器,其中 returns 一个 pre-decorated 函数与您想要的链。比如我们可以先定义三个自定义装饰器:
import functools
# A decorator factory which returns a new decorator.
def decorator_factory(message):
def decorator(function):
# Wraps the decorated function.
@functools.wraps(function)
def wrapper(*args, **kwargs):
# Example behavior:
# - Prints a message before calling the decorated function.
print(message)
# Calls the decorated function.
return function(*args, **kwargs)
return wrapper
return decorator
# Defines three new decorators.
decorator_1 = decorator_factory("Ham")
decorator_2 = decorator_factory("Spam")
decorator_3 = decorator_factory("Eggs")
目前调用这些装饰器的方式类似于以下内容,对于多个函数,它很快就会变得重复:
@decorator_1
@decorator_2
@decorator_3
def f():
pass # Do something.
@decorator_1
@decorator_2
@decorator_3
def g():
pass # Do something.
@decorator_1
@decorator_2
@decorator_3
def h():
pass # Do something.
但是,您可以在装饰器的主体内装饰包装函数:
def decorator_chain(function):
@functools.wraps(function)
@decorator_1
@decorator_2
@decorator_3
def wrapper(*args, **kwargs):
return function(*args, **kwargs)
return wrapper
这将函数定义简化为:
@decorator_chain
def f():
pass # Do something.
@decorator_chain
def g():
pass # Do something.
@decorator_chain
def h():
pass # Do something.
在您提供的示例中,这可能类似于以下内容:
import functools
def decorator_chain(function):
@functools.wraps(function)
@extend_schema(
methods = ['GET'],
responses = {(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY}
)
@extend_schema(
methods = ['PUT'],
request = {STYLES_MIME_TYPE: OpenApiTypes.BINARY},
responses = {
(204, 'application/json'): OpenApiResponse(
response = {'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
examples = [
OpenApiExample(
'Returned style IDs example',
status_codes = ['204'],
value = [101, 102, 103]
)
]
)
}
)
@api_view(['GET', 'PUT'])
@permission_classes([IsAuthenticated | ReadOnly])
@renderer_classes([StylesRenderer, StylesJSONRenderer])
@parser_classes([StylesParser])
def wrapper(*args, **kwargs):
return function(*args, **kwargs)
return wrapper
@decorator_chain
def styles(request: Request, pid: int) -> Response:
"""
Get or save styles for a project.
GET - protobuf binary response
POST - returnd IDs for saved styles
"""
try:
project = Project.objects.get(pk=pid)
return _handle_styles(request, project)
except Project.DoesNotExist:
raise Http404()
@decorator_chain
def styles_xref(request: Request, xref: uuid.UUID) -> Response:
"""
Get or save styles for a project.
GET - protobuf binary response
POST - returnd IDs for saved styles
"""
try:
project = Project.objects.get(xref=xref)
return _handle_styles(request, project)
except Project.DoesNotExist:
raise Http404()
使用装饰器工厂甚至可以让您快速创建给定装饰器链的不同变体。
@extend_schema(
methods=['GET'],
responses={(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY})
@extend_schema(
methods=['PUT'],
request={STYLES_MIME_TYPE: OpenApiTypes.BINARY},
responses={(204, 'application/json'): OpenApiResponse(
response={'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
examples=[OpenApiExample(
'Returned style IDs example',
status_codes=['204'],
value=[101, 102, 103])])})
@api_view(['GET', 'PUT'])
@permission_classes([IsAuthenticated|ReadOnly])
@renderer_classes([StylesRenderer, StylesJSONRenderer])
@parser_classes([StylesParser])
def styles(request: Request, pid: int) -> Response:
"""
Get or save styles for a project.
GET - protobuf binary response
POST - returnd IDs for saved styles
"""
try:
project = Project.objects.get(pk=pid)
return _handle_styles(request, project)
except Project.DoesNotExist:
raise Http404()
@extend_schema(
methods=['GET'],
responses={(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY})
@extend_schema(
methods=['PUT'],
request={STYLES_MIME_TYPE: OpenApiTypes.BINARY},
responses={(204, 'application/json'): OpenApiResponse(
response={'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
examples=[OpenApiExample(
'Returned style IDs example',
status_codes=['204'],
value=[101, 102, 103])])})
@api_view(['GET', 'PUT'])
@permission_classes([IsAuthenticated|ReadOnly])
@renderer_classes([StylesRenderer, StylesJSONRenderer])
@parser_classes([StylesParser])
def styles_xref(request: Request, xref: uuid.UUID) -> Response:
"""
Get or save styles for a project.
GET - protobuf binary response
POST - returnd IDs for saved styles
"""
try:
project = Project.objects.get(xref=xref)
return _handle_styles(request, project)
except Project.DoesNotExist:
raise Http404()
这是 Django,显然我想对这 2 个视图使用相同的装饰器。唯一的区别是一个通过 int ID 查找对象,另一个通过 UUID 外部引用字段查找对象。我怎样才能保持干燥?
装饰器本质上是普通函数。因此,您可以使用分部方法为它们提供一组参数,而无需实际调用该方法。
from functools import partial
def decorator(like):
print("Inside decorator")
def inner(func):
print(like)
func()
return inner
premade_decorator = partial(decorator, like = "geeksforgeeks")
@premade_decorator()
def my_func():
print("Inside actual function")
现在我们知道如何 pre-apply 装饰器的参数,我们可以尝试链接它们:
def chain_decorator(dec_list):
def inner(func):
for dec in dec_list:
func = partial(dec(), func)
return func
return inner
@chain_decorator([premade_decorator1, premade_decorator2])
def my_func():
print("Inside actual function")
return 1
print(my_func())
您可以定义一个新装饰器,其中 returns 一个 pre-decorated 函数与您想要的链。比如我们可以先定义三个自定义装饰器:
import functools
# A decorator factory which returns a new decorator.
def decorator_factory(message):
def decorator(function):
# Wraps the decorated function.
@functools.wraps(function)
def wrapper(*args, **kwargs):
# Example behavior:
# - Prints a message before calling the decorated function.
print(message)
# Calls the decorated function.
return function(*args, **kwargs)
return wrapper
return decorator
# Defines three new decorators.
decorator_1 = decorator_factory("Ham")
decorator_2 = decorator_factory("Spam")
decorator_3 = decorator_factory("Eggs")
目前调用这些装饰器的方式类似于以下内容,对于多个函数,它很快就会变得重复:
@decorator_1
@decorator_2
@decorator_3
def f():
pass # Do something.
@decorator_1
@decorator_2
@decorator_3
def g():
pass # Do something.
@decorator_1
@decorator_2
@decorator_3
def h():
pass # Do something.
但是,您可以在装饰器的主体内装饰包装函数:
def decorator_chain(function):
@functools.wraps(function)
@decorator_1
@decorator_2
@decorator_3
def wrapper(*args, **kwargs):
return function(*args, **kwargs)
return wrapper
这将函数定义简化为:
@decorator_chain
def f():
pass # Do something.
@decorator_chain
def g():
pass # Do something.
@decorator_chain
def h():
pass # Do something.
在您提供的示例中,这可能类似于以下内容:
import functools
def decorator_chain(function):
@functools.wraps(function)
@extend_schema(
methods = ['GET'],
responses = {(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY}
)
@extend_schema(
methods = ['PUT'],
request = {STYLES_MIME_TYPE: OpenApiTypes.BINARY},
responses = {
(204, 'application/json'): OpenApiResponse(
response = {'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
examples = [
OpenApiExample(
'Returned style IDs example',
status_codes = ['204'],
value = [101, 102, 103]
)
]
)
}
)
@api_view(['GET', 'PUT'])
@permission_classes([IsAuthenticated | ReadOnly])
@renderer_classes([StylesRenderer, StylesJSONRenderer])
@parser_classes([StylesParser])
def wrapper(*args, **kwargs):
return function(*args, **kwargs)
return wrapper
@decorator_chain
def styles(request: Request, pid: int) -> Response:
"""
Get or save styles for a project.
GET - protobuf binary response
POST - returnd IDs for saved styles
"""
try:
project = Project.objects.get(pk=pid)
return _handle_styles(request, project)
except Project.DoesNotExist:
raise Http404()
@decorator_chain
def styles_xref(request: Request, xref: uuid.UUID) -> Response:
"""
Get or save styles for a project.
GET - protobuf binary response
POST - returnd IDs for saved styles
"""
try:
project = Project.objects.get(xref=xref)
return _handle_styles(request, project)
except Project.DoesNotExist:
raise Http404()
使用装饰器工厂甚至可以让您快速创建给定装饰器链的不同变体。