可选的/required/keyword-only装饰器参数很容易实现。
decorator-args的Python项目详细描述
这个迷你图书馆远不是革命性的,也不是必不可少的,但它的功能可能对某些人有用。 这是一个混乱的想法和特点,我并不真的觉得是坚如磐石,但我认为这不是一个 把它放归野外是不可饶恕的罪恶感。在最坏的情况下,您可以打开“请销毁此代码/库” 在其github页面上发布。
内容
Usage
Problem to solve
下面的代码片段显示了两个示例:
- 在不带任何参数的情况下应用名为argless的装饰器。
- 应用名为argful的装饰器,该装饰器在应用前接收参数。
#1@arglessdefdecorated_function():...#2@argful('arg1_value',arg2='arg2_value')defdecorated_function():...
这个库试图使实现接收参数的decorator(如上面的argful_decorator)变得更容易。 除此之外,它还提供了一些相关的额外功能(可选的和仅限关键字的decorator参数)。
How this library helps
以前使用的argfuldecorator可以用无数种方法实现,但是有两种最标准的方法可以实现它 如果没有这个库,则如下所示:
- “初始风格”功能实现:
defargful(arg1,arg2='arg2_default'):# TODO: Validate and pre-process decorator args as early as possible for easier debuggingdefdecorate(decoratable):@functools.wraps(decoratable)defwrapper(*args,**kwargs):# TODO: Manipulate the input and output of the wrapped# decoratable object and use arg1 and arg2 if you want...returndecoratable(*args,**kwargs)returnwrapperreturndecorate
- 类实现:
classargful(object):def__init__(self,arg1,arg2='arg2_default'):# TODO: Validate and pre-process decorator args as early as possible for easier debuggingself.arg1=arg1self.arg2=arg2def__call__(self,decoratable):@functools.wraps(decoratable)defwrapper(*args,**kwargs)# TODO: Manipulate the input and output of the wrapped# decoratable object and use self.arg1 and self.arg2 if you want...returndecoratable(*args,**kwargs)returnwrapper
此库提供的decorator_args.decorator_argsdecorator可以从 示例1中的“inception style”实现使代码更简单、可读性更强:
fromdecorator_argsimportdecorator_args@decorator_argsdefargful(decoratable,arg1,arg2='arg2_default'):@functools.wraps(decoratable)defwrapper(*args,**kwargs):# TODO: Manipulate the input and output of the wrapped# decoratable object and use arg1 and arg2 if you want...returndecoratable(*args,**kwargs)returnwrapper
同时,此库还提供以下附加功能:
- 它可以强制为您的decorator传递仅关键字的参数。在某些情况下,这是可取的,因为它可以 代码更易于阅读和理解:
# Decorator implementation with keyword-only decorator arguments:@decorator_args(keyword_only=True)defargful(decoratable,arg1,arg2='arg2_default'):...# This would fail with a ``TypeError('This decorator receives only keyword arguments')``@argful('arg1_value',arg2='arg2_value')defdecorated_function():...# This is OK because all args are passed as keyword args@argful(arg1='arg1_value',arg2='arg2_value')defdecorated_function():...
- 如果您的decorator没有必需的参数,并且您使用此库的optional功能,则 如果不想向装饰符传递任何参数,请在不带参数列表的情况下应用装饰符:
# Decorator implementation with optional argument list:# Note that our decorator doesn't have required args other than the decoratable object:@decorator_args(optional=True)defargful(decoratable,arg1='arg1_default',arg2='arg2_default'):...# This works because of using `optional=True` above:@argfuldefdecorated_function():...# This would work even without `optional=True` in our decorator implementation:@argful()defdecorated_function():...# Of course passing actual args also works as expected:@argful('arg1_value','arg2_value')defdecorated_function():...
Library interface
库提供了一个decorator_args.decorator_argsdecorator,它是库的主要“主力”,并且 一组其他装饰器,它们只是前面提到的主装饰器周围的方便助手。 从语法上看,这些修饰符的参数是可选的,仅限关键字。
Main “entrypoint”
decorator_args.decorator_args(*,keyword_only=false,optional=false,is_decoratible_object=none)
The main decorator of the library. All other decorators are just convenience helpers based on this one.
^{tt8}$: Makes the arguments of your decorator keyword-only. Passing any positional arguments to your decorator will result in a ^{tt9}$ with an appropriate error message.
^{tt6}$: ^{tt11}$ allows you to write ^{tt12}$ instead of ^{tt13}$. When you apply your decorator without passing any args to it you can omit the empty brackets that specify the empty decorator argument list.
^{tt14}$: This argument can be used only when ^{tt15}$ and ^{tt11}$. When the argument list of your decorator is optional and you apply your decorator by passing only a single positional argument to the decorator this library has hard time to decide whether that single positional argument is an optional decorator argument or a decoratable object. This decision is made by the library function ^{tt17}$ function which returns ^{tt18}$ if the given single positional argument is a function, method, or class. This default behavior is good in most of the cases when your decorator receives only simple arguments like integers, strings, bools, etc… However if your decorator can receive a single positional argument that can be a function, method, or class, then the default behavior isn’t suitable. There are several workarounds to this problem, one of them is providing your own ^{tt19}$ implementation through the currently documented decorator argument. You probably have additional info to make an accurate distinction between decorator arguments and decoratable objects to provide a working ^{tt19}$ implementation.
In such pathological edge-cases you can also use the following workarounds besides the previously documented custom ^{tt19}$ implementation:
- When you apply your decorator with only a single argument that is a function/method/class you can pass the argument as a keyword-argument. This way it will be detected as a decorator argument for sure. This is however just a dirty hack that still leaves chance for the users of your decorator to make an error. This can result in long debugging sessions.
- You can make your optional arguments keyword-only with ^{tt22}$. This completely eliminates the problem.
- Don’t make the argument list of this decorator optional. With a required decorator argument list this problem isn’t present.
Helpers: convenience API
便利api提供了一组decorator,它们只是围绕main的“包装器” decorator_args.decorator_args装饰器。这些方便的装饰器只是绑定了一些主装饰器 一些常量的参数。
decorator_args.可选的_decorator_args(*,keyword_only=false,is_decorated_object=none)
Works just like the main ^{tt5}$ decorator with ^{tt11}$.
decorator_args.keyword_only_decorator_args(*,optional=false)
Works just like the main ^{tt5}$ decorator with ^{tt22}$.
decorator_args.可选_keyword_only_decorator_args(
Works just like the main ^{tt5}$ decorator with ^{tt11}$ and ^{tt22}$.
Implementing your decorators in a “twisted” way
库的复杂实现确保了此库提供的装饰器可以应用于 甚至在一些异国情调的情况下,装饰师:
When your decorator is a bound instance/class/static method
classAnyClass(object):@decorator_argsdefdecorator_when_bound(self,decoratable,arg1,arg2):...# It is important to apply @decorator_args after @classmethod!@decorator_args@classmethoddefdecorator_when_bound_2(cls,decoratable,arg1,arg2):...# It is important to apply @decorator_args after @statimethod!@decorator_args@staticmethoddefdecorator_when_bound_3(decoratable,arg1,arg2):...any_class_instance=AnyClass()decorator_with_args=any_class_instance.decorator_when_bounddecorator_with_args_2=AnyClass.decorator_when_bound_2decorator_with_args_3a=any_class_instance.decorator_when_bound_3decorator_with_args_3b=AnyClass.decorator_when_bound_3
When your decorator is a bound __call__ magic (instance)method
classAnyClass(object):@decorator_argsdef__call__(self,decoratable,arg1,arg2):...# Because of the syntactic sugar provided by python it is as simple as:decorator_with_args=AnyClass()