В 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 не может зафиксировать какие-либо изменения, внесенные в объект.