Obtenir la métaclasse

a marqué ce sujet comme résolu.

Salut,

J'essaie de créer un décorateur ClassMethod qui s'applique sur la méthode d'une classe. Le but de ce décorateur est de faire en sorte que la méthode décorée ne puisse être appelée uniquement via MaClasse.methode_decoree et pas par instance_de_MaClasse.methode_decoree. Je pense faire ça en redéfinissant la métaclasse de la classe dans laquelle est définie la méthode (il est probable que mon objectif soit irréalisable). Pour cela j'ai besoin de récupérer la métaclasse de cette classe, et de l'affecter à une nouvelle.

Merci d'avance,

Alpha.

+0 -0

Pas compris.... Désolé, mais je préfère vérifier. Là on parle de POO, conceptuellement qu'est-ce que tu veux faire ?

Qu'est-ce que la syntaxe classmethod est censé résoudre, apporter, simplifier dans ton problème ?

Je dis pas que ça n'a pas d'intérêt, c'est juste que je voudrais savoir ce que tu comptes faire avec, car ce que tu écris en haut n'a aucune cohérence avec l'objectif de cette syntaxe.

Donc, si tu nous en disais plus ;)

Salut fred1599,

L'idée ici est de progresser en POO, en particulier sur les métaclasses et les décorateurs. Il n'y a aucun intérêt et cela n'est censé résoudre aucun problème si ce n'est une syntaxe différente. L'objectif ultime serait:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Patate:
    def __init__(self, nom, age):
        self.nom, self.age = nom, age
    def __repr__(self):
        return "Bonjour je suis {nom} et j'ai {age} an{s}".format(nom=self.nom, age=self.age, s='' if self.age <= 1 else 's')
    @ClassMethod
    def foo(bar): 
        return bar * 2

p = Patate('Jean-Eudes', 42)
p.foo(2)
# AttributeError: foo is not defined.
Patate.foo(5)
# 10

Ça n'a probablement pas beaucoup de sens, excuse moi si je manque de clarté.

Bon malgré tout, avant de connaître une syntaxe, il est bon d'en connaître l'intérêt, car apprendre une syntaxe est inutile, surtout quand on sait utiliser une documentation.

classmethod est souvent utilisé lorsqu'on souhaite créer une nouvelle instance de la classe donnée.

Exemple, je l'utilise actuellement pour une classe Contact, un exercice qui se fait actuellement sur ce forum avec ma méthode fromlist.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Contact:

    contacts = []

    def __init__(self, nom=None, prenom=None, telephone=None):

        self.nom = nom
        self.prenom = prenom
        self.telephone = telephone

    @classmethod
    def fromlist(cls, keys):
        if len(keys) == 3:
            a, b, c = keys
            a, b = a.lower(), b.lower()
            contact = cls(nom=a, prenom=b, telephone=c)
            cls.contacts.append(contact)
            return contact

Comme tu le vois, ma méthode prend en paramètre la classe donnée et en retourne son instance.

C'est un peu l'équivalent de la classe UserDict avec sa méthode fromkeys.

Je peux initialiser mes contacts sous cette forme

1
2
3
c1 = Contact.fromlist(["Toto", "tata", "0123456789"])
c2 = Contact.fromlist(["Titi", "toto", "0123654789"])
c3 = Contact.fromlist(["Toto", "titi", "0321456987"])

Voilà, en espérant t'avoir ouvert les yeux sur l'intérêt premier de ce décorateur.

+0 -0

@psycopy: Le fait que mon décorateur et le builtin classmethod aient le même nom est un pur hasard. L'idée de mon décorateur serait de transférer la méthode décorée vers la métaclasse, sans laisser aucune trace derrière elle. Si une classe Personne possède une métaclasse par défaut (object il me semble), alors ces deux codes sont équivalent:

1
2
3
4
5
6
7
class Personne:
    def __init__(self, nom):
        self.nom = nom
   @ClassMethod
   def display(*people)
       for person in people:
           print(person)

et

1
2
3
4
5
6
7
8
class PersonneMetaclasse:
    def display(cls, *people):
        for person in people:
           print(person)

class Personne(metaclass=PersonneMetaclasse):
    def __init__(self, nom):
        self.nom = nom

Oui oui, ça sert à rien, mais c'est pour le "lol" comme disent les djeun's.

+0 -0

@psycopy

Euh mais où a-t-il participer?

Le fait que mon décorateur et le builtin classmethod aient le même nom est un pur hasard. L'idée de mon décorateur serait de transférer la méthode décorée vers la métaclasse, sans laisser aucune trace derrière elle. Si une classe Personne possède une métaclasse par défaut (object il me semble), alors si on poursuit ton exemple le code suivant:

Si on parlait conception, j'ai rien compris…

Il me semble, je dis bien il me semble… que tu veuilles modifier une fonction built-in, ce qui je pense n'est pas possible. Ça serait équivalent à modifier le contenu de la valeur du dictionnaire locals() ayant comme clé 'builtins'. La documentation semble claire en indiquant qu'il n'est pas possible de modifier le contenu de ce dictionnaire.

Maintenant tu parles de métaclasse, je dirais ce qu'un grand codeur dit souvent de ces métaclasses.

Metaclasses are deeper magic than 99% of users should never worry about. If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why).

Python Guru Tim Peters

+0 -0

@fred1599: Trompé de loup :D Sinon on peut bien recréer le décorateur property, alors pourquoi pas classmethod ? C'est exactement ce qui m'attire en Python: on peut recréer presque n'importe quel mécanisme builtin. De toute façon mon décorateur n'a aucune utilité, c'est pour le fun et pour progresser, et il ne conserve pas exactement les mêmes propriétés que son homologue builtin. L'utilisation des métaclasses est sûrement inapproprié mais c'est leur côté intrigant qui m'a conduit jusqu'à elles.

Je pense que tu as mal compris l'utilité des métaclasses. Si tu déplaces la méthode display vers la métaclasse, tu n'auras plus aucun moyen d'y accéder depuis la classe.

Edit: Enfin si, en passant par type, mais bon.

Juste pour information, les métaclasses sont des sous classes de Type. Avec la méthode spéciale __class__ tu peux vérifier que ta classe hérite bien de type

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
>>> class A:
...     def __init__(self):
...         pass
... 
>>> a = A()
>>> type(a)
<class '__main__.A'>
>>> a.__class__
<class '__main__.A'>
>>> type(A)
<class 'type'>
>>> A.__class__
<class 'type'>

Peut-être que ça te débloquera dans ce que tu souhaites, car j'ai du mal à cerner…

Je pense que tu as mal compris l'utilité des métaclasses.

C'est fort possible ._. Mais, si on reprend mon code un peu plus haut, et que je l'exécute, ça donne:

1
2
3
4
5
6
7
>>> Classe.display('Patrick', 'Patrock', 'Patruck')
Patrick
Patrock
Patruck
>>> c = Classe('Patrick')
>>> c.display('Patrick', 'Patrock', 'Patruck')
AtributeError: bla bla bla

C'est exactement le comportement que je veux.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte