Я только что прочитал кучу документации, и, насколько я могу судить, вся история о том, как foo.bar
разрешена, выглядит следующим образом:
- Можем ли мы найти
foo.__getattribute__
с помощью следующего процесса?Если это так, используйте результат foo.__getattribute__('bar')
. - (Поиск
__getattribute__
не вызовет бесконечной рекурсии, но реализация этого может.) - (В действительности, мы всегда найдем
__getattribute__
в объектах нового стиля, так какреализация по умолчанию предоставляется в object
- но эта реализация имеет следующий процесс.;)) - (Если мы определяем метод
__getattribute__
в Foo
и получаем доступ foo.__getattribute__
, foo.__getattribute__('__getattribute__')
будет вызываться! Но это не подразумевает бесконечную рекурсию - , если вы осторожны ;))
- Является ли
bar
«специальным» именем для атрибута, предоставленного средой исполнения Python (например, __dict__
, __class__
, __bases__
, __mro__
)?Если так, используйте это.(Насколько я могу судить, __getattribute__
попадает в эту категорию, которая избегает бесконечной рекурсии.) - Является ли
bar
в диктате foo.__dict__
?Если это так, используйте foo.__dict__['bar']
. - Существует ли
foo.__mro__
(т. Е. Действительно ли foo
является классом)?Если это так, - Для каждого базового класса
base
в foo.__mro__
[1:]: - (Обратите внимание, что первым будет сам
foo
, который мы уже искали.) - Является ли
bar
в base.__dict__
?Если так: - Пусть
x
будет base.__dict__['bar']
. - Можем ли мы найти (опять же, рекурсивно, но это не вызовет проблемы)
x.__get__
? - Если это так, используйте
x.__get__(foo, foo.__class__)
. - (Обратите внимание, что функция
bar
сама является объектом, и компилятор Python автоматически присваивает функциям атрибут __get__
, который предназначендля использования таким образом.) - В противном случае используйте
x
.
- Для каждого базового класса
base
из foo.__class__.__mro__
: - (Обратите внимание, что эта рекурсия не является проблемой: эти атрибуты всегда должны существовать и попадать в случай «предоставленный Python runtime».
foo.__class__.__mro__[0]
всегда будет foo.__class__
, то есть Foo
в нашем примере.) - (Обратите внимание, что мы делаем это, даже если существует
foo.__mro__
. Это потому, что у классов тоже есть класс: его имя равно type
, и, кроме всего прочего, предоставляет метод, используемый для вычисления __mro__
атрибутов.) - Является ли
bar
в base.__dict__
?Если так: - Пусть
x
будет base.__dict__['bar']
. - Можем ли мы найти (опять же, рекурсивно, но это не вызовет проблемы)
x.__get__
? - Если это так, используйте
x.__get__(foo, foo.__class__)
. - (Обратите внимание, что функция
bar
сама по себе является объектом, и компилятор Python автоматически дает функциям атрибут __get__
, который предназначениспользовать таким образом.) - В противном случае используйте
x
.
- Если мы все еще не нашел что-то для использования: можем ли мы найти
foo.__getattr__
с помощью предыдущего процесса?Если это так, используйте результат foo.__getattr__('bar')
. - Если все не удалось,
raise AttributeError
.
bar.__get__
на самом деле не функция - это«метод-обертка» - но вы можете представить, что он реализован примерно так:
# Somewhere in the Python internals
class __method_wrapper(object):
def __init__(self, func):
self.func = func
def __call__(self, obj, cls):
return lambda *args, **kwargs: func(obj, *args, **kwargs)
# Except it actually returns a "bound method" object
# that uses cls for its __repr__
# and there is a __repr__ for the method_wrapper that I *think*
# uses the hashcode of the underlying function, rather than of itself,
# but I'm not sure.
# Automatically done after compiling bar
bar.__get__ = __method_wrapper(bar)
«Связывание», которое происходит внутри __get__
, автоматически присоединяемого к bar
(называемого дескриптор ), кстати, является более или менее причиной, по которой вы должны явно указывать self
параметры для методов Python.В Javascript this
само по себе волшебно;в Python это просто процесс привязки вещей к self
, который является магическим.;)
И да, вы можете явно установить метод __get__
для ваших собственных объектов и заставить его делать особые вещи, когда вы устанавливаете атрибут класса для экземпляра объекта, а затемполучить доступ к нему из экземпляра этого другого класса.Питон чрезвычайно отражающий.:) Но если вы хотите научиться делать это и получить действительно полное понимание ситуации, у вас есть много чтения , чтобы сделать .;)