Initialisation d'un compteur -python

Résolu
Mikzapad Messages postés 7 Date d'inscription dimanche 7 avril 2024 Statut Membre Dernière intervention 10 avril 2024 - Modifié le 8 avril 2024 à 14:24
mamiemando Messages postés 33081 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 27 avril 2024 - 11 avril 2024 à 15:48

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

A voir également:

7 réponses

jee pee Messages postés 39661 Date d'inscription mercredi 2 mai 2007 Statut Modérateur Dernière intervention 28 avril 2024 9 240
7 avril 2024 à 22:36

Bonjour,

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


0
Mikzapad Messages postés 7 Date d'inscription dimanche 7 avril 2024 Statut Membre Dernière intervention 10 avril 2024
7 avril 2024 à 22:53

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
Phil_1857 > Mikzapad Messages postés 7 Date d'inscription dimanche 7 avril 2024 Statut Membre Dernière intervention 10 avril 2024
8 avril 2024 à 10:29

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

mon_compteur == 0

mais 

mon_compteur = 0
0
jee pee Messages postés 39661 Date d'inscription mercredi 2 mai 2007 Statut Modérateur Dernière intervention 28 avril 2024 9 240
7 avril 2024 à 23:42

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

Pour initialiser une variable, il faut écrire:

variable = valeur

et non:

variable == vvaleur

0
Mikzapad Messages postés 7 Date d'inscription dimanche 7 avril 2024 Statut Membre Dernière intervention 10 avril 2024
8 avril 2024 à 10:05

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
jee pee Messages postés 39661 Date d'inscription mercredi 2 mai 2007 Statut Modérateur Dernière intervention 28 avril 2024 9 240 > Mikzapad Messages postés 7 Date d'inscription dimanche 7 avril 2024 Statut Membre Dernière intervention 10 avril 2024
8 avril 2024 à 10:37

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
mamiemando Messages postés 33081 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 27 avril 2024 7 749
8 avril 2024 à 15:01

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
Mikzapad Messages postés 7 Date d'inscription dimanche 7 avril 2024 Statut Membre Dernière intervention 10 avril 2024
8 avril 2024 à 16:53

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
mamiemando Messages postés 33081 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 27 avril 2024 7 749 > Mikzapad Messages postés 7 Date d'inscription dimanche 7 avril 2024 Statut Membre Dernière intervention 10 avril 2024
Modifié le 8 avril 2024 à 17:12

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

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Mikzapad Messages postés 7 Date d'inscription dimanche 7 avril 2024 Statut Membre Dernière intervention 10 avril 2024
8 avril 2024 à 22:26

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
Diablo76 Messages postés 155 Date d'inscription vendredi 25 novembre 2022 Statut Membre Dernière intervention 27 avril 2024 40
9 avril 2024 à 06:22

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
Mikzapad Messages postés 7 Date d'inscription dimanche 7 avril 2024 Statut Membre Dernière intervention 10 avril 2024 > Diablo76 Messages postés 155 Date d'inscription vendredi 25 novembre 2022 Statut Membre Dernière intervention 27 avril 2024
9 avril 2024 à 23:34

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
Diablo76 Messages postés 155 Date d'inscription vendredi 25 novembre 2022 Statut Membre Dernière intervention 27 avril 2024 40 > Mikzapad Messages postés 7 Date d'inscription dimanche 7 avril 2024 Statut Membre Dernière intervention 10 avril 2024
10 avril 2024 à 00:05

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
Whismeril Messages postés 19032 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 28 avril 2024 931 > Diablo76 Messages postés 155 Date d'inscription vendredi 25 novembre 2022 Statut Membre Dernière intervention 27 avril 2024
10 avril 2024 à 07:40

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
yg_be Messages postés 22733 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 29 avril 2024 1 477
10 avril 2024 à 09:25

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
Mikzapad Messages postés 7 Date d'inscription dimanche 7 avril 2024 Statut Membre Dernière intervention 10 avril 2024
10 avril 2024 à 15:23

Bonjour

Bonne nouvelle 

Mon probleme est resolu par un mix de vos suggestions

Merci a vous tous de votre aide

mik

0
mamiemando Messages postés 33081 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 27 avril 2024 7 749
11 avril 2024 à 15:48

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

0

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