Pitón:generación de clases dinámicas:sobrescribir miembros
-
22-09-2019 - |
Pregunta
Tengo una jerarquía de clases de Python que quiero ampliar en tiempo de ejecución.Además, cada clase en esta jerarquía tiene un atributo estático 'dict' que quiero sobrescribir en cada subclase.Simplificado se ve así:
'dict' es un miembro protegido (público pero con guión bajo inicial)
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
Esto funciona tal como se esperaba.La pregunta ahora es:¿Por qué ya no funciona si declaro el atributo privado?
Ahora pasa lo mismo con 'dict' como miembro privado.
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!?
De repente D
, la subclase de C
, tiene los mismos valores en 'dict' que su superclase!?
¿Alguien podría ser tan amable y explicarme cuál es el motivo de esto?¡Gracias de antemano!
Solución
Phild, como sabes, cuando antepones un nombre de atributo con doble guión bajo __
, el intérprete de Python cambia automáticamente (destroza) el nombre del atributo de __attribute
a _CLS__attribute
, donde CLS es el nombre de la clase.
Sin embargo, cuando dices
return type(name, (cls, ), { '__dict' : {} })
las claves en el diccionario { '__dict' : {} }
no te dejes destrozar. __dict
sigue siendo el mismo.
Así D termina con ambos D._C__dict
y 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
se refiere al atributo de clase de C.Entonces cuando corres
C.setval(1, 5)
estas cambiando D._C__dict
así como C._C__dict
.Son uno y lo mismo.
Otros consejos
Aquí está una capítulo en la documentación acerca de "privado" atributos . Y te comento definición de clase para que sea más claro:
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']
es decir. todas niño tienen su propio atributo __dict
, pero sólo uno de la clase base se utiliza.
El Java o conceptos de "protegido" y "privado" C ++ no se aplican. La convención de nombres de Python hace un poco, pero no te lo estás imaginando.
El __name
hace algún renombrado de nombres, lo que hace difícil el acceso porque el nombre es oscurecida.
Su _dict
y __dict
son simplemente atributos de nivel de clase que simplemente están compartidos por todas las instancias de las clases.