Как создать список объектов, чтобы атрибуты и методы объекта подсказывались в IDE

Возникла потребность создать список классов на Python. класс используется для хранения данных и должен обладать своими методами, которые будут манипулировать с полями класса.

class B:
    def __init__(self, a:str, b:str):
        self.a = a
        self.b = b

    def foo(self):
        pass


class A:
    def __init__(self):
        self.data_1: str
        self.data_2: str
        self.listB: list[B]

    def foo(self):
        pass


a = A()

b = B("ddd", "rrr")


a.listB.append(b)

при вызове append возникает ошибка: AttributeError: 'A' object has no attribute 'listB'

Не могу понять, что не так. Видимо я слишком засел на C++ и такие действия там проходят на ура. Я что-то делаю не так, но не могу понять, что именно.

Правлено:

После исправлений:

class B:
    def __init__(self, a:str, b:str):
        self.a = a
        self.b = b

    def foo(self):
        pass

class A:
    def __init__(self):
        self.data_1: str
        self.data_2: str
        self.listB = []

    def foo(self):
        pass


a = A()

b = B("ddd", "rrr")

a.listB.append(b)

vala = a.listB[0].a

В строке vala = a.listB[0].a не выпадает а после точки, как-будто нет поля класса B. Хотя все отрабатывает


Ответы (1 шт):

Автор решения: insolor

Поля с аннотациями должны быть описаны на уровне класса, а не в методе __init__, тогда IDE будет их видеть. В __init__ нужно делать инициализацию, например присваивание начального значения полю listB.

Пример кода:

class B:
    a: str
    b: str

    def __init__(self, a: str, b: str):
        self.a = a
        self.b = b

    def foo(self):
        pass


class A:
    data_1: str
    data_2: str
    listB: list[B]

    def __init__(self):
        self.listB = []

    def foo(self):
        pass


a = A()
b = B("ddd", "rrr")
a.listB.append(b)

Можно переписать через датаклассы, тогда не нужно будет вручную описывать __init__, в том числе не перекладывать вручную параметры __init__ в одноименные атрибуты, и можно будет прямо при объявлении описать дефолтные значения:

from dataclasses import dataclass, field


@dataclass
class B:
    a: str
    b: str

    def foo(self):
        pass


@dataclass
class A:
    data_1: str | None = None  # Неизменяемые дефолтные значения можно прописать напрямую
    data_2: str | None = None
    listB: list[B] = field(default_factory=list)  # Изменяемые нужно описывать через default_factory, иначе список будет общим для всех экземпляров класса

    def foo(self):
        pass


a = A()
b = B("ddd", "rrr")
a.listB.append(b)
→ Ссылка