我认为过程化编程范式会限制你对问题的视角。以下是我使用其他Python特性发现的一些解决方案。
面向对象编程
你正在用相同的参数子集调用f()
和g()
,这是参数表示相同实体的好提示。为什么不将其封装成一个对象呢?
class FG:
def __init__(self, accuracy=1e-3, nstep=10):
self.accuracy = accuracy
self.nstep = nstep
def f(self):
print ('f', self.accuracy, self.nstep)
def g(self):
self.f()
print ('g', self.accuracy, self.nstep)
FG().f()
FG(1e-5).g()
FG(nstep=20).g()
函数式编程
您可以将f()
转换为高阶函数,例如:
from functools import partial
def g(accuracy, nstep):
print ('g', accuracy, nstep)
def f(accuracy=1e-3, nstep=10):
g(accuracy, nstep)
print ('f', accuracy, nstep)
def fg(func, accuracy=1e-3, nstep=10):
return partial(func, accuracy=accuracy, nstep=nstep)
fg(g)()
fg(f, 2e-5)()
fg(f, nstep=32)()
但这也是一个棘手的方法--在这里f()
和g()
调用被交换了。可能有更好的方法来做到这一点--比如带回调函数的管道,我对FP不是很擅长 :(
动态性和内省
这是一个更复杂的方法,它需要深入研究CPython内部,但由于CPython允许这样做,为什么不使用它呢?
下面是一个修饰器,通过__defaults__
成员更新默认值:
class use_defaults:
def __init__(self, deflt_func):
self.deflt_func = deflt_func
def __call__(self, func):
defltargs = dict(zip(getargspec(self.deflt_func).args,
getargspec(self.deflt_func).defaults))
defaults = (list(func.__defaults__)
if func.__defaults__ is not None
else [])
func_args = reversed(getargspec(func).args[:-len(defaults)])
for func_arg in func_args:
if func_arg not in defltargs:
break
defaults.insert(0, defltargs[func_arg])
func.__defaults__ = tuple(defaults)
return func
def f(accuracy=1e-3, nstep=10, b = 'bbb'):
print ('f', accuracy, nstep, b)
@use_defaults(f)
def g(first, accuracy, nstep, a = 'aaa'):
f(accuracy, nstep)
print ('g', first, accuracy, nstep, a)
g(True)
g(False, 2e-5)
g(True, nstep=32)
然而,这排除了具有单独的__kwdefaults__
的仅关键字参数,并且可能会破坏use_defaults
装饰器背后的逻辑。
您也可以通过使用包装器在运行时添加参数,但这可能会降低性能。