Derivacion de tipos, interceptar operaciones
Oswaldo Hernández
listas en soft-com.es
Lun Mar 17 22:03:18 CET 2008
Chema Cortes escribió:
> El 17/03/08, Oswaldo Hernández <listas en soft-com.es> escribió:
>
>> Buscando solucion a esto he encontrado otra discrepancia entre tipos y clases "normales". El
>> __dict__ de una clase normal es un diccionario, mientras que en una clase derivada de un tipo es un
>> objeto 'dictproxy', el cual no se puede utilizar directamente, o por lo menos yo no he encontrado como.
>
> En realidad, más que discrepar, es una parte del mecanismo para poder
> unificar ambos conceptos (PEPs 252 y 253) con el que se implementa el
> protocolo del "descriptor" (por cierto, procotolo implementado gracias
> al método __getatribute__ del tipo 'object', progenitor de todas las
> clases nuevas).
>
> El "dictproxy" funciona igual que un diccionario (protocolo
> "mapping"). Lo que pasa es que sus items están protegidos contra
> escritura, por lo que de poco te servirá.
>
> http://www.python.org/dev/peps/pep-0252/
> http://www.python.org/dev/peps/pep-0253/
Los estuve viendo, igual que cafepy.com, pero he de confesar que llega un momento en que me pierdo, :(
>
> Es muy complejo de explicar, por lo que te remito a estos PEPs y a los
> tutoriales que hay por http://cafepy.com (y, si te animas, a revisar
> el código C). Así, por encima, puedo contarte que los atributos
> "estáticos" (aquellos que obtiene la clase por ser un "tipo de dato")
> se implementan mediante "slots". Estos slots apuntan inicialmente a
> métodos facilitados por el tipo de dato, y son sustituídos por los que
> suplanta la herencia (atributos dinámicos). Los métodos estáticos son
> mucho más rápidos de ejecutar por el intérprete, evitando la
> sobrecarga que impone una herencia pura con las operaciones básicas
> estándar. Supongo que, por éso mismo, python resulta bastante más
> rápido que smalltalk y lenguajes similares.
>
> La única solución para tu problema sería crear "wrappers" para todos y
> cada uno de los métodos estáticos que tenga el tipo de dato, algo como
> se cuenta en esta receta:
>
> http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496741
Muy, muy, muy interesante, aunque no es lo mismo, se parece bastante a mi proyecto. Lo revisare con
detenimiento.
Sobre los slots habia visto algo, pero lo volveré a revisar.
Mi necesidad es filtrar las instancias antes de que se realice cualquier tipo de operacion con
ellas, y en ciertos casos impedir la operacion lanzando un ValueError.
Un resumen de lo que he hecho he hecho es lo siguiente:
1.- Definicion de las clases con un metodo __new__ comun
# esta funcion es llamada en el new de las bases para verificar valores y creacion de instancia
def _Super_New_(cls, *args):
# verificacion de rango
if cls.Rango and args[0] < cls.Rango[0] or args[0] > cls.Rango[1]:
raise ValueError
...otros chequeos y adaptaciones ...
return = cls.__base__.__new__(cls, *args)
# Clases principales
class c_int(int):
Valido = True # en ciertos casos me interesa no dar error
# y marcar la instancia como 'No Valida'
Rango = None
def __new__(cls, *args):
return _Super_new_(cls, *args)
class c_date(datetime.date):
Valido = True
Rango = None
def __new__(cls, *args):
return _Super_new_(cls, *args)
2.- Añadir metodos sobrescritos
Para añadir los metodos sobreescritos, sin el engorro y la dificultad de mantenimiento posterior de
poner uno por uno en cada clase, he hecho lo siguiente:
Primero, un SuperMetodo que filtrará todas las operaciones:
def __SuperMetodo(self, op, *args):
if not self.Valido:
raise ValueError
# verificar si other es mio y si es valido tambien
if hasattr(args[0], "Valido") and not args[0].Valido:
raise ValueError
# ejecutar la operacion en base
r = getattr(self.__base__, op)(self, *args, **kwds)
... Otras verificaciones ....
return r
A nivel de modulo, para que se ejecute cuando se realice el import:
Operadores = ("__add__", "__sub__", ....)
Clases = (m_int, m_date, ...)
Ahora tengo que sobreescribir el metodo de operadores en cada clase para que llamen al __SuperMetodo
pasandole como argumento la instancia, la operacion a realizar y sus argumentos, y este, si lo cree
conveniente, ejecute el metodo en la base.
Aqui es donde entra mi problema con el __dict__, no puedo utilizar exec() ni new.Function() para
insertar directamente los metodos con su codigo en las clases porque a ambas hay que pasarles el
diccionario y rechazan el 'dictproxy'.
Para solucionar esto he hecho lo siguiente:
Creo Metodos genericos para todas las clases en globals()
for op in in Operadores:
exec "def %sz(self, *args, **kwds): return __SuperMetodo(self, '%s', *args, **kwds)" % (op, op)
in globals()
Creo los metodos en las clases son setattr() asignandolos a los creados en globals()
for c in Clase :
for m in Metodos:
if hasattr(c , op):
setattr(c, op, globals()["%sz" % op])
Bueno, discupad el rollo, pero llevo un monton de horas peleandome con esto.
Parace que ya funciona :), pero si alguin tiene una idea mejor de como hacerlo, bienvenida es.
Saludos,
--
*****************************************
Oswaldo Hernández
oswaldo (@) soft-com (.) es
*****************************************
PD:
Antes de imprimir este mensaje, asegúrese de que es necesario.
El medio ambiente está en nuestra mano.
_______________________________________________
Lista de correo Python-es
http://listas.aditel.org/listinfo/python-es
FAQ: http://listas.aditel.org/faqpyes
Más información sobre la lista de distribución Python-es