Python, как получить доступ к свойствам класса данных в списке классов данных

Использование питона 3.10.4

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

Пример ямла:

account: 12345
clusters:
  - name: cluster_1
    endpoint: https://cluster_2
    certificate: abcdef
  - name: cluster_1
    endpoint: https://cluster_2
    certificate: abcdef

А вот мой скрипт для загрузки и доступа к нему:

import yaml
from dataclasses import dataclass

@dataclass
class ClusterInfo:
    _name: str
    _endpoint: str
    _certificate: str

@dataclass
class AWSInfo:
    _account: int
    _clusters: list[ClusterInfo]


clusters = yaml.safe_load(open('D:\git\clusters.yml', 'r'))
a = AWSInfo(
  _account=clusters['account'],
  _clusters=clusters['clusters']
)
print(a._account) #prints 12345
print(a._clusters) #prints the dict of both clusters
print(a._clusters[0]) #prints the dict of the first cluster

#These prints fails with AttributeError: 'dict' object has no attribute '_endpoint'
print(a._clusters[0]._endpoint)
for c in a._clusters:
    print(c._endpoint)

Итак, мой вопрос: что я делаю неправильно на последних отпечатках? Как я могу получить доступ к свойствам каждого члена в массиве объектов класса данных?

a._clusters — это список словарей, а не список ClusterInfo объектов.
Barmar 30.03.2023 03:29

Вам нужно преобразовать clusters['clusters'] в список ClusterInfo, когда вы звоните AWSInfo().

Barmar 30.03.2023 03:30

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

juanpa.arrivillaga 30.03.2023 03:55

Кроме того, почему перед каждым атрибутом стоит один символ подчеркивания??

juanpa.arrivillaga 30.03.2023 03:55

@juanpa.arrivillaga Я видел это в другом примере класса данных, и я полагаю, что это сделано для предотвращения случайного присвоения имени свойству встроенного свойства.

user3066571 30.03.2023 18:14

Нет! Не делай этого.

juanpa.arrivillaga 30.03.2023 21:05
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
6
79
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Как отмечает Бармар в комментарии, даже если вы правильно ввели ключ _clusters в свой класс данных AWSInfo...

@dataclass
class AWSInfo:
    _account: int
    _clusters: list[ClusterInfo]

... модуль dataclasses недостаточно умен, чтобы автоматически преобразовывать элементы списка clusters во входных данных в соответствующий тип данных. Если вы используете более полную библиотеку моделей данных, такую ​​как Pydantic, все будет работать так, как вы ожидаете:

import yaml
from pydantic import BaseModel

class ClusterInfo(BaseModel):
    name: str
    endpoint: str
    certificate: str

class AWSInfo(BaseModel):
    account: int
    clusters: list[ClusterInfo]


with open('clusters.yml', 'r') as fd:
    clusters = yaml.safe_load(fd)
a = AWSInfo(**clusters)

print(a.account) #prints 12345
print(a.clusters) #prints the dict of both clusters
print(a.clusters[0]) #prints the dict of the first cluster

#These prints fails with AttributeError: 'dict' object has no attribute '_endpoint'
print(a.clusters[0].endpoint)
for c in a.clusters:
    print(c.endpoint)

Запуск приведенного выше кода (с вашим образцом ввода) дает:

12345
[ClusterInfo(name='cluster_1', endpoint='https://cluster_2', certificate='abcdef'), ClusterInfo(name='cluster_1', endpoint='https://cluster_2', certificate='abcdef')]
name='cluster_1' endpoint='https://cluster_2' certificate='abcdef'
https://cluster_2
https://cluster_2
https://cluster_2

«недостаточно умен» -> «выходит за рамки»

juanpa.arrivillaga 30.03.2023 03:54

Я думаю, что такое поведение нарушает «принцип наименьшего удивления», поэтому нам просто придется расходиться в этом вопросе.

larsks 30.03.2023 03:57

@larsks спасибо, что показали мне, что я сделал не так. Я изменил код, и он работал в соответствии с вашим предложением, но другой ответ о мастерах данных я использовал только потому, что он был более естественным образом создан для того, что я ищу. Спасибо за помощь!

user3066571 30.03.2023 18:24
Ответ принят как подходящий

Модуль dataclasses не предоставляет встроенной поддержки для этого варианта использования, то есть для загрузки данных YAML во вложенную модель класса.

В таком случае я бы обратился к библиотеке ser/de, такой как dataclass-wizard , которая обеспечивает поддержку OOTB для (де)сериализации данных YAML через библиотеку PyYAML.

Отказ от ответственности: я являюсь создателем и хранителем этой библиотеки.

Шаг 1: Создайте модель класса данных

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

На данный момент проще всего сделать это в самом Python:

from json import dumps

# pip install PyYAML dataclass-wizard
from yaml import safe_load
from dataclass_wizard.wizard_cli import PyCodeGenerator

yaml_string = """
account: 12345
clusters:
  - name: cluster_1
    endpoint: https://cluster_2
    certificate: abcdef
  - name: cluster_1
    endpoint: https://cluster_2
    certificate: abcdef
"""

py_code = PyCodeGenerator(experimental=True, file_contents=dumps(safe_load(yaml_string))).py_code
print(py_code)

Отпечатки:

from __future__ import annotations

from dataclasses import dataclass

from dataclass_wizard import JSONWizard


@dataclass
class Data(JSONWizard):
    """
    Data dataclass

    """
    account: int
    clusters: list[Cluster]


@dataclass
class Cluster:
    """
    Cluster dataclass

    """
    name: str
    endpoint: str
    certificate: str

Шаг 2: Используйте сгенерированную модель класса данных вместе с YAMLWizard

Содержимое my_file.yml:

account: 12345
clusters:
  - name: cluster_1
    endpoint: https://cluster_5
    certificate: abcdef
  - name: cluster_2
    endpoint: https://cluster_7
    certificate: xyz

Код Python:

from __future__ import annotations

from dataclasses import dataclass
from pprint import pprint

from dataclass_wizard import YAMLWizard


@dataclass
class Data(YAMLWizard):
    account: int
    clusters: list[Cluster]


@dataclass
class Cluster:
    name: str
    endpoint: str
    certificate: str


data = Data.from_yaml_file('./my_file.yml')
pprint(data)
for c in data.clusters:
    print(c.endpoint)

Результат:

Data(account=12345,
     clusters=[Cluster(name='cluster_1',
                       endpoint='https://cluster_5',
                       certificate='abcdef'),
               Cluster(name='cluster_2',
                       endpoint='https://cluster_7',
                       certificate='xyz')])
https://cluster_5
https://cluster_7

Спасибо за это, это было идеальное решение!

user3066571 30.03.2023 18:24

не беспокойся! рад, что это было полезно в вашем случае использования.

rv.kvetch 30.03.2023 19:36

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