Date un'occhiata al seguente codice:Come accedere agli attributi di una classe di una superclasse in 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)
ho una gerarchia di classi ciascuna con un proprio dizionario che contiene alcuni valori di default. Se un'istanza di una classe non ha un particolare attributo, dovrebbe invece essere restituito un valore predefinito. Se nessun valore predefinito per l'attributo è contenuto nel dizionario defaults
della classe corrente, deve essere cercato il dizionario defaults
della superclasse.
Sto cercando di implementarlo utilizzando il metodo della classe ricorsivo get_default
. Il programma si blocca in una ricorsione infinita, sfortunatamente. La mia comprensione di super()
è ovviamente carente. Accedendo a __mro__
, posso farlo funzionare correttamente, ma non sono sicuro che questa sia una soluzione adeguata.
Ho la sensazione che la risposta sia da qualche parte in this article, ma non sono ancora riuscita a trovarla. Forse ho bisogno di ricorrere all'utilizzo di un metaclass?
modifica: Nella mia domanda, __getattr__
primi assegni self.base
. Se non è None
, l'attributo deve essere recuperato da lì. Solo nell'altro caso, è necessario restituire un valore predefinito. Probabilmente potrei ignorare lo __getattribute__
. Sarebbe la soluzione migliore?
modifica 2: Di seguito è riportato un esempio esteso della funzionalità che sto cercando. Attualmente è implementato usando __mro__
(suggerimento precedente di unutbu, in contrasto con il mio metodo ricorsivo originale). A meno che qualcuno non possa suggerire una soluzione più elegante, sono felice di usare questa implementazione. Spero che questo chiarisca le cose.
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
L'output:
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
Come mettere le impostazioni predefinite direttamente nello spazio dei nomi classe invece di utilizzare il dizionario "default"? Ciò renderebbe inutile qualsiasi magia - funzionerebbe semplicemente. –
@Sven Sapevo che avrei dovuto dirlo :) Nella mia applicazione, '__getattr__' sta prima controllando un altro attributo (base). Solo se l'altro attributo è Nessuno, è necessario restituire il valore predefinito. Altrimenti, l'attributo deve essere cercato in base. –
Sì, è così che funzionano gli attributi instance/class in Python. :) Controlla la risposta di Toni. Afaik il suo codice farà esattamente lo stesso tuo. –