Initialisation d'un compteur -python

Résolu

Bonjour,

Je suis en train de terminer un programme en python mais j'ai un souci :

Tout au long de mon programme, j'ai une série de fonctions qui mettent à jour un compteur.

Bien sûr, au début du programme, je ne lance aucune fonction et mon compteur n'affiche RIEN ; comment faire pour qu'au tout début il affiche -0- au lancement ??

Merci beaucoup
Windows / Opera 108.0.0.0

7 réponses

  1. Modérateur

    Bonjour,

    Dans ton programme au tout début tu dois l'initialiser à zéro, à ce moment là affiche le.


    0
    1. bonsoir  & merci

      oui  bien sur , c'est ce je cherche a faire 

      genre:  mon_compteur == 0

      Si j initialise au tout debut ; c'est a dire hors fonction , il l'ignore ;

      dans les fonctions il n'a rien a y faire ...

      a+

      0
    2. @Mikzapad

      Par contre, pour l'initialiser à 0, ce n'est pas

      mon_compteur == 0

      mais 

      mon_compteur = 0
      0
  2. Modérateur

    tu pourrais rajouter un fonction qui affiche la valeur du compteur au départ,

    ou tu donnes le source de ton programme pour que l'on jette un oeil


    0
    1. Pour initialiser une variable, il faut écrire:

      variable = valeur

      et non:

      variable == vvaleur

      0
      1. hello jee pee -Justement,  je ne veux pas de fonction que je devrai ensuite declancher par un bouton ou autre ; Il faut que ce compteur s'initalise de lui meme au demarrage - c est a dire qu'il affiche 0

        0
      2. Modérateur
        @Mikzapad

        le compteur ne s'initialise pas de lui même, c'est le programme qui le fait, pareil pour l'affichage ;-)

        Donne nous un extrait simple de ton code, mais fonctionnel, peut être avec juste la première fonction qui agit sur le compteur.

        0
    2. Modérateur

      Bonjour,

      Il faudrait nous dire ce que tu comptes. Tu risques de devoir passer ton compteur en paramètre de plein de fonctions, ce qui à terme risque d'être lourd.

      Variable globale

      Une solution simple, mais très sale, consisterait à créer ton compteur en variable globale. Ton programme initialise cette variable globale au démarrage et ensuite tes autres fonctions accèdent à ce compteur.

      compteur = 0
      
      def f():
          global compteur
          print("f", compteur)
          compteur += 1
      
      def g():
          global compteur
          print("g", compteur)
          compteur += 1
      
      def main():
          for _ in range(3):
              f()
          for _ in range(2):
              g()
          print("compteur", compteur)
      
      if __name__ == "__main__":
          main()

      Pourquoi les variables globales sont sales :

      • Dans le cas général, on ne sait pas d'où elles proviennent (qui les déclare).
      • Leur nom peut entrer en collision avec d'autres variables qui n'ont rien à voir.
      • Si on fait de la programmation concurrente, on peut avoir des accès concurrents qui entraînent des effets de bords pernicieux et des bogues difficiles à résoudre.
      • Dans d'autres langages que python (ou des versions anciennes de python), l'absence du mot clé global ne permet pas de distinguer les variables globales des variables locales.

      Singleton

      Une solution plus propre, mais plus compliquée à écrire, consisterait à utiliser un singleton. Un singleton garantit qu'une classe ne peut être instanciée qu'au plus une fois. Si on tente de la ré-instancier, on récupère l'instance déjà existante. Ce serait typiquement le cas de ton compteur.

      Cela nous mène par exemple à ce code :

      class Singleton(type):
          """
          The :py:class:`Singleton` allows to define singleton
          classes, i.e. classes that can be instantiated at most once.
      
          >>> class MyClass(metaclass=Singleton): pass
          >>> x = MyClass()
          >>> y = MyClass()
          >>> x is y
          True
      
          Based on this `thread
          <https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python>`__.
          """
          s_instances = dict()
      
          def __call__(cls, *args, **kwargs):
              """
              Functor method.
      
              Args:
                  cls: The class using the singleton pattern.
                  *args: The args of the class constructor
                       of ``cls``.
                  **kwargs: The kwargs of the class
                       constructor of ``cls``.
      
              Returns:
                  The singleton corresponding to ``cls``.
              """
              if cls not in cls.s_instances:
                  cls.s_instances[cls] = (
                  super(Singleton, cls).__call__(*args, **kwargs)
              )
              return cls.s_instances[cls]
      
      class Compteur(metaclass=Singleton):
          def __init__(self, valeur_initiale: int = 0):
              print("init")
              self.compteur = valeur_initiale
      
          def __iadd__(self, i: int):
              self.compteur += i
              print("iadd", self.compteur)
      
          def __int__(self) -> int:
              return self.compteur
      
          def __str__(self) -> str:
              return str(self.compteur)
      
      def f():
          compteur = Compteur(0)
          print("f", compteur)
          compteur += 1
      
      def g():
          compteur = Compteur(0)
          print("g", compteur)
          compteur += 1
      
      def main():
          for _ in range(3):
              f()
          for _ in range(2):
              g()
          print("compteur %d" % int(Compteur()))
      
      if __name__ == "__main__":
          main()
      

      Quelques explications :

      • La classe Singleton est assez technique, c'est ce qu'on appelle en python une métaclasse. Elle est implémentée ici de sorte à garantir que les classes qui l'exploitent deviennent des singletons.
        • Cette classe maintient un dictionnaire qui associe à chaque type estampillé Singleton son instance correspondante, si elle existe déjà en mémoire.
        • La méthode __call__ est utilisé pour définir le comportement de l'opérateur (), ici utilisé en interne pour créer une instance de classe estampillée singleton. C'est donc au travers de cette méthode qu'on teste l'existence si pour le type qui nous intéresse, il existe déjà une instance en mémoire. Si c'est le cas, on la retourne. Sinon on la crée et on la retourne.
      • La classe Compteur implémente notre compteur. C'est un singleton qui supporte un certain nombre d'opération au travers de ses méthodes :
        • __init__ : création du compteur, avec une valeur initiale par défaut de 0
        • __iadd__ : support de l'opération +=, pour l'incrémenter
        • __str__ : conversion sous forme de chaîne (typiquement via la fonction str(), implicitement utilisée lors d'un appel à la fonction print)
        • __int__ : conversion sous forme d'un entier (typiquement via la fonction int())
      • On voit que chaque fois qu'on a besoin du compteur, on "crée" le compteur sans se préoccuper de savoir s'il existe ou pas. J'aurais d'ailleurs pu simplement écrire :
        compteur = Compteur()

        ... quand j'en ai besoin, puisque dans __init__, j'ai écrit que la valeur par défaut est 0.

      • Quand on veut incrémenter le compteur, on utilise l'opérateur usuel += (ce qui permet de traverser __iadd__)

      • Quand on veut récupérer la valeur du compteur, on le transforme simplement en entier.

      Pourquoi un singleton est préférable

      • Pas de risque de collision : la classe est clairement identifiée
      • On peut facilement déterminer où la classe est utilisée (voir des outils comme grep)
      • On peut facilement modifier le code au niveau de la classe (ici Compteur) pour mettre en place des sections critiques en cas d'accès concurrents.

      Compteur d'instance

      Dans le cas très particulier où tu veux compter le nombre de fois qu'une classe a été instanciée, il est possible d'avoir un compteur commun à toutes ces instances de classe.

      class Personne:
          compteur = 0
      
          def __init__(self, prenom: str, nom: str):
              Personne.compteur += 1
              self.prenom = prenom
              self.nom = nom
      
      def main():
          print(Personne.compteur)  # Affiche 0
          personne1 = Personne("Luke", "SKywalker")
          print(Personne.compteur)  # Affiche 1
          personne2 = Personne("Han", "Solo")
          print(Personne.compteur)  # Affiche 2
      
      if __name__ == "__main__":
          main()

      Bonne chance

      0
      1. hello

        merci bien de ta réponse

        je vais essayer de la comprendre ...

        En fait dans un cadre de cryptographie je fais subir à une chaine de caractère une série de fonctions, qui chacune applique un traitement particulier ex : majuscule/minuscule/sans accents/sans ponctuation /concaténation /fractionnement par 5 caracteres ....;Etc ;

        A la fin de chaque fonction , je compte le nombre de caracteres que j'envoie a une dernière fonction qui affiche le nombre de caracteres  dans un label-- Mais quand je démarre le pgm , aucune chaine de caracteres n'est encore entrée - le programme m'affiche :

        "nombre de caracteres =    " comment faire pour y mettre un sacré  zero "0" au lancement  dans la variable qui suit le label ???

        Ca a l'air bete , mais tres enervant !

        zap

        0
      2. Modérateur
        @Mikzapad

        Bonjour,

        Je vais faire une réponse assez générale puisque tu ne partages pas ton code actuel.

        Si tu reprends les solutions que je t'ai proposées :

        • Approche avec une variable globale : dans le premier extrait de code que j'ai proposé (voir #8) :
          • L'initialisation est clairement faite à la déclaration du compteur.
          • L'utilisation (du compteur) nécessite de re-déclarer la variable globale à l'aide du mot clé global (sans quoi python croit que tu parles d'une variable locale qui n'existe pas). Ensuite, tu incrémentes ton compteur de la valeur de ton choix : appeler compteur += i est équivalent à écrire compteur = compteur + i
        • Approche avec un singleton : l'initialisation se fait lors de la première construction. L'utilisation est conforme à ce que j'ai expliqué pour l'approche par variable globale.
        • La troisième approche ne correspond pas à ton besoin, oublie-la.

        Par contre on peut proposer une quatrième approche qui s'en inspire et qui est sans doute la plus indiquée par rapport à ce que tu décris :

        • Approche avec un compteur interne à l'instance de classe (self) : Les fonctions dont tu parles ne font pas forcément partie d'une même classe, tu ne peux pas forcément reproduire la même approche. En admettant que tu sois prêt à le réorganiser, ton code ça pourrait ressembler à ceci (et les méthodes f et g font ce que tu veux en plus de mettre à jour le compteur) : 
          class Encoder:
              def __init__(self):
                  self.compteur = 0  # Initialisation
              def f(self):
                  self.compteur += 5
                  print(encoder.compteur)
              def g(self):
                  self.compteur += 10
                  print(encoder.compteur)
          
          
          encoder = Encoder()
          encoder.f()  # Affiche 5
          encoder.g()  # Affiche 15

        Bonne chance 

        0
    3. bonsoir 

      Je ne rechigne pas a envoyer mon source , y a t il une procedure particuliere ??

      en complement de mon explication ,  il ne s'agit pas d'une incrementation de compteur .

      Chaque fonction récupere un string , la manipule . puis je recupere sa longueur "ln"

      et je l'expedie a la fonction compte par :  compte(ln) .

      chaque fonction envoie un nombre different , il y a remplacement et non incrementation

      bonne nuit  ; j'etudie vos scripts merci

      0
      1. Salut,

        Puisque l'affichage ce fait dans un label, qu'est-ce qui t'empêche de saisir un texte à la création de celui-ci.

        import tkinter as tk
        
        root = tk.Tk()
        
        label = tk.Label(root, text="Nombre de caractères = 0", width=25)
        label.pack()
        
        root.mainloop()
        0
      2. @Diablo76

        Bonsoir 

        Oui mais le compteur  est updaté a chaque fonction - si on l'ecrit en dur ca ne marche pas - 

        A la fin de chacune des fonctions de mon pgm  je compte le nombre de caraxteres

        laz = len ( string)      et je l envoie a mon compteur avec l'affichage avec la commande :   compte(laz)

        ===========================

        fonction compteur :

        def compte(cnt):

        Lab3.configure(text="Nombre de caracteres = " + str(cnt) )

        voila

        0
      3. @Mikzapad

        Même si c'est écrit en dur au moment de l'initialisation de ton label, je ne vois pas pourquoi l'update ne marcherait pas.

        import tkinter as tk
        
        def compte(cnt):
            label.configure(text="Nombre de caracteres = " + str(cnt))
        
        root = tk.Tk()
        
        label = tk.Label(root, text="Nombre de caractères = 0", width=25)
        label.pack()
        compte(100)
        
        root.mainloop()
        0
      4. @Diablo76

        Bonjour

        au pire

        import tkinter as tk
        
        def compte(cnt):
            label.configure(text="Nombre de caracteres = " + str(cnt))
        
        root = tk.Tk()
        
        compte(0)
        label.pack()
        compte(100)
        
        root.mainloop()
        0
      5. Contributeur

        bonjour,

        la procédure pour envoyer une source:

        • soit tu cliques sur le bouton "insérer un extrait de code" quand tu tapes une réponse
        • soit tu mets ton fichier source à disposition sur Internet, et tu partages un lien vers ce fichier
        0
    4. Bonjour

      Bonne nouvelle 

      Mon probleme est resolu par un mix de vos suggestions

      Merci a vous tous de votre aide

      mik

      0
      1. Modérateur

        Bonjour, pour rappel, tu peux basculer le sujet en résolu comme expliqué ici.

        0
    5. Bonjour.

      Il serait judicieux d'utiliser une variable tkinter si cela est destiné à être un compteur.

      Vairiable qui serait partagée par les différentes fonctions.

      Exemple très basique :

      import tkinter
      
      window = tkinter.Tk()
      for i in range(2):
          window.columnconfigure(i, weight=1)
      
      count = tkinter.IntVar()
      label = tkinter.Label(window, text="Nombre de caracteres =", font=('', 30))
      label.grid(row=0, column=0, sticky=tkinter.E)
      label = tkinter.Label(window, width=2, textvariable=count, font=('', 30))
      label.grid(row=0, column=1, sticky=tkinter.W, padx=(5, 0))
      
      def funct_1():
          # pouf +1
          count.set(count.get() + 1)
      
      def funct_5():
          # pouf + 5
          count.set(count.get() + 5)
      
      def test():
          import random
          for i in range(1, 11):
              window.after(1000 * i, random.choice((funct_1, funct_5)))
      
      test()
      
      window.mainloop()
      0