Получение атрибутов объекта в Python (рефлексия)

Раздел: Основы Python -> Рефлексия и атрибуты

Основные способы получения атрибутов

В Python существует несколько методов для извлечения атрибутов объектов. Наиболее распространённый и прямой способ - использование встроенной функции getattr().

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

value = getattr(obj, 'attribute_name', default_value)

Get attributes python (получение атрибутов объекта в python)

Функция принимает объект, имя атрибута (строку) и необязательное значение по умолчанию. Если атрибут существует, она возвращает его значение; в противном случае возвращается default_value (или выбрасывается AttributeError, если значение не задано).

Распространённая ошибка: вызов getattr без значения по умолчанию для несуществующего атрибута приводит к AttributeError. Решение - либо предоставлять default, либо предварительно проверять существование с помощью hasattr().

Другая проблема: попытка получить атрибут, который является свойством (@property) или дескриптором - getattr корректно вызовет геттер, поэтому проблем не возникает.

Как проверить существование атрибута перед его получением?

Функция hasattr(obj, name) возвращает True, если объект имеет атрибут с именем name, иначе False. Эта проверка удобна для условного доступа.

if hasattr(obj, 'some_attr'):
    value = getattr(obj, 'some_attr')
else:
    value = None

Python get class (получение класса объекта в python)

Нюанс: hasattr работает путём вызова getattr и перехвата исключения. Если атрибут существует, но его геттер выбрасывает исключение, hasattr вернёт False. Следует учитывать это при работе с динамическими дескрипторами.

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

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

class A:
    x = 10
    def method(self): pass

obj = A()
print(dir(obj))

Python get type (получение типа объекта в python)

['__class__', '__delattr__', '__dict__', '__dir__', ... 'method', 'x']

Ограничение: dir не различает собственные и унаследованные атрибуты. Для получения только собственных атрибутов экземпляра используют vars() или __dict__.

Как извлечь только атрибуты экземпляра (без методов и без унаследованных полей)?

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

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

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

Важно: vars() не работает для встроенных типов, таких как список или строка, и для классов со слотом __slots__ (если не определён __dict__). В таких случаях возникает TypeError. Для классов с __slots__ атрибуты можно получить через getattr и список слотов.

Как переопределить логику получения атрибутов для всех обращений?

Метод __getattribute__(self, name) вызывается при любом доступе к атрибуту объекта. Позволяет реализовать кастомную логику (логирование, контроль доступа, преобразование).

class LoggedGet:
    def __getattribute__(self, name):
        print(f'Доступ к атрибуту: {name}')
        return super().__getattribute__(name)

obj = LoggedGet()
obj.x = 1
print(obj.x)  # выведет сообщение и 1

Опасность: рекурсивный вызов, если внутри __getattribute__ снова обратиться к атрибуту (например, self.name). Рекомендуется использовать super().__getattribute__ для базового получения.

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

Метод __getattr__(self, name) вызывается только если атрибут не найден стандартным механизмом (то есть после того, как __getattribute__ выбросил AttributeError). Это удобно для создания динамических атрибутов.

class DefaultAttr:
    def __getattr__(self, name):
        return f'Атрибут {name} не определён'

obj = DefaultAttr()
print(obj.any_name)  # 'Атрибут any_name не определён'

Внимание: __getattr__ не вызывается для атрибутов, которые реально существуют. Если нужно переопределить получение всех атрибутов, используется __getattribute__.

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

Модуль inspect предоставляет функцию getmembers(obj, predicate=None). Она возвращает список кортежей (имя, значение) для всех членов, удовлетворяющих предикату. Предикат - это вызываемый объект, принимающий значение и возвращающий True/False (например, inspect.ismethod).

import inspect

class Sample:
    a = 1
    def foo(self): pass

for name, val in inspect.getmembers(Sample(), predicate=inspect.ismethod):
    print(name)
foo

Совет: getmembers обходит также __dict__ класса и его предков, поэтому включает унаследованные методы. Для фильтрации по собственным методам можно дополнительно проверять name in obj.__class__.__dict__.

Расширенные примеры

1. Динамическое получение атрибутов из конфигурации

Предположим, есть класс настроек, имена атрибутов берутся из словаря. Для безопасного доступа применяется getattr.

Пример
class Config:
    def __init__(self, data):
        for key, value in data.items():
            setattr(self, key, value)

cfg_data = {'host': 'localhost', 'port': 8080, 'debug': True}
cfg = Config(cfg_data)

# Получение по имени
params = ['host', 'port', 'missing']
for p in params:
    value = getattr(cfg, p, None)
    print(f'{p}: {value}')
host: localhost
port: 8080
missing: None

2. Рефлексивный вызов методов по имени

Используется getattr для получения метода и его вызова, если метод существует.

Пример
class Calculator:
    def add(self, a, b):
        return a + b
    def sub(self, a, b):
        return a - b

calc = Calculator()
operation = 'add'
if hasattr(calc, operation):
    method = getattr(calc, operation)
    result = method(10, 5)
    print(f'Результат {operation}: {result}')
Результат add: 15

3. Сериализация объекта в словарь с помощью vars

Атрибуты экземпляра преобразуются в словарь для последующего сохранения в JSON.

Пример
class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

user = User('Bob', 'bob@example.com')
data = vars(user)
print(data)
# {'name': 'Bob', 'email': 'bob@example.com'}
{'name': 'Bob', 'email': 'bob@example.com'}

4. Проверка наличия атрибута и получение альтернативного значения

Комбинация hasattr и getattr для реализации условного доступа.

Пример
class Database:
    def __init__(self, connected):
        self.connected = connected

db = Database(True)
if hasattr(db, 'connected') and getattr(db, 'connected'):
    print('База данных подключена')
else:
    print('Подключение не установлено')
База данных подключена

5. Сравнение dir() и vars() для класса со слотами

Когда класс использует __slots__, vars() не работает. Представлена альтернатива.

Пример
class Point:
    __slots__ = ('x', 'y')
    def __init__(self, x, y):
        self.x = x
        self.y = y

p = Point(10, 20)
# print(vars(p))  # TypeError: vars() argument must have __dict__ attribute
# Используется getattr со списком слотов
for slot in Point.__slots__:
    print(f'{slot}: {getattr(p, slot)}')
x: 10
y: 20

6. Использование __getattribute__ для логирования всех доступов

Расширенный пример с классом, который записывает в журнал каждое обращение к атрибуту.

Пример
import logging

logging.basicConfig(level=logging.INFO)

class LoggedObject:
    def __getattribute__(self, name):
        logging.info(f'Получен атрибут: {name}')
        return super().__getattribute__(name)

obj = LoggedObject()
obj.value = 42
print(obj.value)  # лог: Получен атрибут: value, затем вывод 42
INFO:root:Получен атрибут: value
42

7. Динамические атрибуты через __getattr__ в ORM-подобном стиле

Создание объекта, который позволяет обращаться к полям базы данных как к атрибутам.

Пример
class DynamicModel:
    def __init__(self, data):
        self._data = data
    def __getattr__(self, name):
        if name in self._data:
            return self._data[name]
        raise AttributeError(f'Поле {name} не найдено')

record = DynamicModel({'id': 1, 'title': 'Пример'})
print(record.title)  # Пример
# print(record.name)  # AttributeError
Пример

8. Inspect.getmembers для извлечения публичных методов класса

Фильтрация по callable и исключение служебных методов.

Пример
import inspect

class Service:
    def __init__(self): pass
    def run(self): pass
    def _internal(self): pass
    def __private(self): pass

members = inspect.getmembers(Service(), predicate=inspect.ismethod)
public_methods = [(n, m) for n, m in members if not n.startswith('_')]
for name, method in public_methods:
    print(name)
run

Получение атрибутов объекта в Python - comments

En
Get attributes python (python)