Vla*_*mir 6 python oop arguments numpy python-decorators
我正在编写一个类,其中有许多方法在类似的参数类型上运行:
class TheClass():
def func1(self, data, params, interval):
....
def func2(self, data, params):
....
def func3(self, data, interval):
....
def func4(self, params):
....
...
Run Code Online (Sandbox Code Playgroud)
关于这些参数有一定的约定(例如data/ params应该是numpy.farrays,interval- a list的2 floats),但是我想允许用户有更多的自由:例如,函数应该接受int为data或者params,或者只接受int一个interval,然后假设这是有起点的终点0等
所以为了避免方法中的所有这些转换,应该只做逻辑,我使用这样的装饰器:
def convertparameters(*types):
def wrapper(func):
def new_func(self, *args, **kwargs):
# Check if we got enough parameters
if len(types) > len(args):
raise Exception('Not enough parameters')
# Convert parameters
new_args = list(args)
for ind, tip in enumerate(types):
if tip == "data":
new_args[ind] = _convert_data(new_args[ind])
elif tip == "params":
new_args[ind] = _convert_params(new_args[ind])
elif tip == "interval":
new_args[ind] = _convert_interval(new_args[ind])
else:
raise Exception('Unknown type for parameter')
return func(self, *new_args, **kwargs)
return new_func
return wrapper
Run Code Online (Sandbox Code Playgroud)
其中_convert_data,_convert_params和_convert_interval做肮脏的工作.然后我按如下方式定义类:
class TheClass():
@convertparameters("data", "params", "interval")
def func1(self, data, params, interval):
....
@convertparameters("data", "params")
def func2(self, data, params):
....
@convertparameters("data", "interval")
def func3(self, data, interval):
....
@convertparameters("params")
def func4(self, params):
....
...
Run Code Online (Sandbox Code Playgroud)
它可以解决问题,但有几个非常令人不安的事情:
@staticmethod或者该方法的后处理输出的东西)组合在一起,这些装饰器的排序很重要func1(*args, **kwargs)是否有更好的(或更多"Pythonic")方法进行如此大规模的参数转换?
更新1:基于n9code建议的解决方案
为了避免混淆,有一个convertparameters包装器的修改解决了第三个问题(掩盖签名和方法的文档字符串) - 由n9code for Python> 2.5 建议.
使用decorator模块(单独安装:) pip install decorator我们可以同时传输所有函数的"元数据"(文档字符串,名称和签名),去掉包装器内部的嵌套结构
from decorator import decorator
def convertparameters(*types):
@decorator
def wrapper(func, self, *args, **kwargs):
# Check if we got enough parameters
if len(types) > len(args):
raise Exception('Not enough parameters')
# Convert parameters
new_args = list(args)
for ind, tip in enumerate(types):
if tip == "data":
new_args[ind] = _convert_data(new_args[ind])
elif tip == "params":
new_args[ind] = _convert_params(new_args[ind])
elif tip == "interval":
new_args[ind] = _convert_interval(new_args[ind])
else:
raise Exception('Unknown type for parameter')
return func(self, *new_args, **kwargs)
return wrapper
Run Code Online (Sandbox Code Playgroud)
更新2:基于zmbq建议的修改后的解决方案
使用inspect模块我们也可以摆脱装饰器的参数,检查初始函数的参数名称.这将消除装饰器的另一层
from decorator import decorator
import inspect
@decorator
def wrapper(func, self, *args, **kwargs):
specs = inspect.getargspec(func)
# Convert parameters
new_args = list(args)
for ind, name in enumerate(specs.args[1:]):
if name == "data":
new_args[ind] = _convert_data(new_args[ind])
elif name == "params":
new_args[ind] = _convert_params(new_args[ind])
elif name == "interval":
new_args[ind] = _convert_interval(new_args[ind])
return func(self, *new_args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
而且使用起来要简单得多.唯一重要的是继续为不同函数之间的参数使用相同的名称.
class TheClass():
@convertparameters
def func1(self, data, params, interval):
....
@convertparameters
def func2(self, data, params):
....
Run Code Online (Sandbox Code Playgroud)
这是一个问题,你是对的,但它很容易解决functools.wraps。只需用newfunc它来装饰你的函数,你就可以保存原始函数的签名。
from functools import wraps
def convertparameters(*types):
def wrapper(func):
@wraps(func)
def new_func(self, *args, **kwargs):
pass # Your stuff
Run Code Online (Sandbox Code Playgroud)
很困难,这仅适用于 Python 3。在 Python 2 中,签名不会被保留,只有__name__并且__doc__会被保留。因此,对于 Python,您可以使用该decorator模块:
from decorator import decorator
def convertparameters(*types):
@decorator
def wrapper(func, self, *args, **kwargs):
pass # return a result
return wrapper
Run Code Online (Sandbox Code Playgroud)根据user3160867 的更新进行编辑。