Список атрибутов объекта: функции dir, vars и другие инструменты Python

Раздел: Объектно-ориентированное программирование -> Объектно-ориентированное программирование

Список атрибутов объекта в Python

Основной способ: функция vars() и атрибут __dict__

Как получить список пользовательских атрибутов экземпляра класса?

Встроенная функция vars() возвращает словарь __dict__ объекта. Этот словарь содержит только те атрибуты, которые были явно присвоены экземпляру через self или напрямую. Для классов vars() возвращает атрибуты самого класса (включая методы).


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person('Alice', 30)
print(vars(p))
# {'name': 'Alice', 'age': 30}

атрибуты класса python (атрибуты классов и объектов в python)

{'name': 'Alice', 'age': 30}

библиотека классов python (библиотека классов в python)

Атрибут __dict__ даёт тот же результат:


print(p.__dict__)
# {'name': 'Alice', 'age': 30}

метод объекта python (методы объектов в python)

Проблемы и ошибки

  • Не все объекты имеют __dict__. Например, экземпляры классов со слотом __slots__ не хранят атрибуты в __dict__. В этом случае vars(obj) вызовет TypeError.
  • Словарь __dict__ не включает унаследованные атрибуты или атрибуты, определённые на уровне класса (если они не переопределены в экземпляре).
  • Для встроенных типов (например, int, str) __dict__ отсутствует.

Когда использовать:

  • Когда нужны только атрибуты, назначенные непосредственно объекту.
  • В сериализации (например, преобразование в JSON) или логировании состояния.

Вариант 1: функция dir() - полный список всех имён

Как получить все имена, доступные через объект, включая унаследованные и специальные методы?

Функция dir() возвращает отсортированный список строк - имён атрибутов и методов объекта. Она полезна для исследования объекта, но содержит много служебных элементов (начинающихся с двойного подчёркивания).


print(dir(p)[:10])
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__']

Python структура объекта (структура объекта в python)

Проблемы и ошибки

  • Список может быть очень большим, особенно для сложных встроенных классов.
  • Не различает атрибуты и методы, не даёт информации о значениях.

Когда использовать:

  • Для быстрой интроспекции объекта в интерактивной среде.
  • При отладке, чтобы увидеть весь доступный интерфейс.

Вариант 2: модуль inspect - гибкий инструмент

Как получить атрибуты с фильтрацией по типу (например, только методы или только свойства)?

Функция inspect.getmembers() возвращает список кортежей (имя, значение) для всех членов объекта, которые удовлетворяют предикату. Это мощный способ для детального анализа.


import inspect

class Circle:
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        return 3.14 * self.radius ** 2

c = Circle(5)
methods = inspect.getmembers(c, predicate=inspect.ismethod)
print(methods)
# [('__init__', ), ('area', )]

Python создание объектов (создание объектов в python)

[('__init__', ), ('area', )]

Self object python (объект self в python)

Проблемы и ошибки

  • Вызов inspect.getmembers() может быть медленным для больших объектов.
  • Предикаты могут случайно вызывать геттеры (если атрибут является свойством).

Когда использовать:

  • Когда нужно отфильтровать только методы, только атрибуты определённого класса или наследуемые члены.
  • При написании декораторов или метапрограммировании.

Вариант 3: обход __dict__ с проверкой типа

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

Можно пройти по __dict__ объекта и отсеять служебные имена. Для экземпляра __dict__ содержит только данные.


class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author
    
    def info(self):
        return f'{self.title} by {self.author}'

b = Book('1984', 'Orwell')
attrs = {k: v for k, v in b.__dict__.items() if not k.startswith('_')}
print(attrs)
# {'title': '1984', 'author': 'Orwell'}

Object attribute python (атрибуты объекта в python)

{'title': '1984', 'author': 'Orwell'}

Python call method (вызов метода в python)

Проблемы и ошибки

  • Не учитывает свойства (property), которые могут быть определены в классе и не отображаются в __dict__ экземпляра.
  • Если класс использует __slots__, __dict__ отсутствует.

Когда использовать:

  • Для сериализации, когда нужно только состояние экземпляра.
  • В ORM или дата-классах для перебора полей.

Вариант 4: использование __annotations__ (аннотации типов)

Как получить атрибуты, объявленные с аннотацией типа?

Атрибут класса __annotations__ содержит словарь имён атрибутов с их типами. Это полезно для классов, где аннотации обязательны (например, дата-классы).


from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

print(Point.__annotations__)
# {'x': float, 'y': float}

Python класс данных (класс данных в python)

{'x': <class 'float'>, 'y': <class 'float'>}

Проблемы и ошибки

  • Доступен только на уровне класса, не для экземпляров.
  • Не включает динамически добавленные атрибуты.

Когда использовать:

  • В метапрограммировании, для генерации кода или валидации на основе аннотаций.
- класс python определение (определение классов в python)
- Self method python (параметр self в методах python)
- приватные атрибуты python (приватные атрибуты в python)

Расширенные примеры получения атрибутов объекта

Рекурсивный обход атрибутов с исключением служебных

Иногда требуется собрать все атрибуты объекта, включая вложенные. Следующая функция рекурсивно обходит атрибуты, игнорируя служебные (начинающиеся с подчеркивания) и основные встроенные типы.

Пример

def deep_attrs(obj, max_depth=2):
    """Рекурсивно собирает атрибуты объекта."""
    result = {}
    if max_depth <= 0:
        return result
    for name in dir(obj):
        if name.startswith('_'):
            continue
        try:
            value = getattr(obj, name)
        except (AttributeError, TypeError):
            continue
        if hasattr(value, '__dict__') or hasattr(value, '__slots__'):
            result[name] = deep_attrs(value, max_depth - 1)
        else:
            result[name] = repr(value)
    return result

class Config:
    host = 'localhost'
    port = 8080

class App:
    def __init__(self):
        self.config = Config()
        self.name = 'MyApp'

a = App()
import pprint
pprint.pprint(deep_attrs(a))
{'config': {'host': "'localhost'", 'port': '8080'},
 'name': "'MyApp'"}

Получение только свойств (properties) с помощью inspect

Свойства (дескрипторы) не отображаются в __dict__ экземпляра, но их можно найти через класс.

Пример

import inspect

class Temperature:
    def __init__(self, celsius):
        self._celsius = celsius
    
    @property
    def fahrenheit(self):
        return self._celsius * 9/5 + 32
    
    @property
    def kelvin(self):
        return self._celsius + 273.15

obj = Temperature(100)
# Получаем свойства из класса
properties = inspect.getmembers(type(obj), predicate=lambda x: isinstance(x, property))
print(properties)
# [('fahrenheit', ), ('kelvin', )]
[('fahrenheit', ), ('kelvin', )]

Фильтрация атрибутов по типу значения (строки, числа, кастомные объекты)

Пример

from typing import get_type_hints

class User:
    def __init__(self, username, age):
        self.username = username
        self.age = age
        self._id = 1

user = User('ivan', 25)
# Получаем только атрибуты со значениями строкового типа
str_attrs = {k: v for k, v in vars(user).items() if isinstance(v, str)}
print(str_attrs)
# {'username': 'ivan'}
{'username': 'ivan'}

Использование __slots__ и получение атрибутов для таких классов

Классы с __slots__ не имеют __dict__, поэтому vars() не работает. Список имён атрибутов можно получить через сам __slots__.

Пример

class Point2D:
    __slots__ = ('x', 'y')
    def __init__(self, x, y):
        self.x = x
        self.y = y

pt = Point2D(1, 2)
# vars(pt) выдаст TypeError
# Используем __slots__
slot_names = pt.__class__.__slots__
print(slot_names)
# ('x', 'y')
values = {slot: getattr(pt, slot) for slot in slot_names}
print(values)
# {'x': 1, 'y': 2}
('x', 'y')
{'x': 1, 'y': 2}

Динамическое добавление атрибутов и их перечисление

Пример

class DynamicObj:
    pass

obj = DynamicObj()
obj.foo = 100
obj.bar = 'hello'

# После добавления атрибуты появятся в __dict__
print(vars(obj))
# {'foo': 100, 'bar': 'hello'}

# Удаление атрибута
if hasattr(obj, 'foo'):
    delattr(obj, 'foo')
print(vars(obj))
# {'bar': 'hello'}
{'foo': 100, 'bar': 'hello'}
{'bar': 'hello'}

Сравнение производительности dir(), vars() и __dict__

Пример

import timeit

class Test:
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3

t = Test()

print(timeit.timeit('dir(t)', globals={'t': t}, number=100000))
print(timeit.timeit('vars(t)', globals={'t': t}, number=100000))
print(timeit.timeit('t.__dict__', globals={'t': t}, number=100000))
# Примерный вывод (зависит от системы):
# 0.32
# 0.07
# 0.04
0.321456
0.070123
0.041987

Как видно, прямой доступ к __dict__ самый быстрый, vars() чуть медленнее, а dir() существенно медленнее из-за сбора унаследованных имён.

Список атрибутов объекта в Python - comments

En
список атрибутов python (python)