В Python, языке объектно-ориентированного программирования (ООП), все является объектом. Объекты имеют атрибуты, либо поля, либо методы, которые могут либо содержать информацию об объекте, либо определять действие, которое может выполнять объект. Изменение или извлечение этих атрибутов, выходящее за рамки этой статьи, может быть достигнуто несколькими различными путями. У каждого объекта есть атрибут __dict__, который представляет собой словарь имен его атрибутов и их значений. Атрибут __dict__ также доступен для ключевых слов класса, где вы увидите определенные атрибуты и методы класса. Python также допускает запись через точку, такую ​​как:

[object].[attribute] = [value] 

для изменения атрибутов объекта. Этот синтаксис интуитивно понятен и удобен для чтения, поэтому он используется чаще всего. Python также имеет встроенный [action]attr methods, который может выполнять задачи записи через точку, когда буквальная интерпретация записи через точку делает ее использование непрактичным. Точечная нотация требует, чтобы имя атрибута шло после точки, поэтому вместо имени атрибута нельзя использовать переменные. Следствием этого ограничения является то, что точечную нотацию нельзя использовать в тех случаях, когда доступ к имени атрибута должен осуществляться через переменную, например, в циклах или при сохранении пользовательского ввода в переменной. Метод модификации объекта, который мы рассмотрим в этой статье:

setattr([object], [key], [value])

Setattr() по существу является синонимом точечной записи, но мы увидим его отличие от __dict__ в контексте SQLAlchemy. SQLAlchemy может «видеть» изменения, сделанные при использовании setattr(), но не может «видеть» изменения, сделанные при использовании __dict__.

При использовании __dict__ и setattr() в контексте Python результаты одинаковы, потому что мы по сути редактируем словарь атрибутов каждого объекта, который представлен __dict__. В контексте SQLAlchemy мы видим разницу из-за того, как SQLAlchemy «видит» изменения. SQLAlchemy переопределяет метод __setattr__, вызываемый при использовании записи через точку, в нескольких своих классах. __setattr__ — это метод универсального класса object в Python. Класс object позволяет использовать дескрипторы, необходимые атрибуты объектов и т. д. __setattr__ — магический метод атрибутов экземпляра, эквивалентный методу setattr(). SQLAlchemy определяет свой собственный класс «DeclarativeMeta», который переопределяет __setattr__ для использования собственного метода set_attribute. Этот метод по существу позволяет диспетчеру состояний SQLAlchemy обрабатывать фоновый процесс обновления _sa_instance_attribute объекта базы данных, чтобы отслеживать любые изменения до того, как они будут зафиксированы в базе данных. Помещая метод set_attribute внутрь переопределенного метода __setattr__, SQLAlchemy перехватывает поведение __setattr__ через собственный дескриптор, определенный в классе Column, подклассе класса DeclarativeMeta. Используя дескрипторы, SQLAlchemy может моделировать модификацию объекта как изменение атрибута без ограничений точечной нотации и с необходимой записью изменений объекта для внесения в базу данных.

[Object].[ColumnObject] = [new_value] === 
[Object].[ColumnObject].__setattr__([key], [new_value])

Таким образом, при использовании __dict__ весь процесс __setattr__ игнорируется, поэтому SQLAlchemy не может «видеть» сделанные изменения, потому что __setattr__ не вызывается. В этом случае есть новый атрибут экземпляра, который маскирует исходный атрибут класса объекта Column. Маскировка означает, что не будет никакой записи сделанных изменений, даже если впоследствии будет использовано точечное обозначение, потому что атрибут полностью изменился. Без установки объекта, который переопределяет __setattr__, SQLAlchemy не может зафиксировать какие-либо изменения, внесенные в объект.