Вопрос: Есть ли лучший способ сделать это, или сама идея неверна
У меня есть класс обработки, который создает что-то с несколькими этапами построения, так что следующая функция зависит от предыдущих.
Я хочу, чтобы зависимости были указаны, как в makefile
, и, если зависимость не существует, создайте ее.
В настоящее время я использую декоратор для достижения этой цели, но он не кажется питоническим.
Пожалуйста, взгляните
from typing import *
from functools import wraps
def step_method(dependency: Optional[dict[str, Callable]] = None):
if dependency is None:
dependency = {}
def decorator(method):
@wraps(method)
def wrapper(self, *args, **kwargs):
for attr, func in dependency.items():
if not getattr(self, attr):
func(self)
ret = method(self, *args, **kwargs)
return ret
return wrapper
return decorator
class StepClass:
def __init__(self, base_val:int):
self.base_val: int = base_val
self.a = None
self.b = []
self.c = None
self.d = []
@step_method({})
def gen_a(self):
self.a = self.base_val * 2
@step_method({'a': gen_a})
def create_b(self):
self.b = [self.a] * 3
@step_method({
'a': gen_a,
'b': create_b
})
def gen_c(self):
self.c = sum(self.b) * self.a
@step_method({'c': gen_c})
def generate_d(self):
self.d = list(range(self.c))
sc = StepClass(10)
sc.base_val = 7 # allow changes before generating starts
sc.b = [1, 2, 3] # allow dependency value injection
sc.generate_d()
print(sc.a, sc.b, sc.c, sc.d, sep='\n')
Мне также интересно, можно ли автоматически обнаруживать использование переменных и генерировать их с помощью заранее заданной функции, если они еще не существуют.
Это хороший пример использования свойств , с помощью которых вы можете генерировать значения по требованию, поэтому нет необходимости строить дерево зависимостей.
По соглашению сгенерированные значения кэшируются в атрибутах имен с префиксом подчеркивания. Поскольку все ваши методы получения и установки будут иметь доступ к атрибутам единым способом, вы можете программно создать метод получения и установки для каждой заданной функции расчета при инициализации подкласса property
.
class SettableCachedProperty(property):
def __init__(self, func):
def fget(instance):
if (value := getattr(instance, name, None)) is None:
setattr(self, name, value := func(instance))
return value
def fset(instance, value):
setattr(instance, name, value)
name = '_' + func.__name__
super().__init__(fget, fset)
class StepClass:
def __init__(self, base_val):
self.base_val = base_val
@SettableCachedProperty
def a(self):
return self.base_val * 2
@SettableCachedProperty
def b(self):
return [self.a] * 3
@SettableCachedProperty
def c(self):
return sum(self.b) * self.a
так что (опуская d
в вашем примере для краткости):
sc = StepClass(10)
sc.base_val = 7 # allow changes before generating starts
sc.b = [1, 2, 3] # allow dependency value injection
print(sc.a, sc.b, sc.c, sep='\n')
выходы:
14
[1, 2, 3]
84
Демо здесь
Пожалуйста. И спасибо за попытку улучшить ответ, но нет необходимости в выделенном WeakKeyDictionary
в качестве кеша, поскольку значения кэшируются как атрибуты экземпляра, которые уже специфичны для экземпляра. Я обновил ответ, переименовав self
вложенные функции, чтобы избежать путаницы с внешней функцией.
Это намного чище исходного кода! большое спасибо