Python: перебор зависимых классов

from dataclasses import dataclass

@dataclass
class BronzeA:
    name = "bronze_a"
    quality = "bronze"
    dependencies = None

@dataclass
class BronzeAA:
    name = "bronze_aa"
    quality = "bronze"
    dependencies = None

@dataclass
class SilverAA:
    name = "silver_aa"
    quality = "silver"
    dependencies = [BronzeAA]

@dataclass
class SilverA:
    name = "silver_a"
    quality = "silver"
    dependencies = [BronzeA, SilverAA]

@dataclass
class BronzeB:
    name = "bronze_b"
    quality = "bronze"
    dependencies = None

@dataclass
class SilverB:
    name = "silver_b"
    quality = "silver"
    dependencies = [BronzeB, SilverAA]

@dataclass
class Gold:
    name = "gold_data"
    quality = "gold"
    dependencies = [SilverA, SilverB]

Я хочу выполнить итерацию по зависимостям моих классов данных и получить словарь всех имен зависимых классов. Я попытался сделать следующее:

my_gold = Gold()
dependants = {}
for d in my_gold.dependencies:
    dependants[d.name] = d.__name__
    if d.quality != 'bronze':
        for dd in d.dependencies:
            dependants[dd.name] = dd.__name__
            if dd.quality != 'bronze':
                for ddd in dd.dependencies:
                    dependants[ddd.name] = ddd.__name__

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

Ожидаемый результат:

{'silver_a': 'SilverA',
 'bronze_a': 'BronzeA',
 'silver_aa': 'SilverAA',
 'bronze_aa': 'BronzeAA',
 'silver_b': 'SilverB',
 'bronze_b': 'BronzeB'}

Возможна ли наследственность? Например, class Gold(SilverA, SilverB), тогда Gold.mro() даст вам список всех классов, от которых он наследуется прямо или косвенно.

chepner 10.02.2023 15:18
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Проблема 1. Не используется декоратор класса данных.

Не украшая свои классы декоратором класса данных, вы не получаете функциональности класса данных, который предоставляет python.

Дополнительные сведения о классах данных см. в следующих ресурсах:


Проблема 2 — Сбор зависимостей

Вы можете думать об этой задаче как о графике: Изображение графика

Что мы хотим сделать, так это пройти по этому графу от заданного узла, в данном случае класса данных Gold. Для этого мы можем выполнить обход в глубину по графу, используя структуру данных стека. В python список можно использовать как стек с помощью метода pop.

Важно отметить, что это будет работать только в том случае, если нет циклических зависимостей. Например, если бы BronzeAA имел SilverB в качестве зависимости, это привело бы к петле (циклу) в графе, и мы, по сути, вечно пересекали бы граф с данным решением ниже.


Решение

from dataclasses import dataclass


# Tag the classes with @dataclass to give them the superpowers of dataclasses
# To learn more see: https://docs.python.org/3/library/dataclasses.html
#                    https://www.dataquest.io/blog/how-to-use-python-data-classes/

@dataclass
class BronzeA:
    name = "bronze_a"
    quality = "bronze"
    dependencies = None


@dataclass
class BronzeAA:
    name = "bronze_aa"
    quality = "bronze"
    dependencies = None


@dataclass
class SilverAA:
    name = "silver_aa"
    quality = "silver"
    dependencies = [BronzeAA]


@dataclass
class SilverA:
    name = "silver_a"
    quality = "silver"
    dependencies = [BronzeA, SilverAA]


@dataclass
class BronzeB:
    name = "bronze_b"
    quality = "bronze"
    dependencies = None


@dataclass
class SilverB:
    name = "silver_b"
    quality = "silver"
    dependencies = [BronzeB, SilverAA]


@dataclass
class Gold:
    name = "gold_data"
    quality = "gold"
    dependencies = [SilverA, SilverB]


my_gold = Gold()
dependants = {}

# using a stack will allow us to iterate through the dependencies in a depth-first manner while collecting the dependants
# more about stacks, see: https://www.geeksforgeeks.org/stack-in-python/
dependencies = my_gold.dependencies
dependency = dependencies.pop(0)

# loop while there are still dependencies to iterate through
while len(dependencies) > 0:
    # add the dependency to the dictionary
    dependants[dependency.name] = dependency.__name__

    # if there are dependencies, add them to the list
    if dependency.dependencies is not None:
        # the extend function will extend the list with the items from another list
        # e.g.: [1, 2, 3].extend([4, 5, 6]) -> [1, 2, 3, 4, 5, 6]
        dependencies.extend(dependency.dependencies)

    # remove, and return, the last item from the list, thereby iterating to the next dependency
    dependency = dependencies.pop(0)

print(dependants)

Спасибо за ответ. У меня есть декоратор класса данных, я забыл добавить его в свой вопрос. Кроме того, чтобы ответить на ваш вопрос: нет, циклических зависимостей нет. Я не получаю правильный вывод, используя ваш цикл: {'silver_b': 'SilverB', 'silver_aa': 'SilverAA', 'bronze_aa': 'BronzeAA', 'bronze_b': 'BronzeB'} Отредактировал мой исходный вопрос с помощью ожидаемый результат

Grizzly2501 10.02.2023 15:30

если я начинаю цикл с: while dependency.quality is not None я получаю правильный результат. Но я не могу понять, как не выскочить из пустого списка в конце из-за этого

Grizzly2501 10.02.2023 15:56

Спасибо за уточнение ожидаемого вывода :) Я изменил код, чтобы он выскакивал из начала списка, а не из конца, по сути, это перевернутый стек. Это дает ожидаемый результат, хотя и не в том же порядке, однако, поскольку это словарь, порядок не должен иметь значения.

Dylan Randall 10.02.2023 16:56

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