Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

meta classes

9 réponses
Avatar
Olivier Ravard
bonjour,

Pour définir la classe d'une classe on utilise la
méta-classe.
ex :
class MetaClass(type):
def ...
class MaClasse:
__metaclass__ = MetaClass
def ...

Mais, est-il possible de faire de même pour une fonction ?

def maFunc(args):
__que_dois_je_mettre__

Merci.

9 réponses

Avatar
Bruno Desthuilliers
bonjour,

Pour définir la classe d'une classe on utilise la
méta-classe.
ex :
class MetaClass(type):
def ...
class MaClasse:
__metaclass__ = MetaClass
def ...

Mais, est-il possible de faire de même pour une fonction ?


Pas tout à fait. Une fonction est une instance de la classe function.
Cette classe a la particularité de définir la méthode __call__. Rien
n'empêche, bien sûr, d'autres classes de définir cette même méthode.

def maFunc(args):
__que_dois_je_mettre__



class MaFunc(object):
__metaclass__ = OnPeutBienSurCombiner
def __call__(self):
print "allo ?"

maFunc = MaFunc()
maFunc()

Attention par contre, si tu veux pouvoir utiliser maFunc comme méthode,
il faut que MaFunc implémente correctement le protocole descripteur.

HTH

Avatar
Olivier Ravard
Mais, est-il possible de faire de même pour une fonction ?


Pas tout à fait. Une fonction est une instance de la classe function.
Cette classe a la particularité de définir la méthode __call__. Rien
n'empêche, bien sûr, d'autres classes de définir cette même méthode.



Mais alors, ne serait-il pas possible à la classe fonction de lui dire
d'utiliser MaMetaClasse comme metaclasse ?

def maFunc(args):
__que_dois_je_mettre__



class MaFunc(object):
__metaclass__ = OnPeutBienSurCombiner
def __call__(self):
print "allo ?"

Oui, bien sûr, on peut faire ça, mais je souhaiterais le faire

sur un ensemble de modules sans toucher au code ...
maFunc = MaFunc()
maFunc()

Attention par contre, si tu veux pouvoir utiliser maFunc comme méthode,
il faut que MaFunc implémente correctement le protocole descripteur.

HTH



Avatar
Bruno Desthuilliers

Mais, est-il possible de faire de même pour une fonction ?


Pas tout à fait. Une fonction est une instance de la classe function.
Cette classe a la particularité de définir la méthode __call__. Rien
n'empêche, bien sûr, d'autres classes de définir cette même méthode.



Mais alors, ne serait-il pas possible à la classe fonction de lui dire
d'utiliser MaMetaClasse comme metaclasse ?


Tu a essayé ?-)

Non, ce n'est pas possible. Et ça n'aurait pas beaucoup de sens par
ailleurs : les metaclasses sont essentiellement utiles pour customiser
le processus de création des classes - or, la classe function est *déjà*
construite quand tu va vouloir en changer la metaclasse.

Accessoirement, et sis ce n'est pas indiscret, pourquoi veux-tu une
autre metaclasse pour les fonctions?



Avatar
Olivier Ravard
Bruno Desthuilliers wrote:

Mais, est-il possible de faire de même pour une fonction ?


Pas tout à fait. Une fonction est une instance de la classe function.
Cette classe a la particularité de définir la méthode __call__. Rien
n'empêche, bien sûr, d'autres classes de définir cette même méthode.



Mais alors, ne serait-il pas possible à la classe fonction de lui dire
d'utiliser MaMetaClasse comme metaclasse ?


Tu a essayé ?-)

Non, ce n'est pas possible. Et ça n'aurait pas beaucoup de sens par
ailleurs : les metaclasses sont essentiellement utiles pour customiser
le processus de création des classes - or, la classe function est *déjà*
construite quand tu va vouloir en changer la metaclasse.

Accessoirement, et sis ce n'est pas indiscret, pourquoi veux-tu une
autre metaclasse pour les fonctions?
Non, ce n'est pas indiscret.

J'utilise ce principe pour appliquer la programmation par contrat :
pré-conditions, post-conditions et invariants de méthodes ou de classes.
Je sais, ça existe déjà (pycontract qui utilise __getattr__) mais
les exceptions ne sont pas très parlantes et on est obligé de spécifier
tous les modules que l'on veut traiter. Bref j'ai voulu faire qqchose
à ma sauce à base de métaclasses et ça marche très bien.
Par exemple :

class Test:
"""
inv:
self.j >0
"""

__metaclass__ = MetaContract

j = 1
k = None
def __init__(self):
"""
post:
len(self.k)==3
"""
self.k = [1,2,3]

def test(self, f=array(()),i=1, toto=1):
"""
This is a test function
pre:
isinstance(i, int)
isinstance(f,type(array(())))
post:

__return__ == 1
self.i == 1
len(f)==3
inv:
self.j == 1
isinstance(self.j,int)
"""
self.i = 1
self.j = 1
return 1


m = Test()
m.test(array((1,2,3)), i=1.0)


donne --->

Traceback (most recent call last):
File "../etc/metacontract.py", line 228, in ?
main()
File "../etc/metacontract.py", line 217, in main
m.test(array((1,2,3)), i=1.0)
File "../etc/metacontract.py", line 107, in _trace
raise ValueError(condition+' condition assertion ('+class_or_method+' contract line
%d): %s'%(i, code_line,))
ValueError: Pre- condition assertion (method contract line 2): isinstance(i, int)

Le problème est que je ne peux pas appliquer ce principe aux fonctions...




Avatar
Bruno Desthuilliers
Bruno Desthuilliers wrote:

Mais, est-il possible de faire de même pour une fonction ?


Pas tout à fait. Une fonction est une instance de la classe
function. Cette classe a la particularité de définir la méthode
__call__. Rien n'empêche, bien sûr, d'autres classes de définir
cette même méthode.



Mais alors, ne serait-il pas possible à la classe fonction de lui dire
d'utiliser MaMetaClasse comme metaclasse ?


Tu a essayé ?-)

Non, ce n'est pas possible. Et ça n'aurait pas beaucoup de sens par
ailleurs : les metaclasses sont essentiellement utiles pour customiser
le processus de création des classes - or, la classe function est
*déjà* construite quand tu va vouloir en changer la metaclasse.

Accessoirement, et sis ce n'est pas indiscret, pourquoi veux-tu une
autre metaclasse pour les fonctions?
Non, ce n'est pas indiscret.

J'utilise ce principe pour appliquer la programmation par contrat :
pré-conditions, post-conditions et invariants de méthodes ou de classes.
Je sais, ça existe déjà (pycontract qui utilise __getattr__) mais
les exceptions ne sont pas très parlantes et on est obligé de spécifier
tous les modules que l'on veut traiter. Bref j'ai voulu faire qqchose
à ma sauce à base de métaclasses et ça marche très bien.
Par exemple :

class Test:


class Test(object):

Si tu veux jouer avec les metaclasses, descripteurs et autres
joyeusetés, utilise les classes new-style (et sinon, pareil - les
classes old-styles n'existent plus guère que pour des raisins hystériques)

"""
inv:
self.j >0
"""

__metaclass__ = MetaContract

j = 1
k = None
def __init__(self):
"""
post:
len(self.k)==3
"""
self.k = [1,2,3]

def test(self, f=array(()),i=1, toto=1):
"""
This is a test function
pre:
isinstance(i, int)
isinstance(f,type(array(())))
post:

__return__ == 1
self.i == 1
len(f)==3
inv:
self.j == 1
isinstance(self.j,int)
"""
self.i = 1
self.j = 1
return 1


m = Test()
m.test(array((1,2,3)), i=1.0)


donne --->

Traceback (most recent call last):
File "../etc/metacontract.py", line 228, in ?
main()
File "../etc/metacontract.py", line 217, in main
m.test(array((1,2,3)), i=1.0)
File "../etc/metacontract.py", line 107, in _trace
raise ValueError(condition+' condition assertion
('+class_or_method+' contract line %d): %s'%(i, code_line,))
ValueError: Pre- condition assertion (method contract line 2):
isinstance(i, int)

Le problème est que je ne peux pas appliquer ce principe aux fonctions...


Je suppose que tu utilises des décorateurs (ajoutés par la métaclasse)
pour gérer les pre/post conditions ?

Si oui, pour les fonctions, il te suffit d'ajouter explicitement les
décorateurs,ie:

@with_contract
def my_func(toto, tutu):
"""
pre:
isinstance(toto, int)
"""
# code here

Sinon (je veux dire, si tu ne gères pas ça avec des décorateurs), c'est
que tu utilise __getattribute__, ce qui est une mauvaise idée AMHA.

Mes deux centimes...





Avatar
Michel Claveau
Salut !


Peut-être que la nouvelle syntaxe, proposée par GVR (pour Py3k) :
class C(B1, B2, metaclass=MC, *more_bases, **kwds):
pass
facilitera les choses...

(voir http://tinyurl.com/2vtgjn )








--
@-salutations

Michel Claveau
Avatar
Michel Claveau
SVP, ignorez mon message précédent, il est HS. Désolé.






--
@-salutations

Michel Claveau
Avatar
Olivier Ravard
class Test:


class Test(object):

Si tu veux jouer avec les metaclasses, descripteurs et autres
joyeusetés, utilise les classes new-style (et sinon, pareil - les
classes old-styles n'existent plus guère que pour des raisins hystériques)

pardonne mon ignorance, mais j'ai peur de ne pas comprendre... quel est le "new-style" ?



Si oui, pour les fonctions, il te suffit d'ajouter explicitement les
décorateurs,ie:

@with_contract
def my_func(toto, tutu):
"""
pre:
isinstance(toto, int)
"""
# code here
oui, c'est vrai, mais dans ce cas, je suis obligé de modifier tous les modules

que je veux "tracer" ce que justement je voulais éviter.
Une autre solution serait, lors de l'import de chaque module, de faire qqchose
du type :
module.my_func = decorate(module.my_func)

Sinon (je veux dire, si tu ne gères pas ça avec des décorateurs), c'est
que tu utilise __getattribute__, ce qui est une mauvaise idée AMHA.

Mes deux centimes...



Avatar
Bruno Desthuilliers

class Test:


class Test(object):

Si tu veux jouer avec les metaclasses, descripteurs et autres
joyeusetés, utilise les classes new-style (et sinon, pareil - les
classes old-styles n'existent plus guère que pour des raisins
hystériques)

pardonne mon ignorance, mais j'ai peur de ne pas comprendre... quel est

le "new-style" ?


http://www.python.org/doc/newstyle/


Si oui, pour les fonctions, il te suffit d'ajouter explicitement les
décorateurs,ie:

@with_contract
def my_func(toto, tutu):
"""
pre:
isinstance(toto, int)
"""
# code here
oui, c'est vrai, mais dans ce cas, je suis obligé de modifier tous les

modules
que je veux "tracer" ce que justement je voulais éviter.


Pardonne mon ignorance, mais en quoi l'utilisation des metaclasses
t'éviterait-t'il ce problème ? Pour ce que j'ai compris de ton example,
tu dois de toutes façons mettre les pre/post dans la docstring, non ?

Une autre solution serait, lors de l'import de chaque module, de faire
qqchose
du type :
module.my_func = decorate(module.my_func)


J'allais justement y venir... Regarde la doc de __import__ (dans les
builtins).