Как получить имена атрибутов объекта, созданных через __init__ в Python
Что проверяют
Понимаете ли вы, где Python хранит атрибуты экземпляра и чем они отличаются от атрибутов класса. Следом обычно спрашивают про dir(), про классы со __slots__ и про то, как отличить атрибуты из __init__ от добавленных позже.
Как отвечать
Атрибуты, присвоенные через self.x = ..., лежат в словаре экземпляра:
vars(obj): возвращаетobj.__dict__, имена этоlist(vars(obj)). Порядок совпадает с порядком присваивания, словари упорядочены начиная с Python 3.7;obj.__dict__: тот же объект, а не копия. Изменение словаря меняет сам объект;dir(obj): все доступные имена, включая методы, атрибуты класса и унаследованные dunder-методы, в алфавитном порядке. Для этой задачи слишком широко;inspect.getmembers(obj): пары из имени и значения, удобно с фильтром по предикату.
Что стоит проговорить дальше:
- атрибуты класса:
x = 1в теле класса вvars(obj)не попадает, пока экземпляру не присвоят своё значение; property: тоже живёт в классе, в__dict__экземпляра её нет;__slots__: у таких объектов нет__dict__, иvars(obj)броситTypeError. Имена собирают из__slots__по всей цепочкеtype(obj).__mro__;- приватные имена:
self.__tokenокажется в словаре как_MyClass__tokenиз-за name mangling; - dataclass и Pydantic: для них есть штатные средства,
dataclasses.fields(obj)иModel.model_fieldsво второй версии Pydantic.
Строго «созданные в __init__» Python не отслеживает: в __dict__ лежит всё, что присвоили экземпляру к этому моменту. Если различие важно, берут vars() у только что созданного объекта или объявляют атрибуты явно через __slots__ или dataclass.
Где спотыкаются
Отвечают dir(obj) и получают полсотни имён вместе с __class__ и методами. На вопрос это не отвечает.
Не знают про __slots__. Код с vars() падает на первом же классе, где память экономили слотами.
Считают, что vars() вернёт копию, и удаляют из результата «лишние» ключи. Вместе с ключами из объекта исчезают сами атрибуты.