Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Générer automatiquement des classes en fonction d'une liste python 2.7

+2 votes

Je souhaite créer de très nombreuses classes automatiquement. Le nom de la classe, et certains éléments internes seraient variables en fonction, par exemple, des éléments d'une liste.

Plus habitué à coder en C, j'ai cherché à définir des macros, mais cela ne semble pas exister en python. J'ai également regardé du côté des métaclasses, et là également j'ai eu certains problèmes (je n'ai notamment pas compris comment faire varier le nom des classes).

Un peu de code pour illustrer ce que je cherche.

On aurait une liste et x set de data:


listeACreer = [
Classe1
Classe2
Classe3
...
] dataClasse1 = (1, 8, 9, 12)
dataClasse2 = (5, 5, 9, 6)
...

Et ça crée automagiquement:


class JeSuisClasse1():
name = "Coucou je suis Classe1 :D"
argClasse1 = 1
data = dataClasse1 class JeSuisClasse2():
name = "Coucou je suis Classe2 :D"
argClasse2 = 2
data = dataClasse2 ...

Et on utilise ça à la manière habituelle:


a = JeSuisClasse1()
a.name
"Coucou je suis Classe1 :D"
demandé 6-Avr-2016 par Nathan

Regarde du coté de type()

ca prend en paramétre le nom de ta classe (ce que tu souhaite modifier),
les parents (dans ton cas il n'y en à pas),
et le dictionnaire des méthodes (le constructeur qui du coup, necessite ta liste)

http://sametmax.com/le-guide-ultime-et-definitif-sur-la-programmation-orientee-objet-en-python-a-lusage-des-debutants-qui-sont-rassures-par-les-textes-detailles-qui-prennent-le-temps-de-tout-expliquer-partie-8/

http://deusyss.developpez.com/tutoriels/Python/heritage_metaclasse/

Je pense qu'il serait plus simple pour toi de stocker les attributs dans la liste de création sous la forme d'un dict par exemple:

classes_a_creer = {"classA": {"__init__": lambda self: None, 'ma_liste': ['a']},
                   "classB": {"__init__": lambda self: None, 'ma_liste': ['b']},
                   "classC": {"__init__": lambda self: None, 'ma_liste': ['c']}}

classes_definitions = {key: type(key, (), value) for key, value in classes_a_creer.items()}

# Maintenant on va toutes les instancier et imprimer la valeur de ma_liste:                                                                            
for key, value in classes_definitions.items():
    print("On instancie {}".format(key))
    test = value()
    print("ma_liste -> {}\n".format(test.ma_liste))

ce qui donne :

On instancie classC
ma_liste -> ['c']

On instancie classB
ma_liste -> ['b']

On instancie classA
ma_liste -> ['a']

Pourrais-tu en dire plus sur le problème que tu cherches à résoudre s'il te plait. C'est assez curieux comme requête, et je me demande s'il ne s'agit pas d'un problème XY.

@rm_ass: un __init__ qui ne fait rien est parfaitement inutile.

@yoch: Effectivement il me semblait pourtant obligatoire de le déclarer mais on peut s'en passer :)

Le use-case m'intérresse également.

En fait je souhaite implémenter un protocole dans scapy. J'ai découpé ce protocole en paquets et sous paquets, et l'utilisation commence à être vraiment lourdingue car de nombreuses combinaisons et donc de nombreuses valeurs à connaitre. J'essaie donc de découper certaines sous parties non pas en décrivant le paquet seulement selon sa structure, mais également selon certaines valeurs définissant un paquet avec une fonction bien précise.

Je ne sais pas si je suis très clair :p Mais en gros, je souhaite une utilisation:

myPacket = MyProtocoleHeader() / Classe1() / Classe1Fonction1()
myPacket = MyProtocoleHeader() / Classe2() / Classe2Fonction4()

Ou Classe1, Classe1Fonction1… ont des noms très parlants pour les futurs utilisateurs de mon outil.

Beaucoup plus simple que :
myPacket = MyProtocoleHeader() / ClasseGenerique(function=0x28, subfonction=0x1)

Je peux également passer par une classe générique que je vais instancier moi-même pour chaque fonction :

Classe1 = ClasseGenerique(function=0x28, subfonction=0x1)
Classe2 = ClasseGenerique(function=0x27, subfonction=0x1)
…

Mais on en revient à une question semblable :
Comment automatiser ces instanciations ?

Je récupère ces valeurs dans un fichier que je découpe. Et je souhaite me baser dessus pour construire mes classes ou mes instanciations d'une classe générique.

Je pense que ma question peut se résumer à:
Peut-on en python manipuler et travailler sur les noms des variable que l'on va instancier ? Ou doit-on obligatoirement poser explicitement, en dur, le nom de cette variable lors de la déclaration ? (nomVar = 1)

Si tu cherches simplement à lier automatiquement les classes que tu crée dynamiquement (par exemple avec type), il te suffit d'employer vars(), comme ceci par exemple :

vars()[clsname] = type(clsname, bases, attrs)

Mais je suis toujours aussi peu convaincu par la pertinence de la création de multiples classes. Ne serait t'il pas plus simple de se contenter d'instances d'une classe générique, qui seraient directement manipulées par les utilisateurs ? Et puis si certaines classes représentent des fonctions, pourquoi ne faire les choses naturellement ? Un truc comme ça ou quelque chose d'approchant :

myPacket = MyProtocoleHeader() / Instance1 / Instance1.Fonction1()
myPacket = MyProtocoleHeader() / Instance2 / Instance2.Fonction4()

3 Réponses

+3 votes

La fonction type permet de définir un type, c'est-à-dire une nouvelle classe:

>>> type('One', (), {})
<class '__main__.One'>

Le second argument est un tuple de base classes, et le troisième le dictionnaire des attributs.

Voilà une génération de deux classes et de leurs attributs:

>>> templates = {'A': {'name': 'test'}, 'B': {'name': 'oulala'}}
>>> for name, attrs in templates.items():
        print(type(name, (), attrs))

Dans le cas où tu aurais une liste de noms et une liste de liste d'attributs:

>>> class_names = ('A', 'B')
>>> class_attrs = ({'name': 'coucou'}, {'name': 'test'})
>>> classes = [type(name, (), attrs) for name, attrs in zip(class_names, class_attrs)]

Une autre solution, probablement plus propre, est d'utiliser la stdlib, et la structure namedtuple, qui est justement adaptée à la création de classes avec des attributs et des noms définits dynamiquement.

>>> from collections import namedtuple
>>> A = namedtuple('A', ['name', 'value'])
>>> a = A('coucou', 42)
>>> a.name, a.value
('coucou', 42)

Pour gérer les valeurs par défaut:

>>> A.__new__.__defaults__ = 'plop', 0
>>> a = A()
>>> a.name, a.value
('plop', 0)
répondu 6-Avr-2016 par lucas (2,204 points)
edité 6-Avr-2016 par lucas
+2 votes

Juste au cas où ça réponde aussi au problème de @Nathan, je propose de prendre la situation autrement. Une seule classe mais avec plein d'instance.

class GrandBazar(object):
    def __init__(self, name, **kwargs):
        self._name = name
        self.__dict__.update(kwargs)

    @property
    def name(self):
        return self._name

A l'usage :

liste_de_truc = [GrandBazar(nom, data=donnnee)
                 for (nom, donnee) in zip(liste_de_nom, liste_de_donnee)]
x = liste_de_truc[0]
x.name  # -> nom 1
x.data  # -> donnee 1
répondu 9-Avr-2016 par bubulle (2,066 points)

Ce qui semble plus malin et plus propre que définir moult classes différentes.

–1 vote

Merci ! C'est exactement ce dont je cherchais !

Je vais également voir pour utiliser une méthode peut-être plus simple. Plusieurs pistes ont été évoqué ici.

répondu 14-Avr-2016 par anonyme
...