new style class et descriptors pour meta programmation
13 réponses
azamruk
Bonjour,
sur la page http://www.python.org/doc/2.2.1/whatsnew/sect-rellinks.html#SECTION000310000000000000000
L'auteur donne l'exemple suivant :
from eiffel import eiffelmethod
class C(object):
def f(self, arg1, arg2):
# The actual function
...
def pre_f(self):
# Check preconditions
...
def post_f(self):
# Check postconditions
...
f = eiffelmethod(f, pre_f, post_f)
en disant qu'il ne serait pas très compliqué d'écrire un module
permettant ce genre de choses.
Seulement voila, je n'arrive pas à imaginer comment faire.
Ce qui m'intéresse, c'est bien la méthode python pour écrire un tel
module et non le module lui même.
Si quelqu'un à une idéee, je suis preneur.
Merci et cordialement,
Michel Claveau - abstraction méta-galactique non triviale en fuite perpétuelle.
Bonsoir !
Effectivement, cette cabane a l'air bien délabrée. C'est plutôt dommage. Mais je ne pourrai pas aider, mon anglais ne dépassant pas le niveau de Google-linguistique...
Michel Claveau
Bonsoir !
Effectivement, cette cabane a l'air bien délabrée. C'est plutôt dommage.
Mais je ne pourrai pas aider, mon anglais ne dépassant pas le niveau de
Google-linguistique...
Effectivement, cette cabane a l'air bien délabrée. C'est plutôt dommage. Mais je ne pourrai pas aider, mon anglais ne dépassant pas le niveau de Google-linguistique...
Michel Claveau
azamruk
Amaury Forgeot d'Arc wrote in message news:<cnja4g$aik$...
Bonjour,
Bonjour,
J'ai une petite question sur les descripteurs. En faisant des essais, je suis tombé sur un comportement que je ne comprend pas.
Soit le code suivant :
class readonly(object) : """ Ne marche que pour un attribut de classe accédé par une instance """
# si V est accédé par une instance de A, ça marche pas mal # si V est accédé par A, on ne passe pas par la méthode __set__ # et donc le descripteur est perdu à la première affectation
def __init__(self) : self.__v = readonly(1000) # ici readonly construit un objet banal, # et __get__ n'est jamais appelée.
Du coup, ce readonly ne sert pas à grand choses...
Sinon, pour votre explication précédente sur les metaclass, si j'ai bien comris, ça ne fonctionne que parce que le code de __init__ de la metaclass est exécuté APRÈS la définition de la classe. Me trompés-je ?
Merci.
-- azam.
Amaury Forgeot d'Arc <afa.NOSPAM@neuf.fr> wrote in message news:<cnja4g$aik$1@aphrodite.grec.isp.9tel.net>...
Bonjour,
Bonjour,
J'ai une petite question sur les descripteurs.
En faisant des essais, je suis tombé sur un comportement que je ne
comprend pas.
Soit le code suivant :
class readonly(object) :
"""
Ne marche que pour un attribut de classe accédé par une instance
"""
# si V est accédé par une instance de A, ça marche pas mal
# si V est accédé par A, on ne passe pas par la méthode __set__
# et donc le descripteur est perdu à la première affectation
def __init__(self) :
self.__v = readonly(1000)
# ici readonly construit un objet banal,
# et __get__ n'est jamais appelée.
Du coup, ce readonly ne sert pas à grand choses...
Sinon, pour votre explication précédente sur les metaclass, si j'ai
bien comris, ça ne fonctionne que parce que le code de __init__ de la
metaclass est exécuté APRÈS la définition de la classe. Me trompés-je
?
# si V est accédé par une instance de A, ça marche pas mal # si V est accédé par A, on ne passe pas par la méthode __set__ # et donc le descripteur est perdu à la première affectation
def __init__(self) : self.__v = readonly(1000) # ici readonly construit un objet banal, # et __get__ n'est jamais appelée.
Du coup, ce readonly ne sert pas à grand choses...
Sinon, pour votre explication précédente sur les metaclass, si j'ai bien comris, ça ne fonctionne que parce que le code de __init__ de la metaclass est exécuté APRÈS la définition de la classe. Me trompés-je ?
Merci.
-- azam.
Adrien Di Mascio
Bonjour,
Le Sun, 21 Nov 2004 04:47:11 -0800, azamruk a écrit :
En faisant des essais, je suis tombé sur un comportement que je ne comprend pas. Soit le code suivant :
class A(object) : V = readonly((1, 2, 3)) # si V est accédé par une instance de A, ça marche pas mal # si V est accédé par A, on ne passe pas par la méthode __set__ # et donc le descripteur est perdu à la première affectation
def __init__(self) : self.__v = readonly(1000) # ici readonly construit un objet banal, # et __get__ n'est jamais appelée.
Effectivement, les descripteurs doivent être définis au niveau de la classe pour que le mécanisme de lookup fonctionne correctement :
a.V sera transformé (si possible) en : type(a).__dict__['V'].__get__(a, type(a))
A.V sera transformé (si possible) en : A.__dict__['V'].__get__(None, V)
En ce qui concerne "__set__", elle n'est appelée que sur les instances, et pas sur les classes, c'est pour ça, que contrairement au protoype de __get__, elle ne prend pas un attribut "obj", et "objtype".
D'autre part, la précédence de lookup dépend du type de descritpeur utilisé (DataDescriptor et NonDataDescriptor (des idées de traductions potables ?)), à savoir qu'un DataDescriptor prend le pas sur une variable d'instance, qui elle-même prend le pas sur un NonDataDescritpor
Exemple : ======== ##################################################################### ## class DataDescriptor(object): ## def __init__(self, val, varname): ## self.val = val ## self.varname = varname ## ## def __get__(self, obj, objtype): ## print "__get__", self.varname ## return self.val ## ## def __set__(self, obj, val): ## print "__set__", self.varname ## self.val = val ## ## class NonDataDescriptor(object): ## def __init__(self, val, varname): ## self.val = val ## self.varname = varname ## ## def __get__(self, obj, objtype): ## print "__get__", self.varname ## return self.val ## ## ## class Test(object): ## x = DataDescriptor('valeur initiale de x', 'X') ## y = NonDataDescriptor('valeur initiale de y', 'Y') ## ## def __init__(self): ## self.x = 12 # (1) ## self.y = 13 # (2) ## ## ## # (1) : on surcharge Test.x, mais comme c'est un DataDescriptor, ## # self.x reste un descripteur, on passe par __get__ / __set__ ## # (2) : on surcharge Test.y, mais c'est un un NonDataDescriptor, ## # on perd donc les appells __get__ / __set__ ######################################################################
Sinon, pour votre explication précédente sur les metaclass, si j'ai bien comris, ça ne fonctionne que parce que le code de __init__ de la metaclass est exécuté APRÈS la définition de la classe. Me trompés-je ? Le __init__ de la métaclasse est effectivement appelé à la fin de la
déclaration de la classe elle-même.
Cordialement,
Adrien.
Bonjour,
Le Sun, 21 Nov 2004 04:47:11 -0800, azamruk a écrit :
En faisant des essais, je suis tombé sur un comportement que je ne
comprend pas.
Soit le code suivant :
class A(object) :
V = readonly((1, 2, 3))
# si V est accédé par une instance de A, ça marche pas mal
# si V est accédé par A, on ne passe pas par la méthode __set__
# et donc le descripteur est perdu à la première affectation
def __init__(self) :
self.__v = readonly(1000)
# ici readonly construit un objet banal,
# et __get__ n'est jamais appelée.
Effectivement, les descripteurs doivent être définis au niveau de la
classe pour que le mécanisme de lookup fonctionne correctement :
a.V sera transformé (si possible) en :
type(a).__dict__['V'].__get__(a, type(a))
A.V sera transformé (si possible) en :
A.__dict__['V'].__get__(None, V)
En ce qui concerne "__set__", elle n'est appelée que sur les instances,
et pas sur les classes, c'est pour ça, que contrairement au protoype de
__get__, elle ne prend pas un attribut "obj", et "objtype".
D'autre part, la précédence de lookup dépend du type de descritpeur
utilisé (DataDescriptor et NonDataDescriptor (des idées de traductions
potables ?)), à savoir qu'un DataDescriptor prend le pas sur une
variable d'instance, qui elle-même prend le pas sur un NonDataDescritpor
Exemple :
========
#####################################################################
## class DataDescriptor(object):
## def __init__(self, val, varname):
## self.val = val
## self.varname = varname
##
## def __get__(self, obj, objtype):
## print "__get__", self.varname
## return self.val
##
## def __set__(self, obj, val):
## print "__set__", self.varname
## self.val = val
##
## class NonDataDescriptor(object):
## def __init__(self, val, varname):
## self.val = val
## self.varname = varname
##
## def __get__(self, obj, objtype):
## print "__get__", self.varname
## return self.val
##
##
## class Test(object):
## x = DataDescriptor('valeur initiale de x', 'X')
## y = NonDataDescriptor('valeur initiale de y', 'Y')
##
## def __init__(self):
## self.x = 12 # (1)
## self.y = 13 # (2)
##
##
## # (1) : on surcharge Test.x, mais comme c'est un DataDescriptor,
## # self.x reste un descripteur, on passe par __get__ / __set__
## # (2) : on surcharge Test.y, mais c'est un un NonDataDescriptor,
## # on perd donc les appells __get__ / __set__
######################################################################
Sinon, pour votre explication précédente sur les metaclass, si j'ai
bien comris, ça ne fonctionne que parce que le code de __init__ de la
metaclass est exécuté APRÈS la définition de la classe. Me trompés-je
?
Le __init__ de la métaclasse est effectivement appelé à la fin de la
class A(object) : V = readonly((1, 2, 3)) # si V est accédé par une instance de A, ça marche pas mal # si V est accédé par A, on ne passe pas par la méthode __set__ # et donc le descripteur est perdu à la première affectation
def __init__(self) : self.__v = readonly(1000) # ici readonly construit un objet banal, # et __get__ n'est jamais appelée.
Effectivement, les descripteurs doivent être définis au niveau de la classe pour que le mécanisme de lookup fonctionne correctement :
a.V sera transformé (si possible) en : type(a).__dict__['V'].__get__(a, type(a))
A.V sera transformé (si possible) en : A.__dict__['V'].__get__(None, V)
En ce qui concerne "__set__", elle n'est appelée que sur les instances, et pas sur les classes, c'est pour ça, que contrairement au protoype de __get__, elle ne prend pas un attribut "obj", et "objtype".
D'autre part, la précédence de lookup dépend du type de descritpeur utilisé (DataDescriptor et NonDataDescriptor (des idées de traductions potables ?)), à savoir qu'un DataDescriptor prend le pas sur une variable d'instance, qui elle-même prend le pas sur un NonDataDescritpor
Exemple : ======== ##################################################################### ## class DataDescriptor(object): ## def __init__(self, val, varname): ## self.val = val ## self.varname = varname ## ## def __get__(self, obj, objtype): ## print "__get__", self.varname ## return self.val ## ## def __set__(self, obj, val): ## print "__set__", self.varname ## self.val = val ## ## class NonDataDescriptor(object): ## def __init__(self, val, varname): ## self.val = val ## self.varname = varname ## ## def __get__(self, obj, objtype): ## print "__get__", self.varname ## return self.val ## ## ## class Test(object): ## x = DataDescriptor('valeur initiale de x', 'X') ## y = NonDataDescriptor('valeur initiale de y', 'Y') ## ## def __init__(self): ## self.x = 12 # (1) ## self.y = 13 # (2) ## ## ## # (1) : on surcharge Test.x, mais comme c'est un DataDescriptor, ## # self.x reste un descripteur, on passe par __get__ / __set__ ## # (2) : on surcharge Test.y, mais c'est un un NonDataDescriptor, ## # on perd donc les appells __get__ / __set__ ######################################################################
Sinon, pour votre explication précédente sur les metaclass, si j'ai bien comris, ça ne fonctionne que parce que le code de __init__ de la metaclass est exécuté APRÈS la définition de la classe. Me trompés-je ? Le __init__ de la métaclasse est effectivement appelé à la fin de la