下面的功能可以完成(似乎可以完成)工作,但是似乎需要的锅炉板要多得多。
我敢肯定还有更优雅的方法。可以将其中的某些代码排除在外的代码,使它看起来不像现在的复制/粘贴/编辑补丁工作。
不过请注意,优雅并不是全部:我不希望性能受到影响。例如,我可以通过做两个装饰器将代码切成两半:一个用于输入转换,另一个用于输出转换。但这会比当前版本的效率低。
pip --version
一些使用示例:
def input_output_decorator(preprocess=None, postprocess=None):
def decorator(func):
if inspect.ismethod(func):
if preprocess is not None:
if postprocess is not None: # both pre and post processes
@wraps(func)
def func_wrapper(self, *args, **kwargs):
return postprocess(func(self, preprocess(*args, **kwargs)))
else: # a preprocess but no postprocess
@wraps(func)
def func_wrapper(self, *args, **kwargs):
return func(self, preprocess(*args, **kwargs))
else: # no preprocess
if postprocess is not None: # no preprocess, but a postprocess
@wraps(func)
def func_wrapper(self, *args, **kwargs):
return postprocess(func(*args, **kwargs))
else: # no pre or post process at all
func_wrapper = func
return func_wrapper
else:
if preprocess is not None:
if postprocess is not None: # both pre and post processes
@wraps(func)
def func_wrapper(*args, **kwargs):
return postprocess(func(preprocess(*args, **kwargs)))
else: # a preprocess but no postprocess
@wraps(func)
def func_wrapper(*args, **kwargs):
return func(preprocess(*args, **kwargs))
else: # no preprocess
if postprocess is not None: # no preprocess, but a postprocess
@wraps(func)
def func_wrapper(*args, **kwargs):
return postprocess(func(*args, **kwargs))
else: # no pre or post process at all
func_wrapper = func
return func_wrapper
return decorator
基于@ micky-loo(接受)的答案,并受到@a_guest答案的启发,我的最终实现是:
>>> # Examples with "normal functions"
>>> def f(x=3):
... '''Some doc...'''
... return x + 10
>>> ff = input_output_decorator()(f)
>>> print((ff(5.0)))
15.0
>>> ff = input_output_decorator(preprocess=int)(f)
>>> print((ff(5.0)))
15
>>> ff = input_output_decorator(preprocess=int, postprocess=lambda x: "Hello {}!".format(x))(f)
>>> print((ff('5')))
Hello 15!
>>> ff = input_output_decorator(postprocess=lambda x: "Hello {}!".format(x))(f)
>>> print((ff(5.0)))
Hello 15.0!
>>> print((ff.__doc__))
Some doc...
>>>
>>> # examples with methods (bounded, class methods, static methods
>>> class F:
... '''This is not what you'd expect: The doc of the class, not the function'''
... def __init__(self, y=10):
... '''Initialize'''
... self.y = y
... def __call__(self, x=3):
... '''Some doc...'''
... return self.y + x
... @staticmethod
... def static_method(x, y):
... return "What {} {} you have".format(x, y)
... @classmethod
... def class_method(cls, x):
... return "{} likes {}".format(cls.__name__, x)
>>>
>>> f = F()
>>> ff = input_output_decorator()(f)
>>> print((ff(5.0)))
15.0
>>> ff = input_output_decorator(preprocess=int)(f)
>>> print((ff(5.0)))
15
>>> ff = input_output_decorator(preprocess=int, postprocess=lambda x: "Hello {}!".format(x))(f)
>>> print((ff('5')))
Hello 15!
>>> ff = input_output_decorator(postprocess=lambda x: "Hello {}!".format(x))(f)
>>> print((ff(5.0)))
Hello 15.0!
>>> print((ff.__doc__))
This is not what you'd expect: The doc of the class, not the function
答案 0 :(得分:1)
使用两个不同的装饰器可以实现更紧凑的实现。的确,在进行预处理后,您还将在链中再进行一次函数调用,但这几乎不会影响性能。
import functools
import inspect
def preprocess(pre):
def decorator(func):
if inspect.ismethod(func):
def wrapper(self, *args, **kwargs):
return func(self, pre(*args, **kwargs))
else:
def wrapper(*args, **kwargs):
return func(pre(*args, **kwargs))
return functools.wraps(func)(wrapper)
return decorator
def postprocess(post):
def decorator(func):
def wrapper(*args, **kwargs):
return post(func(*args, **kwargs))
return functools.wraps(func)(wrapper)
return decorator
答案 1 :(得分:0)
您不需要进行检查检查。可以拼合if / else嵌套,以使代码更具可读性。
from functools import wraps
def input_output_decorator(preprocess=None, postprocess=None):
def decorator(func):
if preprocess and postprocess:
@wraps(func)
def func_wrapper(*args, **kwargs):
return postprocess(func(preprocess(*args, **kwargs)))
elif preprocess:
@wraps(func)
def func_wrapper(*args, **kwargs):
return func(preprocess(*args, **kwargs))
elif postprocess:
@wraps(func)
def func_wrapper(*args, **kwargs):
return postprocess(func(*args, **kwargs))
else:
func_wrapper = func
return func_wrapper
return decorator