Я только что прочитал кучу документации, и, насколько я могу судить, вся история о том, как разрешается foo.bar
, выглядит следующим образом:
- Can we find
foo.__getattribute__
by the following process? If so, use the result of foo.__getattribute__('bar')
.
- (Looking up
__getattribute__
will not cause infinite recursion, but the implementation of it might.)
- (На самом деле мы всегда будем находить
__getattribute__
в объектах нового стиля, поскольку реализация по умолчанию предоставляется в object
, но эта реализация относится к следующему процессу. ;)) )
- (Если мы определим метод
__getattribute__
в Foo
, а доступ foo.__getattribute__
, foo.__getattribute__('__getattribute__')
будет вызываться! Но это не подразумевает бесконечную рекурсию — если вы будете осторожны. эм> ;))
- Является ли
bar
«специальным» именем для атрибута, предоставляемого средой выполнения Python (например, __dict__
, __class__
, __bases__
, __mro__
)? Если это так, используйте это. (Насколько я могу судить, __getattribute__
попадает в эту категорию, что позволяет избежать бесконечной рекурсии.)
- Есть ли
bar
в словаре foo.__dict__
? Если да, используйте foo.__dict__['bar']
.
- Does
foo.__mro__
exist (i.e., is foo
actually a class)? If so,
- For each base-class
base
in foo.__mro__
[1:]:
- (Note that the first one will be
foo
itself, which we already searched.)
- Is
bar
in base.__dict__
? If so:
- Let
x
be base.__dict__['bar']
.
- Can we find (again, recursively, but it won't cause a problem)
x.__get__
?
- If so, use
x.__get__(foo, foo.__class__)
.
- (Обратите внимание, что функция
bar
сама по себе является объектом, и компилятор Python автоматически присваивает функциям атрибут __get__
, предназначенный для такого использования.)
- В противном случае используйте
x
.
- For each base-class
base
of foo.__class__.__mro__
:
- (Note that this recursion is not a problem: those attributes should always exist, and fall into the "provided by the Python runtime" case.
foo.__class__.__mro__[0]
will always be foo.__class__
, i.e. Foo
in our example.)
- (Обратите внимание, что мы делаем это, даже если
foo.__mro__
существует. Это потому, что у классов тоже есть класс: его имя type
, и он, среди прочего, предоставляет метод, используемый для вычисления атрибутов __mro__
в первую очередь.)
- Is
bar
in base.__dict__
? If so:
- Let
x
be base.__dict__['bar']
.
- Can we find (again, recursively, but it won't cause a problem)
x.__get__
?
- If so, use
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__
для своих собственных объектов и заставить его выполнять особые действия, когда вы устанавливаете атрибут класса для экземпляра объекта, а затем получаете к нему доступ из экземпляра этого другого класса. . Python чрезвычайно рефлексивен. :) Но если вы хотите узнать, как это сделать, и получить действительно полное представление о ситуации, вы много чтения делать. ;)
person
Karl Knechtel
schedule
08.06.2011