Класс Python имитирует зависимость make-файла

Вопрос: Есть ли лучший способ сделать это, или сама идея неверна

У меня есть класс обработки, который создает что-то с несколькими этапами построения, так что следующая функция зависит от предыдущих.

Я хочу, чтобы зависимости были указаны, как в 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')

Мне также интересно, можно ли автоматически обнаруживать использование переменных и генерировать их с помощью заранее заданной функции, если они еще не существуют.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
0
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это хороший пример использования свойств , с помощью которых вы можете генерировать значения по требованию, поэтому нет необходимости строить дерево зависимостей.

По соглашению сгенерированные значения кэшируются в атрибутах имен с префиксом подчеркивания. Поскольку все ваши методы получения и установки будут иметь доступ к атрибутам единым способом, вы можете программно создать метод получения и установки для каждой заданной функции расчета при инициализации подкласса 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

Демо здесь

Это намного чище исходного кода! большое спасибо

Electron X 16.08.2024 08:48

Пожалуйста. И спасибо за попытку улучшить ответ, но нет необходимости в выделенном WeakKeyDictionary в качестве кеша, поскольку значения кэшируются как атрибуты экземпляра, которые уже специфичны для экземпляра. Я обновил ответ, переименовав self вложенные функции, чтобы избежать путаницы с внешней функцией.

blhsing 16.08.2024 09:24

Другие вопросы по теме