Tenga una mirada en el siguiente código:¿Cómo acceder a los atributos de clase de una superclase en Python?
class A(object):
defaults = {'a': 1}
def __getattr__(self, name):
print('A.__getattr__')
return self.get_default(name)
@classmethod
def get_default(cls, name):
# some debug output
print('A.get_default({}) - {}'.format(name, cls))
try:
print(super(cls, cls).defaults) # as expected
except AttributeError: #except for the base object class, of course
pass
# the actual function body
try:
return cls.defaults[name]
except KeyError:
return super(cls, cls).get_default(name) # infinite recursion
#return cls.__mro__[1].get_default(name) # this works, though
class B(A):
defaults = {'b': 2}
class C(B):
defaults = {'c': 3}
c = C()
print('c.a =', c.a)
tengo una jerarquía de clases cada uno con su propio diccionario que contiene algunos valores por defecto. Si una instancia de una clase no tiene un atributo particular, se debe devolver un valor predeterminado para ella. Si no hay ningún valor predeterminado para el atributo en el diccionario de la clase actual defaults
, se debe buscar el diccionario de la superclase defaults
.
estoy tratando de poner en práctica esta clase utilizando el método recursivo get_default
. El programa se queda atascado en una recursión infinita, desafortunadamente. Mi comprensión de super()
es, obviamente, deficiente. Al acceder al __mro__
, puedo hacer que funcione correctamente, pero no estoy seguro de que esta sea una solución adecuada.
Tengo la sensación de que la respuesta está en algún lugar en this article, pero no he podido encontrarla todavía. Tal vez necesito recurrir a usar una metaclase?
editar: En mi solicitud, __getattr__
primero comprueba self.base
. Si no es None
, el atributo debe buscarse desde allí. Solo en el otro caso, se debe devolver un valor predeterminado. Probablemente podría anular __getattribute__
. ¿Sería esa la mejor solución?
edición 2: A continuación se muestra un ejemplo extendido de la funcionalidad que estoy buscando. Actualmente se implementa utilizando __mro__
(sugerencia anterior de unutbu, a diferencia de mi método recursivo original). A menos que alguien pueda sugerir una solución más elegante, me complace usar esta implementación. Espero que esto aclare las cosas.
class A(object):
defaults = {'a': 1}
def __init__(self, name, base=None):
self.name = name
self.base = base
def __repr__(self):
return self.name
def __getattr__(self, name):
print(" '{}' attribute not present in '{}'".format(name, self))
if self.base is not None:
print(" getting '{}' from base ({})".format(name, self.base))
return getattr(self.base, name)
else:
print(" base = None; returning default value")
return self.get_default(name)
def get_default(self, name):
for cls in self.__class__.__mro__:
try:
return cls.defaults[name]
except KeyError:
pass
raise KeyError
class B(A):
defaults = {'b': 2}
class C(B):
defaults = {'c': 3}
c1 = C('c1')
c1.b = 55
print('c1.a = ...'); print(' ...', c1.a) # 1
print(); print('c1.b = ...'); print(' ...', c1.b) # 55
print(); print('c1.c = ...'); print(' ...', c1.c) # 3
c2 = C('c2', base=c1)
c2.c = 99
print(); print('c2.a = ...'); print(' ...', c2.a) # 1
print(); print('c2.b = ...'); print(' ...', c2.b) # 55
print(); print('c2.c = ...'); print(' ...', c2.c) # 99
La salida:
c1.a = ...
'a' attribute not present in 'c1'
base = None; returning default value
... 1
c1.b = ...
... 55
c1.c = ...
'c' attribute not present in 'c1'
base = None; returning default value
... 3
c2.a = ...
'a' attribute not present in 'c2'
getting 'a' from base (c1)
'a' attribute not present in 'c1'
base = None; returning default value
... 1
c2.b = ...
'b' attribute not present in 'c2'
getting 'b' from base (c1)
... 55
c2.c = ...
... 99
¿Qué hay de colocar los valores predeterminados directamente en el espacio de nombres de la clase en lugar de utilizar el diccionario 'defaults'? Esto haría innecesaria cualquier magia, simplemente funcionaría. –
@Sven Sabía que debería haber mencionado esto :) En mi aplicación, el '__getattr__' está primero revisando otro atributo (base). Solo si el otro atributo es Ninguno, se debe devolver el valor predeterminado. De lo contrario, el atributo debe buscarse en la base. –
Sí, así es como funcionan los atributos de instancia/clase en Python. :) Verifique la respuesta de Toni. Afaik su código hará exactamente lo mismo que el tuyo. –