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'}
Не украшая свои классы декоратором класса данных, вы не получаете функциональности класса данных, который предоставляет python.
Дополнительные сведения о классах данных см. в следующих ресурсах:
Вы можете думать об этой задаче как о графике: Изображение графика
Что мы хотим сделать, так это пройти по этому графу от заданного узла, в данном случае класса данных 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'} Отредактировал мой исходный вопрос с помощью ожидаемый результат
если я начинаю цикл с: while dependency.quality is not None
я получаю правильный результат. Но я не могу понять, как не выскочить из пустого списка в конце из-за этого
Спасибо за уточнение ожидаемого вывода :) Я изменил код, чтобы он выскакивал из начала списка, а не из конца, по сути, это перевернутый стек. Это дает ожидаемый результат, хотя и не в том же порядке, однако, поскольку это словарь, порядок не должен иметь значения.
Возможна ли наследственность? Например,
class Gold(SilverA, SilverB)
, тогдаGold.mro()
даст вам список всех классов, от которых он наследуется прямо или косвенно.