Zufallszahl aus einer bereits erzeugten Zufallszahl beziehen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
AngelFilmnMusic
User
Beiträge: 35
Registriert: Montag 3. Oktober 2022, 07:58

Hallo zusammen,
zunächst einmal wünsche ich euch allen einen wunderschönen Feiertag.
Ich bin ein absoluter Anfänger mit Python und komme einfach nicht weiter.


Beispiel-Problem:
Ich habe einen Parkplatz mit 500 Stellplätzen.
Im ersten Schritt generiere ich die Anzahl der reingefahrenen Autos.

Rein = random.randint(0, 500)
das stellt die Anzahl der reingefahrenen Autos dar.

Im zweiten Schritt möchte ich eine weitere Zufallszahl für die rausgefahrenen Autos generieren. Und hier ist der Knackpunkt. Wurden beispielsweise 325 reingefahrene Autos erzeugt, muss der Wert der rausgefahrenen Autos kleiner oder gleich 325 sein um die aktuelle Anzahl der freien Stellplätze basierend auf dem Ursprungswert der insgesamt 500 Stellplätze zu berechnen. Sprich, ich muss den erzeugten Wert der reingefahrenen Autos irgendwie beziehen.


Rein = random.randint(0, 500)
generiert zwar eine Zufallszahl, diese kann allerdings auch größer sein, als die Anzahl der reingefahrenen Autos. Ist ja sinnfrei.

Zwei Beispielfehlversuche, habe noch etliches mehr ausprobiert:
Raus = random.choice(Rein) #Fehler:object of type 'int' has no len()
Raus = random.choice( <= Rein) #Fehler: '<=' not supported between instances of 'type' and 'int'

Vielen Dank und einen schönen Feiertag
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

rein = random.randint(0, 500)
raus = random.randint(0, rein)

Variablennamen sollten klein_mit_unterstrich sein.
In specifications, Murphy's Law supersedes Ohm's.
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

›randint(0, 500)‹ liefert eine Zufallszahl zwischen 0 und 500, jetzt brauchst Du eine Zufallszahl zwischen 0 und ›rein‹.
AngelFilmnMusic
User
Beiträge: 35
Registriert: Montag 3. Oktober 2022, 07:58

OMG,
auf die wahrscheinlich einfachste Möglichkeit bin ich gar nicht gekommen. Vielen Dank dafür @pillmuncher
Und auch danke für den Hinweis mit den Variablennamen. In diesem Fall habe ich die allerdings (nur Vorübergehend) mit Absicht groß geschrieben am Anfang, damit ich sie im Variablen Explorer von Spyder schneller wiederfinde, weil dort das meiste entweder komplett in Großbuchstaben oder komplett in Kleinbuchstaben gelistet wird. Die werden jetzt auch sofort umgeändert.
Lieben Dank nochmal, schönen Feiertag
Zuletzt geändert von AngelFilmnMusic am Montag 3. Oktober 2022, 09:27, insgesamt 1-mal geändert.
AngelFilmnMusic
User
Beiträge: 35
Registriert: Montag 3. Oktober 2022, 07:58

Vielen Dank @Sirius3
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Dein ursächlicher Denkfehler liegt möglicherweise hier:
AngelFilmnMusic hat geschrieben: Montag 3. Oktober 2022, 08:36 Rein = random.randint(0, 500)
generiert zwar eine Zufallszahl, diese kann allerdings auch größer sein, als die Anzahl der reingefahrenen Autos. Ist ja sinnfrei.
Die generierte Zufallszahl kann nicht größer sein als die Zahl der reingefahrenen Autos, denn sie definiert den Anfangszustand.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Da bietet sich eine Variante von einem Mathematikerwitz an: Wenn 5 Autos auf den leeren Parkplatz fahren, und 7 Autos wieder weg fahren, dann müssen 2 Autos auf den Parkplatz fahren, damit der wieder leer ist. 🤓
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
DeaD_EyE
User
Beiträge: 1012
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Frage: Können 2 Autos ein und denselben Parkplatz belegen?

Falls das in diesem Denkmodell funktioniert, ist die Lösung mit randint in Ordnung.

Falls die Aufgabe beliebig oft wiederholt werden soll und nur ein Auto ein Parkplatz belegen kann,
muss man sich die freien und vergebenen Parkplätze merken. Am einfachsten geht das mit Listen.

Das hat auch den Vorteil, dass man random.choice, random.sample und random.shuffle verwenden kann.
Wenn mehr Parkplätze zur Verfügung stehen, kann man die range erweitern und/oder sogar die Liste erweitern.
Dann kann man z.B. die Parkpältze 100 - 150, 200 - 250 usw. nach Etagen vergeben. Nur so eine Idee.

Code: Alles auswählen

from random import choice, sample

frei = list(range(1, 501))
# frei.extend(range(601, 701))
belegt = []


def parken():
    if not frei:
        raise ValueError("Alle Parkplätze sind voll")
    nummer = choice(frei)
    frei.remove(nummer)
    belegt.append(nummer)
    return nummer


def fahren():
    if not belegt:
        raise ValueError("Es sind keine Parkplätze belegt.")
    nummer = choice(belegt)
    belegt.remove(nummer)
    frei.append(nummer)
    return nummer


def parken_viele(autos: int):
    if len(frei) < autos:
        raise ValueError("Zu wenig freie Plätze", len(frei))
    nummern = sample(frei, autos)
    for nummer in nummern:
        frei.remove(nummer)
        belegt.append(nummer)

    return nummern


def fahren_viele(autos: int):
    if len(belegt) < autos:
        raise ValueError("Zu wenig belegte Plätze", len(belegt))
    nummern = sample(belegt, autos)
    for nummer in nummern:
        belegt.remove(nummer)
        frei.append(nummer)
Ich habe auch mal jeder Funktion einen ValueError hinzugefügt, wenn die Anzahl nicht stimmt. Zu wenig freie/belegte Parkplätze.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@DeaD_EyE: Nee, am einfachsten geht das in dem man sich die Anzahl der Parkplätze merkt; belegt, frei, gesamt — zwei davon muss man sich merken. Wo die Autos genau stehen ist egal.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
AngelFilmnMusic
User
Beiträge: 35
Registriert: Montag 3. Oktober 2022, 07:58

Hallo @kbr,

sorry für die verspätete Antwort, war leider etwas krank.
Ich hatte ja nur die weiteren Syntax angegeben, die ich ausprobiert hatte. Was ich eigentlich meinte, dass mehr Autos raus als reinfahren könnten, wäre gewesen, wenn dort folgendes gestanden hätte
rein = random.randint(0, 500)
raus = random.randint(0, 500)

dann könnten beispielsweise nur 200 rein und 300 raus, weil ja beides Zufallszahlen wären, die unabhängig voneinander generiert werden.
AngelFilmnMusic
User
Beiträge: 35
Registriert: Montag 3. Oktober 2022, 07:58

Hallo dead_eye,

sorry für die verspätete Antwort, war leider etwas krank. Ja, genau das hatte ich vor. Es soll beliebig oft wiederholt werden, danke für den Codevorschlag. Derzeit habe ich folgenden Code mit ca. 14-18 Stunden (schätze ich) praktischer Pythonerfahrung und einer Woche Theorie. Der läuft nur einmal durch und hat als Zusatzbedingung ob beispielswiese EIN/Ausfahrt blockiert ist, ob das Parkhaus pauschal für eine Veranstaltung vermietet wurde oder ob min. 95% belegt sind.

Code: Alles auswählen

#import der Funktionen für GUI
from tkinter import*
from PIL import Image, ImageTk


#Zufallswerte ermitteln
import random
rein = random.randint(0, 500)
raus = random.randint(0, rein)

frei = (500 - rein) + raus

#Ausgabe der Zufallwerte als Text
if rein == 1:
    print(rein, "Fahrzeug reingefahren\n")    
else:
    print(rein, "Fahrzeuge reingefahren\n")
    
    
if raus == 1:
    print(raus, "Fahrzeug rausgefahren\n")   
else:
    print(raus, "Fahrzeuge rausgefahren\n")


"""
Bei Ausfahrtberechunng sthet die-1 sind die pauschal berechneten 3.50 € für
die erste angefangene Stunde.

Bspw. werden bei einer Gesmatparkdauer von 11 Stunden, 10 Stunden
zu je 2.00 € berechnet und im Nachgnag 3.50 addiert. Somit wären wir
Gebührentechnisch wieder bei 11 Stunden Gesamtparkdauer.
"""


ausfahrtBerechnung = random.randint(1, 24)
gebuehren = ((ausfahrtBerechnung - 1) * 2) + 3.50
blockiert = random.choice([True, False])
vermietet = random.choice([True, False])


#Konsolenausgabe
#Anzeige Displays Einfahrt und Parkleitsysteme
if rein > 474 and not blockiert and not vermietet:
    print("Parkhaus belegt")
    

#Anzeige Display Kassenautomat    
elif rein < 475 and not blockiert and not vermietet:
    
    #Anzeige Display Kassenautomat, wenn noch kein Parkticket eingesteckt
    print("\nErste Std. 3.50 €")
    print("Jede weitere Std. 2.00 €")
    
    #Anzeige Display Kassenautomat nach dem Parkticket eingesteckt wurde
    print("\nParkdauer:", ausfahrtBerechnung, "Std.")
    print("Zu Zahlen: {0:5.2f} €\n".format(gebuehren))
    

    #Anzeige Parkhauseinfahrt Display
    print((500 - rein) + raus, "Stellplätze frei\n")
    
    #Anzeige Parkleitsystem Neumarkt
    print("Galeria Kaufhof 750m ⮏", frei, "Stellplätze frei")
    
    #Anzeige Parkleitsystem Deutzer Brücke
    print("\nGaleria Kaufhof 650m 🡡", frei, "Stellplätze frei")
    
    
#Anzeige Displays Einfahrt und Parkleitsystem  
else:
    print("Parkhaus gesperrt")
    
    #Anzeige Kassenautomaten
    print("Bitte wenden Sie sich an die Parkhausaufsicht!")

#______________________________________________________________________________

#GUI

#Text auf den Displays ändern
def addStoerung():
    print("Störung; Parkhaus gesperrt")
    
    
def addPolizei():
    kassenautomat_1_DisplayLabel.config(text = "Bitte wenden Sie\nsich an die\nParkhausaufsicht!")
    
    kassenautomat_2_DisplayLabel.config(text = "Bitte wenden Sie\nsich an die\nParkhausaufsicht!")
    
    einfahrtDisplayLabel.config(text = "\nParkhaus gesperrt")
    
    pls_NeumarktDisplayLabel.config(text = "Parkhaus gesperrt")
    
    pls_DeutzerBDisplayLabel.config(text = "Parkhaus gesperrt")
    print("Polizeieinsatz; Parkhaus gesperrt") 
    
    
def addFeuerwehr():
    kassenautomat_1_DisplayLabel.config(text = "Bitte verlassen Sie\nzügig das Parkhaus!\n"
                                        "\nSteigen Sie nicht\nins Auto!")
    
    kassenautomat_2_DisplayLabel.config(text = "Bitte verlassen Sie\nzügig das Parkhaus!\n"
                                        "\nSteigen Sie nicht\nins Auto!")
    
    einfahrtDisplayLabel.config(text = "\nParkhaus gesperrt")
    
    pls_NeumarktDisplayLabel.config(text = "Parkhaus gesperrt")
    
    pls_DeutzerBDisplayLabel.config(text = "Parkhaus gesperrt")
    print("Feuerwehreinsatz; Parkhaus gesperrt") 
    
#------------------------------------------------------------------------------

#Versetzt die Displays wieder in den Normalbetrieb
def addStoerungBehoben():
    kassenautomat_1_DisplayLabel.config(text = "Erste Std. 3.50 € \nJede weitere Std. 2.00 €"
     "\n\nBitte Parkticket\neinstecken!") 
    
    kassenautomat_2_DisplayLabel.config(text = "Erste Std. 3.50 € \nJede weitere Std. 2.00 €"
     "\n\nBitte Parkticket\neinstecken!") 
    
    einfahrtDisplayLabel.config(text = "\nWillkommen\nBitte grüne Taste drücken")
         
    ausfahrtDisplayLabel.config(text = "\nAuf Wiedersehen")
              
    pls_NeumarktDisplayLabel.config(text = "750m " + (str(frei) + " freie Stellplätze"))
        
    pls_DeutzerBDisplayLabel.config(text = "650m ⇡ " + (str(frei) + " freie Stellplätze"))
    print("Normalbetrieb wieder augenommen")
    

#Speichern      
def addSave():
    filename = filedialog.asksaveasfilename(initialdir = "/",
                filetypes = (("Textdatei", "*.txt"), ("all files", "*.*" )))
    filename += ".txt"
    
    
    print("Parkdauer:", AusfahrtBerechnung, "Std.",
          "\nZu Zahlen: {0:5.2f} €".format(Gebuehren))
    
    
    fp = open(filename, "w") 
    fp.write("Parkdauer: {1}Std.\nZu Zahlen: {0:5.2f} €".format(Gebuehren, AusfahrtBerechnung))
    fp.close() 

#------------------------------------------------------------------------------

##Programmfenster beginn 
window = Tk()
window.geometry("1920x1080")
window.title("Parkhausüberwachungsmonitor")
icon = PhotoImage(file='Zeichen_314-50_-_Parkhaus,_Parkgarage,_StVO_2017.png')
window.iconphoto(True, icon)
window.config(bg = "#262626")

#------------------------------------------------------------------------------

#Logo und Programmname
canvas_1 = Canvas(window)     
PhotoImage(master = canvas_1)
frame_1 = Frame()
frame_1.place(x = 50, y = 50, width = 50, height = 50)   
img_1 = ImageTk.PhotoImage(Image.open('Zeichen_314-50_-_Parkhaus,_Parkgarage,_StVO_2017-Anwendung.png'))
label_1 = Label(frame_1, image = img_1)
label_1.pack()

        
labelProgram = Label(window,
                     text="Parkhaussysteme",
                     font = ("Helvetica 25 bold"),
                     bg="#262626", fg="#FFFFFF")
labelProgram.place(x = 120, y = 52.5)

#------------------------------------------------------------------------------

#Bilder
canvas_2 = Canvas(window)     
PhotoImage(master = canvas_2)
frame_2 = Frame()
frame_2.place(x = 500, y = 50, width = 498, height = 463)   
img_2 = ImageTk.PhotoImage(Image.open('Kassenautomat-1.png'))
label_2 = Label(frame_2, image = img_2)
label_2.pack()


canvas_3 = Canvas(window)     
PhotoImage(master = canvas_3)
frame_3 = Frame()
frame_3.place(x = 1050, y = 50, width = 498, height = 463)  
img_3 = ImageTk.PhotoImage(Image.open('Kassenautomat-2.png'))
label_3 = Label(frame_3, image = img_3)
label_3.pack()


canvas_4 = Canvas(window)     
PhotoImage(master = canvas_4)
frame_4 = Frame()
frame_4.place(x = 500, y = 575, width = 304, height = 463)  
img_4 = ImageTk.PhotoImage(Image.open('Ticket-Ein.png'))
label_4 = Label(frame_4, image = img_4)
label_4.pack()


canvas_5 = Canvas(window)     
PhotoImage(master = canvas_5)
frame_5 = Frame()
frame_5.place(x = 854, y = 575, width = 304, height = 463)  
img_5 = ImageTk.PhotoImage(Image.open('Ticket-Aus.png'))
label_5 = Label(frame_5, image = img_5)
label_5.pack()


canvas_6 = Canvas(window)     
PhotoImage(master = canvas_6)
frame_6 = Frame()
frame_6.place(x = 1204, y = 575, width = 664, height = 215)  
img_6 = ImageTk.PhotoImage(Image.open('Parkleitsystem-Neumarkt.png'))
label_6 = Label(frame_6, image = img_6)
label_6.pack()


canvas_7 = Canvas(window)     
PhotoImage(master = canvas_7)
frame_7 = Frame()
frame_7.place(x = 1204, y = 825, width = 664, height = 215)  
img_7 = ImageTk.PhotoImage(Image.open('Parkleitsystem-Deutzer_Bruecke.png'))
label_7 = Label(frame_7, image = img_7)
label_7.pack()

#------------------------------------------------------------------------------

#Schaltflächen
stoerung = Button(window,
                  text = "Störung", font = ("Helvetica 20 bold"),
                  width = 20, height = 2,
                  fg = "black", bg = "#FFDE35",
                  activeforeground = "black",
                  activebackground = "#FFDE35",
                  command = addStoerung)
stoerung.place(x = 50, y = 149)
        
        
polizei = Button(window,
                 text = "Polizei Einsatz", font = ("Helvetica 20 bold"),
                 width = 20, height = 2,
                 fg = "white", bg = "#0012FF",
                 activeforeground = "white",
                 activebackground = "#0012FF",
                 command = addPolizei)
polizei.place(x = 50, y = 289)   
        
        
feuerwehr = Button(window,
                   text = "Feuerwehr Einsatz", font = ("Helvetica 20 bold"),
                   width = 20, height = 2,
                   fg = "white", bg = "#FF0000",
                   activeforeground = "white",
                   activebackground = "#FF0000",
                   command = addFeuerwehr)
feuerwehr.place(x = 50, y = 429)


stoerungBehoben = Button(window,
                  text = "Störung behoben", font = ("Helvetica 20 bold"),
                  width = 20, height = 2,
                  fg = "white", bg = "#008000",
                  activeforeground = "white",
                  activebackground = "#008000",
                  command = addStoerungBehoben)
stoerungBehoben.place(x = 50, y = 569)      
        
        
savetoFile = Button(window,
                    text = "Tagesabschluss", font = ("Helvetica 20 bold"),
                    width = 20, height = 2,
                    fg = "black", bg = "#F1F1F1",
                    activeforeground = "black",
                    activebackground = "#F1F1F1",
                    command = addSave)
savetoFile.place(x = 50, y = 810)
        
        
beenden = Button(window,
                 text = "Programm schließen", font = ("Helvetica 20 bold"),
                 width = 20, height = 2,
                 fg = "black", bg = "#F1F1F1",
                 activeforeground = "black",
                 activebackground = "#F1F1F1",
                 command = window.destroy)    
beenden.place(x = 50, y = 950)

#------------------------------------------------------------------------------

#Displayanzeigen Anfangszustand
kassenautomat_1_DisplayLabel = Label(window,
                                     justify = 'left',
                                     text = "Erste Std. 3.50 € \nJede weitere Std. 2.00 €"
                                     "\n\nBitte Parkticket\neinstecken!",
                                     font = ("Helvetica 10 bold"),
                                     bg="#E6E3E3", fg="black")
kassenautomat_1_DisplayLabel.place(width = 150, height = 100, x = 555, y = 175)
        
    
kassenautomat_2_DisplayLabel = Label(window,
                                     justify = 'left',
                                     text = "Erste Std. 3.50 € \nJede weitere Std. 2.00 €"
                                     "\n\nBitte Parkticket\neinstecken!",
                                     font = ("Helvetica 10 bold"),
                                     bg="#E6E3E3", fg="black") 
kassenautomat_2_DisplayLabel.place(width = 150, height = 100, x = 1105, y = 175)
    
    
einfahrtDisplayLabel = Label(window,
                             justify = 'left',
                             anchor = N,
                             text = "\nWillkommen\n"
                             "Bitte grüne Taste drücken",
                             font = ("LCD 5"),
                             bg="#B5E61D", fg="black") 
einfahrtDisplayLabel.place(width = 136, height = 54, x = 597, y = 662)
    
    
ausfahrtDisplayLabel = Label(window,
                             justify = 'left',
                             anchor = N,
                             text = "\nAuf Wiedersehen",
                             font = ("LCD 7"),
                             bg="#B5E61D", fg="black") 
ausfahrtDisplayLabel.place(width = 136, height = 54, x = 951, y = 662)


pls_NeumarktDisplayLabel = Label(window,
                                 justify = 'left',
                                 text = "750m " + (str(frei) + " freie Stellplätze"),
                                 font = "5by7 30",
                                 bg="black",
                                 fg="white") 
pls_NeumarktDisplayLabel.place(width = 609, height = 68, x = 1234, y = 696)
    
    
pls_DeutzerBDisplayLabel = Label(window,
                                 justify = 'left',
                                 text = "650m ⇡ " + (str(frei) + " freie Stellplätze"),
                                 font = "5by7 30",
                                 bg="black",
                                 fg="white") 
pls_DeutzerBDisplayLabel.place(width = 609, height = 68, x = 1234, y = 946)

#------------------------------------------------------------------------------

window.mainloop()
##Programmfenster ende
edit: anscheined werden Bilder von Google Drive Links nicht angezeigt, schade
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@AngelFilmnMusic: Anmerkungen zum Quelltext:

Sternchen-Importe sind Böse™. Da holt man sich gerade bei `tkinter` fast 140 Namen ins Modul von denen nur ein kleiner Bruchteil verwendet wird. Auch Namen die gar nicht in `tkinter` definiert werden, sondern ihrerseits von woanders importiert werden. Das macht Programme unnötig unübersichtlicher und fehleranfälliger und es besteht die Gefahr von Namenskollisionen.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Man nummeriert keine Namen. Dann will man sich entweder bessere Namen überlegen, oder gar keine Einzelnamen/-werte verwenden, sondern eine Datenstruktur. Oft eine Liste.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.

Kommentare werden mit ``#`` eingeleitet. Literale Zeichenketten sollte man nicht als Kommentare missbrauchen. Die haben an einigen Stellen im Quelltext eine besondere Bedeutung als Docstrings. Für einige externe Werkzeuge auch an Stellen wo sie für Python selbst keine besondere Bedeutung haben. Kommentare und Docstrings sollte man nicht verwechseln und auch Docstrings nicht als Kommentare missbrauchen.

Magische Zahlen sollte man vermeiden. Statt mehrfach 500 im Code stehen zu haben, sollte man da eine Konstante für definieren. Dann kann man den Wert leicht ändern, ohne den ganzen Code durchgehen zu müssen und bei jeder 500 die man findet, feststellen zu müssen ob das eine 500 ist die man ändern muss, oder ob das eine 500 ist, die an der Stelle eine andere Bedeutung hat, und deshalb nicht geändert werden darf. Das ist unnötig aufwändig und fehleranfällig.

Dateien öffnet man wo es geht zusammen mit der ``with``-Anweisung. Und bei Textdateien sollte man beim öffnen explizit die Kodierung angeben, statt Python ”raten” zu lassen. Es ist auch immer eine gute Idee Zeilen mit einem Zeilenendezeichen enden zu lassen. Auch die letzte Zeile einer Textdatei und auch wenn das die einzige Zeile ist.

Es werden keine Funktionen benutzt. Die Funktionen die definiert werden sind alle nur ”benannte” Codeabschnitte, als wenn Funktionen so etwas wie Sprungmarken wären. Die ”Funktionen” verändern dann alle irgendwelchen globalen Zustand auf den einfach so zugegriffen wird, ohne das man leicht nachvollziehen könnte was das ist ohne die kompletten Funktionskörper zu lesen. Funktionen bekommen alles was sie ausser Konstanten benötigen, als Argument(e) übergeben. Auf Modulebene gibt es ja keine Variablen, die stecken in Funktionen und Objekten.

Warum fangen die Funktionsnamen alle mit `add` an?

Da sind einige `PhotoImage`-Aufrufe die keinen Sinn machen, weil mit dem Objekt dann überhaupt gar nichts gemacht wird.

Nicht alle Widgets bekommen explizit ein `master` übergeben und werden dadurch implizit im Hauptfenster angezeigt. Das sollte man explizit klar machen.

`place()` verwendet man nicht, weil das nicht wirklich gut funktioniert bei dem den heutigen Zoo an Bildschirmgrössen/-auflösungen/-einstellungen.

Insgesamt würde ich sagen bevor da eine GUI geschrieben wird, sollte man sich mit objektorientierter Programmierung (OOP) auseinandersetzen, denn die braucht man für jede nicht-triviale GUI. Und auch für Simulationen ist das ein sinnvolles Werkzeug.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
AngelFilmnMusic
User
Beiträge: 35
Registriert: Montag 3. Oktober 2022, 07:58

Hi @blackjack,

vielen Dank für dein ausführliches Feedback. Das werde ich mir aber morgen zu früherer Stunde nochmal durchlesen und konzentriertet. Ohne das PhotoImage wurden die Grafiken nicht angezeigt. Keine Ahnung warum. Wie gesagt, bin noch ein absoluter Noob mit gerade einmal 14-18 Stunden Erfahrung.

Und bzgl. der Kommentare sagte man uns, "lehrte"
Nur einzeilige Kommentare mit # und mehrzeilige mit " " "
das ist nicht auf meinem Mist gewachsen :D
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@AngelFilmnMusic: Das ``#`` das Zeichen für Kommentare ist und literale Zeichenketten an bestimmten Stellen eine besondere Bedeutung für Python selbst haben, und damit dort nicht als Kommentar verwendet werden können, steht ja in der Python-Dokumentation. Sollte man dem Lehrpersonal vielleicht mal sagen. 🤔

In der informellen Einführung steht das hier: https://docs.python.org/3/tutorial/intr ... ml#index-0

Und in der Sprachreferenz hier: https://docs.python.org/3/reference/lex ... l#comments

Docstring steht beispielsweise im Glossar: https://docs.python.org/3/glossary.html#term-docstring

Werkzeuge zum erstellen von Dokumentation interpretieren in der Regel auch literale Zeichenketten an einigen anderen Stellen als Docstrings. Unter anderem das Standardwerkzeug Sphinx, mit dem auch die Python-Dokumentation erstellt wird.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
AngelFilmnMusic
User
Beiträge: 35
Registriert: Montag 3. Oktober 2022, 07:58

Hi @blackjack,

auf das "#" und die " " " werde ich ihn ansprechen, wenn wir ihn im November erneut haben.

Jetzt gehe ich deine Nachricht von gestern erst Stück für Stück durch. Und werde auch mal ausprobieren
AngelFilmnMusic
User
Beiträge: 35
Registriert: Montag 3. Oktober 2022, 07:58

@__blackjack__,
Sternchen-Importe sind Böse™. Da holt man sich gerade bei `tkinter` fast 140 Namen ins Modul von denen nur ein kleiner Bruchteil verwendet wird. Auch Namen die gar nicht in `tkinter` definiert werden, sondern ihrerseits von woanders importiert werden. Das macht Programme unnötig unübersichtlicher und fehleranfälliger und es besteht die Gefahr von Namenskollisionen.
GUI erstellen bekommen wir gar nicht gelehrt/gezeigt. Die GUI ist eine Eigeninitiative von mir. Da ich leider keine Ahnung davon habe, was ich im Detail für welche Funktion benötige, ist es mir als Noob leichter gefallen direkt alles mit * zu importieren.
Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
Das hatte ich im Nachhinein vor, da ich die GUI ja, wie bereits erwähnt, in Eigeninitiative erstellt habe. Habe den Code dann einfach hinten dran gehangen.
Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase)
Das ist diese CapitalCamelCase Gewohnheit von mir oder in Python anscheinend PascalCase genannt. Jedes neue Wort mit am Anfang groß zu schreiben, sofern es ein aus mehreren Wörtern zusammengesetztes Wort ist wie StellPlaetze zum Beispiel. Werde ich aber ändern.
Man nummeriert keine Namen. Dann will man sich entweder bessere Namen überlegen, oder gar keine Einzelnamen/-werte verwenden, sondern eine Datenstruktur. Oft eine Liste.
Ich vermute du meinst

Code: Alles auswählen

kassenautomat_1_DisplayLabel
. Da beide identisch sind, fand ich das am sinnigsten. Wüsste auch nicht, wie ich das überhaupt als Liste umsetzen soll.[/i]
Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.
Hatte unser Dozent auch schon bemängelt. Das hat eine ganz einfache Ursache. Da ich ja selbst noch gar keinen Plan habe oder nicht wirklich einen Plan hatte am Anfang, was diese und jene Codezeile macht, weil ich selbst noch gar nicht in der Lage bin es direkt aus dem FF zu sagen, wenn ich einen Fremdcode sehe, dachte ich mir, wenn Leute, die gar keine Programmierer sind, den Code sehen bzw. noch keine Programmiererfahrung haben (Klassen, die erst später damit anfangen, wir teilen die Codes), es leichter haben werden. Weil es mir als Anfänger derzeit so auch leichter fällt. Mit der Zeit wird es dann ehe weggelassen. Dann kommt nur noch das "warum".
Kommentare werden mit ``#`` eingeleitet. Literale Zeichenketten sollte man nicht als Kommentare missbrauchen. Die haben an einigen Stellen im Quelltext eine besondere Bedeutung als Docstrings. Für einige externe Werkzeuge auch an Stellen wo sie für Python selbst keine besondere Bedeutung haben. Kommentare und Docstrings sollte man nicht verwechseln und auch Docstrings nicht als Kommentare missbrauchen.
Gestern Abend beantwortet
Magische Zahlen sollte man vermeiden. Statt mehrfach 500 im Code stehen zu haben, sollte man da eine Konstante für definieren. Dann kann man den Wert leicht ändern, ohne den ganzen Code durchgehen zu müssen und bei jeder 500 die man findet, feststellen zu müssen ob das eine 500 ist die man ändern muss, oder ob das eine 500 ist, die an der Stelle eine andere Bedeutung hat, und deshalb nicht geändert werden darf. Das ist unnötig aufwändig und fehleranfällig.
Du meinst bspw.:

Code: Alles auswählen

gesamtAnzahl_stellPlaetze = 500
?[/i]
Dateien öffnet man wo es geht zusammen mit der ``with``-Anweisung. Und bei Textdateien sollte man beim öffnen explizit die Kodierung angeben, statt Python ”raten” zu lassen. Es ist auch immer eine gute Idee Zeilen mit einem Zeilenendezeichen enden zu lassen. Auch die letzte Zeile einer Textdatei und auch wenn das die einzige Zeile ist.
Hier bin ich raus. Weiß nur, dass der Dozent am Donnerstag oder gestern eine Debatte mit einem anderen aus der Klasse hatte ob jetzt with oder open verwendet soll und es irgendwie beides im Endeffekt dasselbe ist? Irgendwie so was. Bzgl. Kodierung meinst du, glaube ich die *.txt am Ende. Falls das gemeint ist, das ist Absicht. Weil diese Datei noch gar nicht existent ist. Die Datei wird zum ersten Mal erstellt. Ich hatte selbst nur das:

Code: Alles auswählen

def addSave():
    filename = filedialog.asksaveasfilename(initialdir = "/",
                filetypes = (("Textdatei", "*.txt"), ("all files", "*.*" )))
    filename += ".txt"
    
    
    print("Parkdauer:", AusfahrtBerechnung, "Std.",
          "\nZu Zahlen: {0:5.2f} €".format(Gebuehren))
Dann wurde die Datei aber nie erstellt, obwohl ich in Windows Verzeichnis, Ordner auswählen und den Dateinamen eingeben konnte. Dozi meinte, das ist, weil das hier fehlt:

Code: Alles auswählen

    fp = open(filename, "w") 
    fp.write("Parkdauer: {1}Std.\nZu Zahlen: {0:5.2f} €".format(Gebuehren, AusfahrtBerechnung))
    fp.close() 
Aber, wie schon gesagt, es wird keine bereits vorhandene Datei geöffnet, was wohl der Grund sein wird, dass keine Kodierung angegeben ist[/i]
Es werden keine Funktionen benutzt. Die Funktionen die definiert werden sind alle nur ”benannte” Codeabschnitte, als wenn Funktionen so etwas wie Sprungmarken wären. Die ”Funktionen” verändern dann alle irgendwelchen globalen Zustand auf den einfach so zugegriffen wird, ohne das man leicht nachvollziehen könnte was das ist ohne die kompletten Funktionskörper zu lesen. Funktionen bekommen alles was sie ausser Konstanten benötigen, als Argument(e) übergeben. Auf Modulebene gibt es ja keine Variablen, die stecken in Funktionen und Objekten.
Den Teil verstehe ich nicht. Liegt wohl daran, dass ich gar nicht weiß, was Argumente überhaupt sind.
Warum fangen die Funktionsnamen alle mit `add` an?
Das ist ein Gedankenfehler von mir. Ich hatte das bei der Erstellung nach dem Motto Schalfläche wird hinzugefügt gemacht, deswegen das "add". Wollte das "add" in "btn" wie Button ändern, bin ich noch nicht zu gekommen.
Da sind einige `PhotoImage`-Aufrufe die keinen Sinn machen, weil mit dem Objekt dann überhaupt gar nichts gemacht wird.
Nicht alle Widgets bekommen explizit ein `master` übergeben und werden dadurch implizit im Hauptfenster angezeigt. Das sollte man explizit klar machen.
Da werde ich nochmal sehen, ob das nach dieser einen Einstellungsänderung, die ich in Spyder vorgenommen habe, noch notwendig ist. Wenn nicht, kommt es weg, wo es weg muss.
`place()` verwendet man nicht, weil das nicht wirklich gut funktioniert bei dem den heutigen Zoo an Bildschirmgrössen/-auflösungen/-einstellungen.
Da ich die Fenstergröße des Programms ja auf 1.920x1.080 festgesetzt habe, sollte das doch eigentlich kein Problem darstellen oder irre ich mich in meiner Theorie?
Insgesamt würde ich sagen bevor da eine GUI geschrieben wird, sollte man sich mit objektorientierter Programmierung (OOP) auseinandersetzen, denn die braucht man für jede nicht-triviale GUI. Und auch für Simulationen ist das ein sinnvolles Werkzeug.
Wie bereits erwähnt, es ist ja eine Eigeninitiative von mir. Zur Objektorientierung kommen wir im November oder noch später glaube ich. Wir haben zwischendrin andere Fächer immer als Block. Fachliches Englisch zum Beispiel und all so Scherze.
AngelFilmnMusic
User
Beiträge: 35
Registriert: Montag 3. Oktober 2022, 07:58

@blackjack
Nachtrag, hatte ich vorhin vergessen:
Da sind einige `PhotoImage`-Aufrufe die keinen Sinn machen, weil mit dem Objekt dann überhaupt gar nichts gemacht wird.
Es kam immer der Fehler "pyImage not found" oder so ähnlich. Dann hatte ich das nur einmal reingeschrieben und es ging immer noch nicht. Also habe ich es dann überall reingeschrieben, immer noch nirgends eine Grafik zu sehen. Nach dem ich dann in Spyder in den iPyhton console --> Graphics Einstellungen "Activate Support" deaktiviert habe, ging es und hatte noch keine Gelegenheit den Master zu entfernen oder nur einmal am Anfang zu lassen und es erneut auszuprobieren.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@AngelFilmnMusic: Regeln sind ja dafür da, das man Ausnahmen machen kann, und bei einigen wenigen Modulen, die sehr viele Namen enthalten, ist es üblich eine kryptische Abkürzung zu verwenden. Wobei Tk ja selbst schon diese Abkürzung ist. Das Modul wird üblicherweise mit ``import tkinter as tk`` importiert. Dann werden Zeilen nicht zu lang weil man sonst mehrfach `tkinter` in einigen Zeilen hätte, aber man weiss trotzdem noch das `tk.Image` was anderes ist als `PIL.Image`.

Bei den Kassenautomaten ``kassenautomaten_displaylabel = []`` und da dann die Label rein. Sollten dir grundsätzlich das gleiche anzeigen, würde man die Label selbst eher gar nicht speichern, sondern eine `tkinter.StringVar` erstellen, die beiden Labeln als `textvariable` übergeben, und dann immer nur die `StringVar` verändern.

``GESAMTANZAHL_STELLPLAETZE = 500`` damit es als Konstante erkennbar ist. Die Gross-/Kleinschreibung bei `gesamtAnzahl_stellPlaetze` wäre komisch mit den Grossbuchstaben, und damit meine ich nicht mal Namenskonventionen, sondern Gesamtanzahl und Stellplätze sind doch auch im Deutschen jeweils *ein* Wort‽

Also `open()` muss man ja auch bei ``with`` verwenden, und wenn das beides im Endeffekt das selbe ist, dann wäre ``with`` ja überflüssig. Bei ``with`` ist sichergestellt, beim verlassen des ``with``-Blocks — egal aus welchem Grund — der Ressource aus der ``with``-Anweisung gesagt wird „räum mal auf“. Was bei Dateiobjekten „schliessen“ bedeutet. Klar kann man jetzt bei einem Dreizeiler öffnen/schreiben/schliessen anfangen zu Argumentieren was soll denn da schon schief gehen wenn ich da mal nicht ``with`` verwende. Das ist aber so ähnlich wie, „Ich kenne die Strecke und die ist kurz, dafür muss ich mich ja nicht anschnallen“. Das sollte einfach Routine sein/werden, bei Kontextmanagern auch tatsächlich ``with`` zu verwenden. Wobei das im Gegensatz zum Anschnallen, was ja eigentlich keine zusätzliche Arbeit ist, wenn das Routine geworden ist, ja sogar aus dem Dreizeiler einen Zweizeiler macht.

Mit Kodierung ist das `encoding`-Argument gemeint. Wenn man das nicht explizit angibt, ”rät” Python aufgrund der Systemeinstellungen welche Kodierung für den Text verwendet werden soll. Rechner können keinen Text sondern nur Zahlen, also werden Zeichen als Zahl kodiert und dabei ist leider nicht klar welche Zahl(en) für welches Zeichen verwendet werden in Dateien, denn in Dateien gibt es nur eine Folge von Zahlen zwischen 0 und 255 (inklusive). Es gibt einen Grundvorrat bei dem es zumindest in unserem Kulturkreis und wenn man keine Grossrechner verwendet, nahezu egal ist welche Kodierung man verwendet, weil ASCII eine Untermenge von sehr vielen gängigen Kodierungen ist, aber bei bei Ä, ö, ß, € & Co hören die Gemeinsamkeiten dann auf. Viele der klassischen Kodierungen können auch nur 256 unterschiedliche Zeichen kodieren, also ein Byte pro Zeichen, was für Unicode natürlich nicht ausreicht. Wenn man die Kodierung selbst in der Hand hat, bietet sich UTF-8 an. Das kann alle Unicode-Zeichen kodieren und gleichzeitig ist ASCII eine Untermenge. Ist also immer noch platzsparend für viele ”westliche” Texte.

Das mit dem ``+ ".txt"`` ist ungünstig wenn der Benutzer die Endung selbst eingibt, oder eine bereits vorhandene Datei mit Endung im Dateidialog auswählt. Da sollte man vorher prüfen ob die Endung bereits existiert, bevor man sie dann eventuell verdoppelt. Da kommt dann `pathlib` ins Spiel, denn Dateinamen/Pfade sind keine gewöhnlichen Zeichenketten, sondern welche die bestimmten Regeln unterworfen sind, die zudem noch systemabhängig sein können. *Und* man wird dadurch in `addSave()`-Fall auch die Frage ``with`` oder nicht los, weil `Path`-Objekte eine praktische Methode haben um den kompletten Dateiinhalt auf einmal zu schreiben.

Ausserdem müsste man noch den Fall berücksichtigen, dass der Benutzer gar keine Datei auswählt, sondern den Dialog abbricht.

Wenn man per `print()` das gleiche Ausgibt was man auch in die Datei schreibt, sollte man diese Zeichenkette einmal erstellen und nicht zwei mal. Und das dann auch noch mit leicht unterschiedlichem Code.

Warum sind die Platzhalter bei dem `format()`-Aufruf in einer anderen Reihenfolge als die Argumente? Das kann Sinn machen wenn man die Vorlage zum Ausfüllen getrennt vom eigentlichen Ausfüllen hat und die Vorlage nicht zwingend fest ist, weil es dann sein kann, das man verschiedene Vorlagen für die gleichen Daten hat, in denen aber die Reihenfolge nicht immer gleich sein muss. Wenn man das trennt, würde ich aber auch nicht unbedingt die nummerierten Platzhalter verwenden, sondern Namen. Das ist dann verständlicher.

Argumente sind die Werte die man Funktionen beim Aufruf übergibt. Und das sind neben Konstanten die einzigen Werte, die eine Funktion intern benutzen sollte. Das ist der Sinn von (echten) Funktionen — eine in sich geschlossene Einheit zu bilden, wo man Werte rein steckt und ein Erfgebnis heraus bekommt, und zwar über Argument(e) rein, und einen Rückgabewert raus. Die `addSave()`-”Funktion” benutzt beispielsweise einfach so `AusfahrtBerechnung` und `Gebuehren` ohne das man das an der ``def``-Zeile sehen könnte. Dazu muss man die Funktion komplett lesen. Je mehr Funktionen man hat und je länger die werden, um so unübersichtlicher wird das Programm welcher Wert eigentlich wo definiert, verwendet, und verändert wird. Darum verwendet man keine globalen Variablen, also keine Variablen auf Modulebene, und das Hauptprogramm in einer Funktion, und daraus folgt dann, das man Werte als Argumente an Funktionen übergeben muss.

Und daraus folgt bei GUIs mehr oder weniger zwangsläufig, dass man sich mit objektorientierter Programmierung beschäftigen muss, weil man sich bei nicht-trivialen GUIs Zustand über Aufrufe hinweg merken muss.

Code: Alles auswählen

def save(parkdauer, gebuehren):
    filename = filedialog.asksaveasfilename(
        initialdir=Path().absolute().root,
        filetypes=(("Textdatei", "*.txt"), ("all files", "*.*")),
    )
    if filename:
        file_path = Path(filename)
        if not file_path.suffix:
            file_path = file_path.with_suffix(".txt")

        text = f"Parkdauer: {parkdauer} Std.\nZu zahlen: {gebuehren:5.2f} €\n"
        print(text, end="")
        file_path.write_text(text, encoding="utf-8")
`btn` wäre eine kryptische Abkürzung und Funktions- und Methodennamen werden üblicherweise nach der Tätigkeit benannt die sie durchführen. Bei Funktionen/Methoden die Ereignisse behandeln ist manchmal auch `on_<ereignisname>` üblich, also beispielsweise `on_save()` oder `on_exit()`.

Mir fällt gerade auf, das nicht nur die `PhotoImage`-Aufrufe überflüssig sind, sondern auch die `Canvas`-Objekte die da als `master` übergeben werden, nirgends verwendet werden.

Spyder ist IMHO keine so glücliche Wahl zur Programmentwicklung, jedenfalls nicht wenn man Programm in der IDE laufen lässt, weil sich das deutlich anders verhält als Programme normal laufen zu lassen. Es sei denn man kann bei Spyder mittlerweile irgendwo einstellen, das jeder Programmlauf mit einem frischen Kernel passieren soll.

Bei Bildobjekten für Tk(inter) gibt es eine Besonderheit, das man auf der Python-Seite unbedingt sicherstellen muss, dass das Objekt dort bestehen bleibt, also im Programm erreichbar bleibt, weil die `tkinter`-Anbindung keine Ahnung hat ob und wie lange das Bild auf der Tk-Seite benutzt wird, und das einfach abräumt, wenn es auf Python-Seite nicht mehr erreichbar ist. Falls Tk das aber trotzdem noch braucht hat es Pech gehabt und zeigt nichts an, weil nichts mehr da ist.

Die Fenstergrösse fest vorzugeben ist ja ein Teil des Problems. Wenn man konsequent Layouts verwendet, dann ist das Fenster automatisch so gross, das da alles rein passt. Nicht kleiner, aber auch nicht grösser. Und das gilt auch für die ganzen Komponenten die man im Fenster anzeigt.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten