Dataclasse: sa liste indexée d'objets, et brider le nombre max d' instances

a marqué ce sujet comme résolu.

Salut, avec les classes habituelles on peut par exemple brider à 5 le nombre d' instances d’une classe comme ceci:

MAX_COUNT = 5    # pour l'exemple

class X:
    counter = 0    # variable de classe
    def __new__(cls):
        if X.counter < MAX_COUNT:
            X.counter += 1
            return super().__new__(cls)
        else:
            raise RuntimeError('maximum instances exceded')

Comment fait on pour obtenir le self.id (ordre chronologique d' instanciation donc il faudrait l’index d’une liste des instances) et brider le nombre maximum d' instances lorsqu’il s’ agit d’une dataclasse ?

+0 -0

Salut,

Ben… self.counter est directement le nombre que tu cherches. Ça marche parce que par défaut, rechercher un attribut non présent dans l’instance va faire la recherche dans la classe elle-même (puis remonte le mro). Note que ton implémentation est pas terrible en l’état, on peut simplement changer la valeur de X.counter…

et brider le nombre maximum d' instances lorsqu’il s’ agit d’une dataclasse ?

Pareil, pas sûr de comprendre ce qui pose problème, si tu fais une sous-classe de dataclass et implémente __new__ comme dans ton exemple, ça devrait marcher.

+0 -0

Salut Adri1,

#! /usr/bin/env python3
# -*- coding: utf-8 -*-

from dataclasses import dataclass

MAX_COUNT = 5    # pour l'exemple

class X:
    counter = 0    # variable de classe
    listobj = []
    def __new__(cls):
        if X.counter < MAX_COUNT:
            X.counter += 1

            newobj = super().__new__(cls)
            X.listobj.append(newobj)
            return newobj
        else:
            raise RuntimeError('maximum instances exceded')

    def __init__(self):
        self.ident = len(X.listobj) -1 #index
        #self.ident = X.counter #position

x0, x1, x2, x3, x4 = X(),X(),X(),X(),X()

for obj in X.listobj:
    print("\n  ",obj, "   ident: ",obj.ident)


@dataclass
class Achat(X):
    produit: str
    prix: float
    quantite: int = 0

a0 = Achat('truc', 3.0, 2)
a1 = Achat('machin', 7.0, 5)
a2 = Achat('bidul', 9.0, 3)
a3 = Achat('foo', 1.0, 1)
a4 = Achat('bar', 4.0, 100)

print(a0,"  : ",a0.ident)
print(a1,"  : ",a1.ident)
print(a2,"  : ",a2.ident)

je voudrais boucler sur les Achat comme pour les objets X mais ça crash

` <main.X object at 0x7ff78c7a22b0> ident: 0

<main.X object at 0x7ff78c7a22e8> ident: 1

<main.X object at 0x7ff78c7a26a0> ident: 2

<main.X object at 0x7ff78c7a2710> ident: 3

<main.X object at 0x7ff78c723dd8> ident: 4

Traceback (most recent call last): File "test.py", line 42, in <module> a0 = Achat(’truc’, 3.0, 2) TypeError: new() takes 1 positional argument but 4 were given

`

+0 -0

Salut,

Tu te prends pas mal la tête

from dataclasses import dataclass

MAX_COUNT = 2


class BoundedNInstances:

    instances = []

    def __new__(cls, *_):
        if len(cls.instances) < MAX_COUNT:
            newobj = super().__new__(cls)
            cls.instances.append(newobj)
            return newobj
        else:
            raise RuntimeError('Too many instances')


@dataclass
class Product(BoundedNInstances):
    name: str
    price: float
    quantity: int


Product('milk', 2.4, 1)
Product('butter', 0.5, 2)
# Product('salt', 1.2, 1)  # raises RuntimeError

for product in Product.instances:
    print(product)

Ceci dit, je suis pas sûr de comprendre l’intérêt de ce que tu es en train de faire, quel est le vrai problème que tu essayes de résoudre ?

+1 -0

Salut,

Une chose aussi par rapport à ton code : quand tu écris X.counter += 1, c’est l’attribut de la classe X que tu incrémentes. Pas celui de la classe fille concernée.

L’attribut existera tout de même dans les classes filles (le mro faisant qu’il sera cherché dans toute l’ascendance), mais cela veut dire que le compteur sera partagé entre toutes.

C’est à dire que si en plus de ta classe Achat(X) tu avais une Vente(X), les deux pointeraient sur le même compteur. Est-ce bien le comportement que tu veux ?
Sinon, il faudrait utiliser les attributs sur cls, qui référence la classe fille.

Merci pour vos réponses: j' essaie d' obtenir ce comportement mais je n’y arrive pas avec les dataclasses que j' apprecie:

a=Product('milk', 2.4, 1)
b=Product('butter', 0.5, 2)
print(a)

Product(ident=0, name=’milk’, price=2.4, quantity=1)

print(b)

Product(ident=1, name=’butter’, price=0.5, quantity=2)

+0 -0

Salut,

Très honnêtement, j’ai l’impression que tu te plantes dans ce que tu essayes de faire. Encore une fois, quel est le problème que tu essayes de résoudre ? Je ne parle pas de la réalisation de l’implémentation, je parle vraiment du problème lui-même. Il est extrêmement rare d’avoir besoin de limiter le nombre d’instances des classes et surtout de gérer ça au niveau de la classe elle-même. Donc mon petit doigt me dit que c’est ton design qui n’est pas bon.

+0 -0

Quel est le problème que tu essayes de résoudre ? Autrement dit, pourquoi penses tu avoir besoin de cet identifiant ?

Obtenir l’identifiant n’est vraiment pas compliqué, mais je ne pense pas que tu en ai besoin. Tu vas te retrouver avec du code à maintenir qui n’a en fait pas de raison d’être. Comme la classe avec des instances limitées. À part pour un exercice de style pour un cours (auquel cas c’est un peu à toi de te débrouiller, il y a largement ce qu’il faut pour répondre à ta question), on n’écrit pas du code à moins d’en avoir besoin.

+0 -0

J’ai besoin d’une référence sur mes objets créés dynamiquement

C’est encore une question de programmation ça, pas le problème que tu essayes de résoudre. Mais on avance un peu. Les références, tu les as déjà, elles sont contenues dans Product.instances… Pourquoi ajouter un mécanisme d’indices par-dessus ? Tu vas compliquer les choses.

+1 -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