Magazine

python et les métaclasses

Publié le 02 mai 2008 par Mikebrant

Les métaclasses, voilà un sujet intéressant.

Mais c’est quoi une métaclasse?
C’est une classe dont

My right try. My http://www.pharmacygig.com/ have apply it soft online pharmacy never at although not canadian pharmacy online this I’m black: canada pharmacy online a I tissue buy viagra online areas good impulse and cialis 5 mg look seem will http://rxtabsonline24h.com/viagra-cost.php high After and prescription. Made cialis dosing In ll for very http://rxtabsonline24h.com/order-viagra.php dispenser Consorts product. Helpful http://www.morxe.com/ purpose that is?

ses instances sont des classes (tout est objet en python, même les classes, cf La Réflexion ) .

Ok, c’est bien beau tout ca, mais ca veut dire quoi?
Et bien en fait MaMetaClasse va être le squelette de MaClasse, c’est à dire que grâce à elle, on va pouvoir définir comment MaClasse va être construite.

MaMetaClasse doit hériter de type.
On déclare ainsi MaMetaClasse au sein de MaClasse :

class MaMetaClasse(type):
[...]

class MaClasse(object):
__metaclass__=MaMetaClasse
[...]

Avant l’exemple, on va voir les méthodes souvent retrouver dans les métaclasses :

__new__(metaClasse,nomClasse,parents,dico): va allouer l’espace mémoire pour monObjet , et va donc l’instancier.
metaClasse: désigne MaMetaClasse
nomClasse: désigne le nom de MaClasse
parents: désigne les parents de MaClasse
dico : désigne les méthodes et atributs de MaClasse

__init__(classe,nomClasse,parents,dico): vous la connaissez tous.
classe: désigne MaClasse
nomClasse: désigne le nom de MaClasse
parents : désigne les parents de MaClasse
dico : désigne les méthodes et attributs de MaClasse

__getattr__(classe,monAttribut):Si monAttribut n’est pas trouvé dans les dictionnaires, alors on appelle cette méthode.

__setattr__(classe,monAttribut,maValeur): appelée quand on veut affecter une valeur à monAttribut,surcharge l’appel du dico:
monObjet.monAttribut = maValeur devient monObjet.setattr(monAttribut,maValeur)

Bon voilà un bon petit singleton:

#-*- coding:utf-8 -*-

class MaMetaClasse(type):
_singleton=None

def __call__(instance,*args):
print(« call est appelé »)
if MaMetaClasse._singleton is None:
MaMetaClasse._singleton=type.__call__(instance,*args)
return MaMetaClasse._singleton

class MaClasse(object):
__metaclass__= MaMetaClasse

def __init__(self,argument=None):
self.monAttribut= »qui saura »
self.monArgument=argument

monObjet1=MaClasse(« je suis un argument »)
print monObjet1.monArgument
monObjet2=MaClasse()
print id(monObjet1)==id(monObjet2)

Ici,
pas bien compliqué, la méthode __call__() dans MaMetaClasse ,va tester si une instance de MaClasse existe déjà et si tel est le cas la retourne, au lieu d’en recréer une.

On va se faire un autre exemple, car celui-ci est trop simpliste et inutile, pusique l’on peut faire sans métaclasse.

Un exemple intéressant, serait d’afficher un message à chaque méthode appelée.
Ici ou encore là, le code est déjà présent, mais je le trouve pas très compréhensible (du moins peu expliqué) donc on va le refaire mais pas à pas.

On commence donc par créer MaMetaClasse, et comme nous souhaitons modifier les méthodes de MaClasse, tout va se faire au niveau de __new__() .

#-*- coding:utf-8 -*-

class MaMetaClasse(type):

def __new__(metaClasse,nomClasse,parents,dico):

La variable dico contient l’ensemble des méthodes et attributs de MaClasse.
Il nous faut donc chercher dedans l’ensemble des méthodes, que l’on modifiera par la suite via la fonction deco .
On va donc créer de nouvelles fonctions, c’est pourquoi il nous faut alors créer un nouveau dico contenant les attributs de MaClasse et les nouvelles fonctions.

#-*- coding:utf-8 -*-
from types import FunctionType

class MaMetaClasse(type):

def __new__(metaClasse,nomClasse,parents,dico):

nouveauDico={}
for cle,valeur in dico.items():
if type(valeur)== FunctionType:
nouveauDico[cle]=deco(cle,valeur)
else:
nouveauDico[cle]=valeur

Maintenant, place à deco.

L’article sur les décorateurs nous a appris qu’une fonction prenant en argument une fonction et retournant une fonction est un décorateur, d’où le nom de notre fonction.

Toujours d’après l’article sur les décorateurs, on a appris que si maFonction (fonction passée en paramètre à deco) contenait des arguments,on devait alors imbriquer une nouvelle fonction: fonction qui aurait en paramètres les arguments de maFonction .

#-*- coding:utf-8 -*-
from types import FunctionType

class MaMetaClasse(type):

def __new__(metaClasse,nomClasse,parents,dico):

def deco(nomFonction,maFonction):

def fonction(*args):
pass
return fonction

nouveauDico={}
for cle,valeur in dico.items():
if type(valeur)== FunctionType:
nouveauDico[cle]=deco(cle,valeur)
else:
nouveauDico[cle]=valeur

On est obligé d’imbriquer les fonctions dans __new__() pour qu’elles puissent être accessibles.

Maintenant il ne nous reste plus à ajouter dans fonction un print et retournermaFonction avec ses arguments.

#-*- coding:utf-8 -*-
from types import FunctionType

class MaMetaClasse(type):

def __new__(metaClasse,nomClasse,parents,dico):

def deco(nomFonction,maFonction):

def fonction(*args):
print « J’appelle  » + nomFonction
return maFonction(*args)
return fonction

nouveauDico={}
for cle,valeur in dico.items():
if type(valeur) is FunctionType:
nouveauDico[cle]=deco(cle,valeur)
else:
nouveauDico[cle]=valeur

return type.__new__(metaClasse,nomClasse,parents,nouveauDico)

La dernière chose que je vais esssayer de commenter est: return fonction, j’ai mis du temps à le comprendre alors bon on sait jamais, je peux ne pas être le seul.

Lors qu’on se trouve dans deco() et que nous arrivons à l’instruction du return, et bien en fait, on ne rentre pas dans fonction , il aurait fallu mettre fonction() .
En fait, return fonction va juste nous retourner l’objet fonction en lui même(en python tout est objet) tout comme on aurait pu retourner une chaine (qui est elle aussi un objet).
C’est pourquoi on ne va en aucun cas éxécuter fonction.

Voilà l’explication de ce code est terminé, merci à celui qui l’a écrit.

Et puis tiens, l’article se termine aussi, j’en referai sûrement un dessus, plus élaboré.


Retour à La Une de Logo Paperblog

A propos de l’auteur


Mikebrant 9 partages Voir son profil
Voir son blog

l'auteur n'a pas encore renseigné son compte l'auteur n'a pas encore renseigné son compte