Каков «лучший» метод для сбора набора функций, использующих некоторые общие имена аргументов (предполагается, что они означают одно и то же), и создания объекта, который содержит эти функции, но с фиксированными значениями некоторых ключевых аргументов или, по крайней мере,их значения по умолчанию исправлены.
Если у меня будет набор функций, предназначенных для работы с некоторыми конкретными данными, определенными набором атрибутов, я обычно использую класс, предоставляющий атрибуты, которые должны быть установлены в __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-функции, которые имеют аргумент «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) я бы хотел что-то, что больше похоже на обычный класс, с атрибутом self, имеющим фиксированные атрибуты,впоследствии используется функциями, (3) это только один аргумент в качестве примера.
Я предполагаю, что умное использование декораторов и / или дескрипторов может сделать что-то хорошее.