Forum Programmation.python Mon premier code python

Posté par  . Licence CC By‑SA.
Étiquettes : aucune
3
22
jan.
2019

Bonjour,

Voilà dans mon apprentissage de python, j'ai pondu ce code

import datetime
from tkinter import Entry, Button, Scrollbar
from tkinter import Listbox, StringVar, Tk
from tkinter import Label, LabelFrame, Menu, ANCHOR, E, N, S
from tkinter.colorchooser import askcolor
from tkinter.messagebox import showinfo
import os
import sys
import math
import pytz
###VARIABLES
obj = dict()
listtz = list(pytz.all_timezones)
BINPATH = os.path.dirname(os.path.realpath(__file__))
with open("{}/tz.conf".format(BINPATH), "r") as conffile:
    TIMEZONES = conffile.readlines()
    TIMEZONES = list(map(lambda s: s.strip(), TIMEZONES))
citytable = []
for element in TIMEZONES:
    tmpelement = element.split("/")
    try:
        citytable.append(tmpelement[::-1])
    except:
        pass
TIMEZONES = []
citytable.sort()
for element in citytable:
    element.reverse()
    TIMEZONES.append("/".join(element))
listtz = [x for x in listtz if x not in TIMEZONES]
print("Timezones initialization....OK")
colorsdic = dict()
try:
    with open("{}/colors.conf".format(BINPATH), "r") as CONFFILE:
        COLORS = CONFFILE.readlines()
        COLORS = list(map(lambda s: s.strip(), COLORS))
    for i in COLORS:
        j = i.split(":")
        colorsdic[str(j[0])] = (j[1])

except:
    with open("{}/colors.conf".format(BINPATH), "w") as f:
        f.write('bgcolor:bisque\nfgcolor:maroon\nbgclock:black\nfgclock:yellow\nbgday:white\nfgday:black\n')
    with open("{}/colors.conf".format(BINPATH), "r") as conffile:
        COLORS = conffile.readlines()
        COLORS = list(map(lambda s: s.strip(), COLORS))

    for i in COLORS:
        j = i.split(":")
        colorsdic[j[0]] = (j[1])
COLORDAY = [colorsdic['bgday'],colorsdic['fgday']]
COLORCLOCK = [colorsdic['bgclock'],colorsdic['fgclock']]
COLORFRAME = [colorsdic['bgcolor'],colorsdic['fgcolor']]

print("Colors initialization ...OK")
framewidth = 0
for element in listtz:
    lenframe = len(element)
    if lenframe > framewidth:
        framewidth = lenframe
i = 0
column = 1
row = 0
rowmax = 7
relief = 'sunken'
city = 'localtime'
bgcolor = '#AA0000'
fgcolor = 'yellow'
############CLASS
class clock:
    """
    Defines displays of the CLOCKS
    """
    def __init__(self,city,bgcol,fgcol,bgclock,fgclock,bgday,fgday,fontsize,column,row,relief):
        if  city == 'localtime':
            pass
        else:
            self.timezone = pytz.timezone(city)
        self.bgcol = bgcol
        self.fgcol = fgcol
        self.bgclock = bgclock
        self.fgclock = fgclock
        self.bgday = bgday
        self.fgday = fgday
        self.column = column
        self.row = row
        self.tz = city
        self.bgcol = bgcol
        self.fgcol = fgcol
        self.fontsize = fontsize
        if city == 'localtime':
            self.date = datetime.datetime.now()
        else:
            self.date = datetime.datetime.now(self.timezone)
        self.daybefore = int(self.date.day)
        self.monthbefore = int(self.date.month)
        self.yearbefore = int(self.date.year)
        self.Frame = LabelFrame(root,bg=self.bgcol,fg=self.fgcol,highlightbackground="yellow", highlightcolor="green", highlightthickness=2)
        self.temptable = []
        self.labeltext = StringVar()
        self.entrydatetext = StringVar()
        self.entryhourtext = StringVar()
        self.labelsecondtext = StringVar()
        self.display_cal()
        self.update_window()

    def display_cal(self):
        self.label = Label(self.Frame,textvariable=self.labeltext,width=15,height=int(framewidth/5),bg=self.bgcol,fg=self.fgcol)
        self.label.config(font=("URW Gothic", self.fontsize+2,"bold"))
        self.temptable = self.tz.split("/")
        if len(self.temptable) == 1:
            self.labeltext.set('Localtime')
        self.labeltext.set(self.temptable[-1].upper())
        self.entryhour = Entry(self.Frame,textvariable=self.entryhourtext,width=5,bg=self.bgclock,fg=self.fgclock,relief=relief)
        self.entrydate = Entry(self.Frame,textvariable=self.entrydatetext,width=10,bg=self.bgday,fg=self.fgday,relief=relief)
        self.labelsecond = Label(self.Frame,textvariable=self.labelsecondtext,bg=self.bgcol,fg=self.fgcol)
        self.entrydate.config(font=("Swiss 721",14))
        self.entryhour.config(font=("Serto Kharput",28,"bold"))
        self.labelsecond.config(font=("Console",8))
        self.label.grid(column=0,row=0,rowspan=2)
        self.entryhour.grid(column=1,row=0)
        self.entrydate.grid(column=1,row=1)
    def update_window(self):
        if self.tz == 'localtime':
            self.date = datetime.datetime.now()
        else:
            self.date = datetime.datetime.now(self.timezone)
        self.place = self.tz.upper()
        self.year = str(self.date.year)
        if int(self.year) > self.yearbefore:
            showinfo("HAPPY","Happy new year {}".format(self.place))
        self.yearbefore = int(self.date.year)
        self.month = str(self.date.month).zfill(2)
        if int(self.month) > self.monthbefore:
            showinfo("WARNING","The month has changed in {}".format(self.place))
        self.monthbefore = int(self.date.month)
        self.day = str(self.date.day).zfill(2)
        if int(self.day) > self.daybefore:
            showinfo("WARNING","The day has changed in {}".format(self.place))
        self.daybefore = int(self.date.day)
        self.hour = str(self.date.hour).zfill(2)
        self.minute = str(self.date.minute).zfill(2)
        self.second = str(self.date.second).zfill(2)
        self.calendar = "{}/{}/{}".format(self.day,self.month,self.year)
        self.time = "{}:{}".format(self.hour,self.minute)
        self.entrydatetext.set(self.calendar)
        self.entryhourtext.set(self.time)
        self.labelsecondtext.set(self.second)
        self.label.grid(column=0,row=0,rowspan=2)
        self.entryhour.grid(column=1,row=0)
        self.entrydate.grid(column=1,row=1)
        self.labelsecond.grid(column=2,row=0)
        self.Frame.grid(column=self.column,row=self.row)
        root.after(1000,self.update_window)
    def removeinstance(self):
        with open("{}/tz.conf".format(BINPATH), 'r') as f1:
            lines = f1.readlines()
        with open("{}/tz.conf".format(BINPATH), 'w') as f:
            for line in lines:
                if self.tz not in line:
                    f.write(line)
                f.truncate()
        os.execl(sys.executable, os.path.abspath(__file__), *sys.argv)
    def __str__(self):
        return(self)
#DEF
def addtz(str,column,row):
    locallisttz = []
    str=str+'/'
    OptionFrame.grid_forget()
    for widget in OptionFrame.winfo_children():
        widget.grid_forget()
    SetTzList.delete(0, 'end')
    LabelText.set('Add timezone')
    def CurSelet(evt):
        global row
        global column
        global COLORDAY
        global COLORFRAME
        global COLORCLOCK
        global relief
        if (row + column) %2 == 0:
            (bgday, fgday)=COLORDAY
            (bgcolor,fgcolor)=COLORFRAME
            (bgclock,fgclock)=COLORCLOCK
            relief='sunken'
        else:
            (fgday, bgday)=COLORDAY
            (fgcolor,bgcolor)=COLORFRAME
            (fgclock,bgclock)=COLORCLOCK
            relief='ridge'
        value = (SetTzList.get(ANCHOR))
        with open("{}/tz.conf".format(BINPATH), 'a') as f1:
            f1.write("{}{}\n".format(str,value))
        obj[value]=clock("{}{}".format(str,value),bgcolor,fgcolor,bgclock,fgclock,bgday,fgday,10,column,row,relief)
        DelTZ.add_command(label=value, command=obj[value].removeinstance)
        if row < rowmax:
            row += 1
        else:
            row = 0
            column += 1

    for element in listtz:
        locallisttz=[x for x in listtz if str in x]
    LabelTitle.grid(column=0, row=1)
    SetTzList.grid(column=0, row=2)
    table=[]
    for entries in locallisttz:
        table=entries.split('/')
        if len(table) > 2:
            SetTzList.insert("end", "{}/{}".format(table[1],table[2]))
        else:
            SetTzList.insert("end", table[1])
    value=SetTzList.get(ANCHOR)
    value=SetTzList.bind('<<ListboxSelect>>', CurSelet)
    scrollbar.grid(column=1,row=2,sticky=E+N+S)
    scrollbar.config( command = SetTzList.yview )
    SetTzList.config(yscrollcommand=scrollbar.set)
    ButtonCancel.grid(column=0,row=4,columnspan=2)
    OptionFrame.grid(column=0,row=0,rowspan=rowmax+2)
def SetColors():
    OptionFrame.grid_forget()
    for widget in OptionFrame.winfo_children():
        widget.grid_forget()
    SetTzList.delete(0, 'end')
    LabelText.set('Setcolors')
    OptionFrame.grid(column=0,row=0,rowspan=rowmax+1)
    def changecolor(key):
        newlist=[]
        (a, newcolor) = askcolor(color=colorsdic[key],title='Choose...')
        if newcolor:
            for oldkey, oldvalues in colorsdic.items():
                if oldkey == key:
                    oldvalues=newcolor
                newlist.append("{}:{}\n".format(oldkey,str(oldvalues)))
            with open("{}/colors.conf".format(BINPATH), 'w') as f1:
                f1.writelines(newlist)
            os.execl(sys.executable, os.path.abspath(__file__), *sys.argv)
    Buttonbg=Button(OptionFrame,text=colorsdic['bgcolor'],bg=colorsdic['bgcolor'],width=7,command=lambda: changecolor('bgcolor'))
    Buttonbg.grid(column=0,row=0)
    Buttonfg=Button(OptionFrame,text=colorsdic['fgcolor'],bg=colorsdic['fgcolor'],width=7,command=lambda: changecolor('fgcolor'))
    Buttonfg.grid(column=0,row=1)
    Buttonbgclock=Button(OptionFrame,text=colorsdic['bgclock'],bg=colorsdic['bgclock'],width=7,command=lambda: changecolor('bgclock'))
    Buttonbgclock.grid(column=1,row=0)
    Buttonfgclock=Button(OptionFrame,text=colorsdic['fgclock'],bg=colorsdic['fgclock'],width=7,command=lambda: changecolor('fgclock'))
    Buttonfgclock.grid(column=1,row=1)
    Buttonbgday=Button(OptionFrame,text=colorsdic['bgday'],bg=colorsdic['bgday'],width=7,command=lambda: changecolor('bgday'))
    Buttonbgday.grid(column=2,row=0)
    Buttonfgday=Button(OptionFrame,text=colorsdic['fgday'],bg=colorsdic['fgday'],width=7,command=lambda: changecolor('fgday'))
    Buttonfgday.grid(column=2,row=1)
    ButtonCancel.grid(column=0,row=2,columnspan=3)
def info():
    OptionFrame.grid_forget()
    for widget in OptionFrame.winfo_children():
        widget.grid_forget()
    SetTzList.grid_forget()
    SetTzList.delete(0, 'end')
    LabelText.set('Developped...')
    OptionFrame.config(text="?")
    LabelTitle.grid(column=0, row=0)
    labelGPL=Label(OptionFrame,text="under GPL",bg=fgcolor,fg=bgcolor,width=16)
    labelname=Label(OptionFrame,text="by David LUCAS",bg=bgcolor,fg=fgcolor,width=16)
    labelmail=Label(OptionFrame,text="davlucas@gmail.com",bg=bgcolor,fg=fgcolor,width=16)
    labelthanks=Label(OptionFrame,text="thanks:",bg=fgcolor,fg=bgcolor,width=16)
    labelfunmooc=Label(OptionFrame,text="INRIA and fun-mooc",bg=bgcolor,fg=fgcolor,width=16)
    labelryoko=Label(OptionFrame,text="Ryoko SHINOHARA",bg=bgcolor,fg=fgcolor,width=16)
    labelGPL.grid(column=0, row=1)
    labelname.grid(column=0, row=2)
    labelmail.grid(column=0,row=3)
    labelthanks.grid(column=0, row=4)
    labelfunmooc.grid(column=0, row=5)
    labelryoko.grid(column=0, row=6)
    OptionFrame.grid(column=0,row=0,rowspan=rowmax+1)
    ButtonCancel.grid(column=0,row=7)
def refresh():
    os.execl(sys.executable, os.path.abspath(__file__), *sys.argv)
############MAIN
root=Tk()
root.title("Clock ")
menubar = Menu(root)
LabelText=StringVar()
OptionFrame = LabelFrame(root, text='SETTINGS',width=int(framewidth/1.5))
SetTzList = Listbox(OptionFrame,width=int(framewidth/1.5),bg='white',fg='black')
scrollbar = Scrollbar(OptionFrame,width=5,borderwidth=1)
LabelTitle = Label(OptionFrame, textvariable=LabelText)
ButtonCancel = Button(OptionFrame,text='CANCEL',width=6,command=OptionFrame.grid_forget)
obj_localtime=clock(city,'green','black','black','yellow','green','black',8,column,row,relief)
row+=1
for city in TIMEZONES:
    if (row + column) %2 == 0:
        (bgday, fgday)=COLORDAY
        (bgcolor,fgcolor)=COLORFRAME
        (bgclock,fgclock)=COLORCLOCK
        relief='sunken'
    else:
        (fgday, bgday)=COLORDAY
        (fgcolor,bgcolor)=COLORFRAME
        (fgclock,bgclock)=COLORCLOCK
        relief='ridge'
    obj[city]=clock(city,bgcolor,fgcolor,bgclock,fgclock,bgday,fgday,10,column,row,relief)
    if row < (rowmax):
        row += 1
    else:
        row = 0
        column += 1
tzmenu = Menu(menubar, tearoff=0)
AddTZ= Menu (tzmenu,tearoff=0)
AddTZ.add_command(label='Africa',command= lambda: addtz('Africa',column,row))
AddTZ.add_command(label='America',command= lambda: addtz('America',column,row))
AddTZ.add_command(label='Antarctica',command= lambda: addtz('Antarctica',column,row))
AddTZ.add_command(label='Asia',command= lambda: addtz('Asia',column,row))
AddTZ.add_command(label='Australia',command= lambda: addtz('Australia',column,row))
AddTZ.add_command(label='Canada',command= lambda: addtz('Canada',column,row))
AddTZ.add_command(label='Europa',command= lambda: addtz('Europe',column,row))
AddTZ.add_command(label='Indian',command= lambda: addtz('Indian',column,row))
AddTZ.add_command(label='Pacific',command= lambda:addtz('Pacific',column,row))
AddTZ.add_command(label='USA',command= lambda:addtz('US',column,row))
AddTZ.add_command(label='Etc',command= lambda:addtz('Etc',column,row))
tzmenu.add_cascade(label="Add timezone",menu=AddTZ)
DelTZ= Menu (tzmenu,tearoff=0)
for element in TIMEZONES:
    DelTZ.add_command(label=element, command=obj[element].removeinstance)
tzmenu.add_cascade(label="Remove timezone",menu=DelTZ)
color=Menu(menubar,tearoff=0)
color.add_command(label='change colors settings',command=SetColors)
menubar.add_cascade(label="Timezones Operations", menu=tzmenu)
menubar.add_cascade(label="Set Colors", menu=color)
menubar.add_separator()
About=Menu(menubar,tearoff=0)
About.add_command(label='?',command=info)
About.add_command(label='Refresh display',command=refresh)
About.add_command(label='Quit',command=root.destroy)
menubar.add_cascade(label='About and Refresh',menu=About)
root.config(menu=menubar)
root.mainloop()

Donc, comme vous pouvez le voir, je suis parti d'un timer et puis j'ai rajouté des horloges selon différentes timezones, au début sans classe, j(ai vite vu l'avantage de programmé en objet.

Même si ce code doit vous faire rire (pylint le note -6 quelque chose, c'est que c'est pas terrible) , j'en suis content et ravi.

Néanmoins, persiste un problème, que je pense de design mais je préfère avoir confirmation:

Chaque horloge est donc une classe qui actualise son affichage toutes les secondes.
Donc, si je détruit la frame lors de la supression d'une TZ, cela n'a aucune conséquence: l'objet est bloqué dans la func update_clock. Ma question est donc : est il possible de supprimer subitement une instance de classe et de la forcer à s'arrêter de tourner ?

  • # Ça fait mal aux yeux

    Posté par  (site web personnel) . Évalué à 3.

    Même si ce code doit vous faire rire (pylint le note -6 quelque chose, c'est que c'est pas terrible) , j'en suis content et ravi.

    Non, il fait pas rire.
    pylint est la pour rendre le code lisible. Et justement ton code est très compliqué à lire.
    Et j'ai vraiment pas envie de me creuser la tête pour t'aider alors que ton code me faire mal à la tête juste à le regarder (sans parler de lire).

    Ceci doit expliquer en parti que tu n'aies pas de réponse.

    Matthieu Gautier|irc:starmad

  • # Pas beau du tout...

    Posté par  (Mastodon) . Évalué à 3. Dernière modification le 23 janvier 2019 à 18:11.

    Salut,

    Je fais du python au quotidien… et là je suis séché : je n'ai jamais rien lu de si moche !

    • Code illisible
    • Tout est mélangé

    Pour commencer, python dispose d'un mécanisme de namespace qui permet de séparer les trucs dans des distincts:

    • les modules : de simples fichiers python qui permettent de séparer le code, importables.
    • des packages : des dossiers contenant (au moins) un fichier __init__.py, importables.

    Ces 2 trucs permettent de séparer les choses dans des espaces distincts, et éviter les scripts à ralonge.

    Les fonctions et méthodes ne doivent pas dépasser 50 lignes, sinon tu dois découper.
    En plus, tu mélanges des aspects "objet" avec du code procédural : la seule fonction doit être main(*args), protégée :

    def main(*args):
        # your launching code...
        return 0
    
    if __name__ == "__main__":
        sys.exit(main(sys.argv[1:]))

    Ça permet de rendre un script importable, sans qu'il soit executé directement.

    Le main doit retourner un entier, 0 si tout va bien, un autre entier sinon.

    Ensuite, les règles de nommage:

    • les noms de variables en snake-case : truc_machin = "bidule"
    • les noms de classes capitalisées :
    class Clock:
        def __init__(self, **kwargs):
            self.thing = kwargs.get('thing', None) # None by default
            ...
    • les variables privées commencent par un _
    _private = "foo"

    Tu noteras que plutôt que de mettre 20 args à mon __init__, j'utilise l'opérateur splat qui permet de passer un **kwargs, qui est le dictionnaire de tous les paramètres passée, nommés.

    Pour ta question concernant la suppression d'instance, tu as une méthode spéciale __del__(self) qui permet de gérer la suppression d'instance.

    Il faut garder en mémoire qu'une classe permet de fabriquer des instances, et qu'elle peut avoir des méthodes qui permettent de manipuler ces instances…

    Bon courage !

    • [^] # Re: Pas beau du tout...

      Posté par  . Évalué à 3.

      Merci, je me doute bien mis c'est en forgeant qu'on devient forgerons.
      Merci pour splat, j'ignorais, j'ai un peu modifier, le pylint est positif désormais
      Your code has been rated at 6.96/10 (previous run: 6.92/10, +0.03)

      Donc en résumé: couper les fonctions pour qu'elle ne depassent pas 50 lignes, et utilisation de splat/kwargs
      et rajouter la func main

      Bon, grand merci pour tes conseils
      Autre avis, sur un premier truc que j'ai fait pour manipuler les tableau, c'est fournir un tirage du loto , vaut il mieux :
      full_list = [str(x) for x in range(1, 29)]
      random.shuffle(full_list)
      LABELTIRAGE.config(text='Numéros Bleus:\n{}\nNumérosBonus:\n{}'.format(' '.join(full_list[0:7]), ' '.join(full_list[7:12])))

      ou découper par des variables temporaires ?

      • [^] # Re: Pas beau du tout...

        Posté par  (site web personnel, Mastodon) . Évalué à 3.

        Autre avis, sur un premier truc que j'ai fait pour manipuler les tableau, c'est fournir un tirage du loto , vaut il mieux :
        full_list = [str(x) for x in range(1, 29)]
        random.shuffle(full_list)
        LABELTIRAGE.config(text='Numéros Bleus:\n{}\nNumérosBonus:\n{}'.format(' '.join(full_list[0:7]), ' '.join(full_list[7:12])))

        ou découper par des variables temporaires ?

        Je ne suis pas certain de ce que tu envisages de mettre dans des variables temporaires…

        full_list = [str(x) for x in range(1, 29)]
        random.shuffle(full_list)
        LABELTIRAGE.config(text='Numéros Bleus:\n{}\nNumérosBonus:\n{}'.format(''.join(full_list[0:7]), ' '.join(full_list[7:12])))
        

        Une manière de formatter ton code pourrait être la suivante :

        full_list = [str(x) for x in range(1, 29)]
        random.shuffle(full_list)
        LABELTIRAGE.config(text='Numéros Bleus:\n{}\n'
                                'NumérosBonus:\n{}'.format(' '.join(full_list[0:7]),
                                                           ' '.join(full_list[7:12])))
        

        Si tu utilises l'outil black qui est en train de se faire une place croissante, tu aurais alors un formation similaire à ça :

        full_list = [str(x) for x in range(1, 29)]
        random.shuffle(full_list)
        LABELTIRAGE.config(
            text="Numéros Bleus:\n{}\n"
            "NumérosBonus:\n{}".format(
                " ".join(full_list[0:7]), " ".join(full_list[7:12])
            )
        )
        

        Passer par des variables intermédiaires ? Si c'est pour la lisibilité, ça peut éventuellement (mais pas dans ce contexte-là), si c'est pour permettre de débuguer plus facilement éventuellement (et encore…), et sinon je ne vois pas de raison.

        Si tu parles et lis en Anglais, un bon test est de lire ton code et de voir si tu comprends "en Anglais" ce que ça fait. Si tu ne comprends pas, c'est que le nommage, le découpage, bref le code n'est pas bien structuré.

    • [^] # Re: Pas beau du tout...

      Posté par  (site web personnel, Mastodon) . Évalué à 6.

      Je fais du python au quotidien… et là je suis séché : je n'ai jamais rien lu de si moche !

      • Code illisible
      • Tout est mélangé

      Je fais du python au quotidien aussi, depuis dix ans, et du code moche on en trouve… et parfois même quand ce n'est pas le premier code écrit (dans le cas du premier jet d'un dév, c'est normal d'avoir du code moche).

      D'ailleurs, ça m'arrive encore d'en écrire du code moche : quand tu prototypes rapidement un truc, tu fais pas du code propre (même si tu appliques tes règles de codage dans une certaine mesure)

      Pour commencer, python dispose d'un mécanisme de namespace qui permet de séparer les trucs dans des distincts:

      • les modules : de simples fichiers python qui permettent de séparer le code, importables.
      • des packages : des dossiers contenant (au moins) un fichier init.py, importables.

      Ces 2 trucs permettent de séparer les choses dans des espaces distincts, et éviter les scripts à ralonge.

      Je vais me faire l'avocat du diable, mais parfois les scripts à rallonge se justifient. Si tu écrits des scripts, justement, l'intérêt d'avoir un script à rallonge c'est d'avoir un outil indépendant. Tout comme Go génère un binaire qui contient tout. A déployer, c'est de la rigolade par rapport à un logiciel qui est bien structuré.

      Les fonctions et méthodes ne doivent pas dépasser 50 lignes, sinon tu dois découper.

      Ce dont tu parles, c'est une bonne pratique, pas une obligation. Il est conseillé d'éviter d'écrire des fonctions et des méthodes de plus de 50 lignes, mais ça n'est pas une règle absolue.

      En plus, tu mélanges des aspects "objet" avec du code procédural : la seule fonction doit être main(*args), protégée :

      C'est un truc que j'ai trouvé dans quasiment tous les projets sur lesquels je suis intervenu. C'est pas forcément une bonne chose, mais c'est la réalité.

      Tu noteras que plutôt que de mettre 20 args à mon __init__, j'utilise l'opérateur splat qui permet de passer un **kwargs, qui est le dictionnaire de tous les paramètres passée, nommés.

      A utiliser avec parcimonie, sinon l'utilisateur de ton code va passer son temps à remonter dans la hierachie de classe pour découvrir la véritable signature du constructeur ou de la fonction.

      C'est pratique d'écrire du code qui manipule un kwargs, c'est la plaie à maintenir et faire évoluer et à comprendre. C'est l'équivalent de l'utilisation des dictionnaires en PHP, et c'est souvent compliqué de monter en compétence sur du code qui en fait une utilisation forte. (je sais, les pythonistes vont me jeter des cailloux, mais plus de 15 ans de développement dont 10 à faire du python, ça fait voir du pays, et ça permet d'avoir un regard critique sur les "bonnes" pratiques)

      Je réagis un peu fort, peut-être, mais els introductions du type "Je fais du python au quotidien… et là je suis séché : je n'ai jamais rien lu de si moche !" ça fait un peu trop le mec qui s'y connait et qui prend le débutant pour un nase.

      Tout le monde a été débutant à un moment ou à un autre ; y'a pas besoin de prendre les débutants de haut pour leur expliquer des trucs. Ca me fait penser aux développeurs qui font passer des entretiens et qui trouvent que les candidats sont nuls parce qu'ils n'ont pas réussi à répondre à tous les problèmes spécifiques qui font leur quotidien. Bah oui : si le mec qui bosse ailleurs sait faire exactement tout ce que tu vois comme problème au quotidien, c'est soit qu'il est vraiment plus fort que toi (parce que lui, il ne sait pas ce que tu as en tête et il n'est pas dans ton contexte), soit que tu es nase… soit les deux.

      Par rapport aux remarques de François, deux remarques complémentaires :

      les variables privées commencent par un _

      Il faut bien avoir en tête que les variables privées ou attributs de classe commencent par un _ par convention

      Et aussi un point sur les commentaires. L'intérêt d'un commentaire n'est pas d'expliquer ce que fait le code mais pourquoi il le fait. Par exemple dans le code suivant :

         ``self.thing = kwargs.get('thing', None) # None by default`
      

      Le commentaire est inutile : il décrit simplement le comportement de la méthode get()

      Dernier point : si tu aimes coder, persévère. Tu vas écrire du code et 1 mois plus tard tu le trouveras dégueu. Ca va continuer comme ça pendant longtemps… au fur et à mesure tu vas passer de 1 mois à 2, puis 3, puis 6, puis 12… mais tu ne seras probablement jamais satisfait de ce que tu as écrit, car ton expérience change, ta maîtrise du sujet que tu essaies de résoudre va se préciser et donc la manière de concevoir ou de modéliser évolue.

      Je rejoins François dans ce qu'il ne dit pas explicitement mais qui ressort de sa réponse : il faut essayer d'appliquer les bonnes pratiques. Ca rend ton code plus standard, plus propre en général, et ça permet plus facilement de travailler à plusieurs. Choisi également une approche principale : tu fais de la programmation objet ou tu préfères faire du procédural ? Formatte ton code et standardise-le, il y a pas mal d'outils pour cela : flake, pylint, black, par exemple, et pas mal de bonnes pratiques "standards" que tu peux retrouver dans les pep - dont la fameuse pep8

      • [^] # Re: Pas beau du tout...

        Posté par  (site web personnel, Mastodon) . Évalué à 4.

        Autre point : évite d'utiliser des global. C'est comme utiliser dans variables globales dans tout autre langage : ça rend le code fragile et ça réduit l'évolutivité.

        • [^] # Re: Pas beau du tout...

          Posté par  . Évalué à 1.

          J'ai recommencer, et je me rends compte qu'en effet. Les espaces de nommage, j'ai pas encore super compris. Une classe pour le menu, une classe pour les horloges. Ensuite, je créerai les modules à partir des classes.
          Le splat, ça change la vie.

          • [^] # Re: Pas beau du tout...

            Posté par  (site web personnel, Mastodon) . Évalué à 4.

            Le splat, ça change la vie.

            Si tu te dis dès maintenant que c'est super et que tu vas l'utiliser partout, je te dis : "attention"… ça complique la maintenance de ton code : tu perds la description de ta signature.

            • [^] # Re: Pas beau du tout...

              Posté par  . Évalué à 2. Dernière modification le 24 janvier 2019 à 20:10.

              oui , j'ai bien compris, mais c'est pratique lorsque ton instance (comme dans mon exemple) a besoin d'une palanquée de variable.

              Titre de l'image

            • [^] # Re: Pas beau du tout...

              Posté par  (site web personnel) . Évalué à 5. Dernière modification le 25 janvier 2019 à 23:36.

              Et ça perd les IDE, qui ne peuvent plus t’aider.
              Je préfère conserver la signature, voire ajouter le type attendu et avoir un IDE qui me prévient dès que je fais une erreur de type.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.