#!/usr/bin/env python
class Make:
def __getattr__(self, name):
self.__dict__[name] = Make()
return self.__dict__[name]
make = Make()
make.a.dot.separated.name = 666
make.anything.i.want = 777
print make.a.dot.separated.name
print make.anything.i.want
Специальный метод __getattr__
вызывается, когда именованное значение не найдено. Строка make.anything.i.want
в итоге делает эквивалент:
m1 = make.anything # calls make.__getattr__("anything")
m2 = m1.i # calls m1.__getattr__("i")
m2.want = 777
Приведенная выше реализация использует эти вызовы __getattr__
для создания цепочки из Make
объектов каждый раз при обращении к неизвестному свойству. Это позволяет произвольно углублять точечные доступы до окончательного присвоения, в котором назначается реальное значение.
object.__getattr__(self, name)
Вызывается, когда поиск атрибута не обнаружил атрибут в обычных местах (то есть он не является атрибутом экземпляра и не найден в дереве классов для self
). name
- имя атрибута. Этот метод должен вернуть (вычисленное) значение атрибута или вызвать исключение AttributeError
.
Обратите внимание, что если атрибут найден с помощью обычного механизма, __getattr__()
не вызывается. (Это намеренная асимметрия между __getattr__()
и __setattr__()
.) Это делается как из соображений эффективности, так и в противном случае __getattr__()
не будет иметь доступа к другим атрибутам экземпляра. Обратите внимание, что по крайней мере для переменных экземпляра вы можете подделать тотальный контроль, не вставляя никаких значений в словарь атрибутов экземпляра (вместо этого вставляя их в другой объект). См. Метод __getattribute__()
ниже, чтобы узнать, как на самом деле получить полный контроль в классах нового стиля.
object.__setattr__(self, name, value)
Вызывается при попытке присвоения атрибута. Это вызывается вместо обычного механизма (то есть сохраните значение в словаре экземпляра). name
- это имя атрибута, value
- это значение, которое будет присвоено ему.
Если __setattr__()
хочет присвоить атрибуту экземпляра, он не должен просто выполнять self.name = value
- это вызовет рекурсивный вызов самого себя. Вместо этого он должен вставить значение в словарь атрибутов экземпляра, например, self.__dict__[name] = value
. Для классов нового стиля вместо доступа к словарю экземпляров следует вызывать метод базового класса с тем же именем, например, object.__setattr__(self, name, value)
.