acabo de leer a través de un montón de documentación, y por lo que puedo decir, la toda la historia de cómo foo.bar
se resuelve, es el siguiente:
- podemos encontrar
foo.__getattribute__
por el siguiente proceso ? Si es así, use el resultado de foo.__getattribute__('bar')
.
- (Mirando hacia arriba
__getattribute__
no causará una recursión infinita, pero la aplicación de la misma fuerza.)
- (En realidad, siempre vamos a encontrar
__getattribute__
en los objetos de nuevo cuño, como se proporciona una implementación por defecto en object
- pero que la aplicación es del siguiente proceso;.))
- (Si definimos un método
__getattribute__
en Foo
, y el acceso foo.__getattribute__
, foo.__getattribute__('__getattribute__')
se ser llamados pero esto no implica recursi infinita! en - si se tiene cuidado;))
- Es
bar
un nombre "especial" para un atributo proporcionada por la ejecución de Python (por ejemplo, __dict__
, __class__
, __bases__
, __mro__
)? Si es así, usa eso. (Por lo que puedo decir, __getattribute__
entra en esta categoría, lo que evita la recursividad infinita.)
- ¿
bar
en el foo.__dict__
dict? Si es así, use foo.__dict__['bar']
.
- ¿Existe
foo.__mro__
(es decir, es foo
en realidad una clase)? Si es así,
- Para cada clase base
base
en foo.__mro__
[1:]:
- (Tenga en cuenta que el primero de ellos será
foo
sí, que ya se realizaron búsquedas.)
- ¿
bar
en base.__dict__
? Si es así:
- Deje
x
ser base.__dict__['bar']
.
- ¿Podemos encontrar (de nuevo, de forma recursiva, pero no causará ningún problema)
x.__get__
?
- Si es así, utilice
x.__get__(foo, foo.__class__)
.
- (Tenga en cuenta que la función de
bar
es, en sí, un objeto, y el compilador Python da automáticamente funciones un atributo __get__
que está diseñado para ser utilizado de esta manera.)
- lo contrario, utilice
x
.
- Para cada base de clase
base
de foo.__class__.__mro__
:
- (Tenga en cuenta que esta recursividad no es un problema: siempre deben existir esos atributos, y caen en el " proporcionado por el caso del tiempo de ejecución de Python.
foo.__class__.__mro__[0]
siempre será foo.__class__
, es decir, Foo
en nuestro ejemplo.)
- (Tenga en cuenta que hacemos esto incluso si existe
foo.__mro__
. Esto se debe a que las clases tienen una clase, también: su nombre es type
, y proporciona, entre otras cosas, el método utilizado para calcular __mro__
atributos en el primer lugar)
- Es
bar
en base.__dict__
.? Si es así:
- Deje
x
ser base.__dict__['bar']
.
- ¿Podemos encontrar (de nuevo, de forma recursiva, pero no causará ningún problema)
x.__get__
?
- Si es así, utilice
x.__get__(foo, foo.__class__)
.
- (Tenga en cuenta que la función de
bar
es, en sí, un objeto, y el compilador Python da automáticamente funciones un atributo __get__
que está diseñado para ser utilizado de esta manera.)
- lo contrario, utilice
x
.
- Si todavía no hemos encontrado algo para usar: podemos encontrar
foo.__getattr__
por el proceso anterior? Si es así, use el resultado de foo.__getattr__('bar')
.
- Si falló todo,
raise AttributeError
.
bar.__get__
no es realmente una función - es un "método de envoltura" - pero se puede imaginar que sea implementado vagamente como esto:
# 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)
La "unión" que ocurre dentro de la __get__
adjunta automáticamente a bar
(llamado descriptor), dicho sea de paso, es más o menos la razón por la que tiene que especificar los parámetros self
explícitamente para los métodos de Python. En Javascript, this
en sí mismo es mágico; en Python, es simplemente el proceso de atar cosas al self
que es mágico. ;)
Y sí, puede establecer explícitamente un método __get__
en sus propios objetos y hacer que haga cosas especiales cuando se establece un atributo de clase de una instancia del objeto y luego acceder a ella desde una instancia de ese otro clase. Python es extremadamente reflectante. :) Pero si quieres aprender a hacer eso, y obtener una comprensión realmente completa de la situación, have a lot de lectura to do. ;)
Pensé que esto no se podía hacer y fue un diferenciador con Ruby. – OscarRyz
¿El envío de métodos en idiomas dinámicos basados en clases funciona de manera idéntica a los lenguajes basados en prototipos? –
Lo intenté yo mismo, y funcionó. Con buena pinta. – JAB