Ответ 1
Я думаю, что процедурная парадигма сужает ваше видение этой проблемы. Вот некоторые решения, которые я нашел, используя другие функции 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__
member:
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:
# Default arguments doesn't allow gaps, ignore rest
break
defaults.insert(0, defltargs[func_arg])
# Update list of default arguments
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
decorator.
Вы также можете добавить аргументы во время выполнения, используя оболочку, но это, вероятно, снизит производительность.