Python: generazione di classe dinamica: i membri di sovrascrittura
-
22-09-2019 - |
Domanda
Ho una gerarchia di classi pitone, che voglio estendere in fase di esecuzione. Inoltre ogni classe in questa gerarchia ha un attributo 'dict' statica, che voglio sovrascrivere in ogni sottoclasse. Simplyfied assomiglia a questo:
'dict' è un membro protetto (pubblico ma con sottolineatura)
class A(object):
_dict = {}
@classmethod
def getdict(cls):
return cls._dict
@classmethod
def setval(cls, name, val):
cls._dict[name] = val
@classmethod
def addchild(cls, name):
return type(name, (cls, ), { '_dict' : {} })
B = A.addchild('B')
A.setval(1, 5)
print A.getdict()
# prints: {1: 5}
# like expected
print B.getdict()
# prints: {}
# like expected
Questo funziona esattamente come previsto. La domanda ora è: perché doesnt funziona più se dichiaro l'attributo privato:
Ora la stessa cosa con 'dict' 'essere un utente privato
class C(object):
__dict = {}
@classmethod
def getdict(cls):
return cls.__dict
@classmethod
def setval(cls, name, val):
cls.__dict[name] = val
@classmethod
def addchild(cls, name):
return type(name, (cls, ), { '__dict' : {} })
D = C.addchild('D')
C.setval(1, 5)
print C.getdict()
# prints: {1: 5}
# like expected
print D.getdict()
# prints: {1: 5}
# why!?
Improvvisamente D
, la sottoclasse di C
, ha gli stessi valori in 'dict' come la sua superclasse!?
si poteva essere così gentile e spiegare a me, quale sia la ragione di questo è? Grazie in anticipo!
Soluzione
phild, come si sa, quando si prefisso un nome di attributo con doppia sottolineatura __
, l'interprete Python cambia automagicamente (Mangani) attribuiscono nome da __attribute
a _CLS__attribute
, dove CLS è il nome della classe.
Tuttavia, quando si dice
return type(name, (cls, ), { '__dict' : {} })
le chiavi nel dizionario { '__dict' : {} }
non ottengono straziati. __dict
rimane lo stesso.
Così D finisce con entrambi D._C__dict
e D.__dict
:
(Pdb) dir(D)
['_C__dict', '__class__', '__delattr__', '__dict', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'addchild', 'getdict', 'setval']
D._C__dict
riferisce alla classe di attributo C. Così, quando si esegue
C.setval(1, 5)
si sta modificando D._C__dict
così come C._C__dict
. Essi sono la stessa cosa.
Altri suggerimenti
Ecco un capitolo nella documentazione su "privato" attributi . E ho commentato voi definizione di classe per renderlo più chiaro:
class C(object):
__dict = {} # This creates C.__dict__['_C__dict']
@classmethod
def getdict(cls):
return cls.__dict # Uses cls.__dict__['_C__dict']
@classmethod
def setval(cls, name, val):
cls.__dict[name] = val # Uses cls.__dict__['_C__dict']
@classmethod
def addchild(cls, name):
return type(name, (cls, ), { '__dict' : {} }) # Creates child.__dict__['__dict']
vale a dire. tutti hanno il loro bambino attributo __dict
, ma è usato solo uno dalla classe base.
non si applicano i concetti Java o C ++ di "protetto" e "privato". La convenzione di denominazione Python fa un po ', ma non quello che stai immaginando.
Il __name
fa qualche nome mangling, rendendo difficile l'accesso perché il nome è oscurata.
Il tuo _dict
e __dict
sono semplicemente gli attributi a livello di classe che sono semplicemente condivisi da tutte le istanze delle classi.