获取使用一些常用参数名称(假定表示相同意思)的函数的集合,并创建一个包含这些函数但固定有一些关键参数值的对象的“最佳”方法是什么?其默认值已修复。
如果我要使用一组功能来处理由一组属性定义的某些特定数据,我通常会使用一个类,提供必须在__init__
中设置的属性。
但是有时候从函数开始更有意义,或者因为使用别人的代码而没有选择的余地。但是,您希望方便地修复某些参数的值,并且在操作时不必重复指定这些值,这很无聊且容易出错。这是DRY的一种形式。
如果您具有一个功能,则只需使用functools.partial
。但是,如果您有大量的函数,那么执行此操作的好方法是什么。
下面是一个如何使用一个参数执行操作的示例:
import inspect
from functools import partial
def mk_func_uses_arg_filt(argname):
def func_uses_arg(obj):
if callable(obj):
try:
if argname in inspect.signature(obj).parameters:
return True
except ValueError: # some functions don't have signatures (!?!)
pass
return False
return func_uses_arg
class FixedArgFuncs(object):
def __init__(self, argname, argval, funcs, only_if_func_uses_arg=True):
func_uses_arg = mk_func_uses_arg_filt(argname)
for func in funcs:
if func_uses_arg(func):
setattr(self, func.__name__, partial(func, **{argname: argval}))
elif not only_if_func_uses_arg:
setattr(self, func.__name__, func)
这是一个使用所有带有“ path”参数(我们将修复到本地主文件夹)的os.path函数的示例。
import os.path
faf = FixedArgFuncs(argname='path', argval=os.path.expanduser('~'),
funcs=filter(callable, os.path.__dict__.values()))
assert faf.exists() == True
assert faf.isfile() == False
print(list(faf.__dict__.keys()))
给我
['exists', 'isfile', '_get_sep', 'islink', 'lexists', 'ismount', 'expanduser', 'expandvars', 'normpath', 'abspath', '_joinrealpath', 'relpath']
这并不完全令人满意,因为(1)根据我要固定的参数的位置,我将不得不使用仅关键字的调用,(2)我想要的东西看起来更像是普通的类,其自我具有固定的属性,随后被函数使用,(3)只是一个参数示例。
我猜想,巧妙地使用装饰器和/或描述符可能会带来一些好处。
答案 0 :(得分:0)
这是一个类装饰器的示例,该类装饰器在类方法中搜索所需的args并将其替换为它们的partialmethod版本。我冻结了值为{2的y
arg,以表明它也没有涉及未使用y
的方法。
'''Freeze args in multiple functions wrapped as class methods,
using a class decorator'''
import math
from functools import partialmethod
import inspect
class Calc:
'''An imaginary Calc class with related methods that might share some args
between them'''
def add(self, x, y):
return x + y
def sub(self, x, y):
return x - y
def sqrt(self, x):
return math.sqrt(x)
def partial_cls_arg_pairs(cls, arg_pairs):
'''A class decorator to freeze arguments in class methods given
as an arg_pairs iterable of argnames with argvalues'''
cls_attrs = dict(cls.__dict__)
freezed_cls_attrs = dict()
for name, value in cls_attrs.items():
if inspect.isfunction(value):
for argname, argvalue in arg_pairs:
if argname in inspect.signature(value).parameters:
print('Freezing args in {}.'.format(name))
value = partialmethod(value, **{argname:argvalue})
freezed_cls_attrs[name] = value
return type(cls.__name__, (object,), freezed_cls_attrs)
c1 = Calc()
print(c1.add(1,2))
print(c1.sub(3,2))
print(c1.sqrt(2))
print()
CalcY2 = partial_cls_arg_pairs(Calc, [('y', 2)])
c2 = CalcY2()
print(c2.add(1))
print(c2.sub(3))
print(c2.sqrt(2))
输出:
3
1
1.4142135623730951
Freezing args in add.
Freezing args in sub.
3
1
1.4142135623730951