züfälliges Objekt ohne wiederholung mit while schleife

Fragen zu Tkinter.
Antworten
jamyoy
User
Beiträge: 3
Registriert: Sonntag 6. Dezember 2020, 23:25

Hallo Leute
bin anfänger.
habe eine liste von 4 worten die zufällig ausgegeben werden sollen, aber das letzte wort darf sich nicht wiederholen. das soll mit dem while-loop passieren. irgendwie krieg ich s nicht hin
danke leute
Benutzeravatar
__blackjack__
User
Beiträge: 14085
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@jamyoy: Wo ist das konkrete Problem? Was hast Du schon programmiert? Wo verhält sich das dann nicht so, wie Du das erwartest?
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
jamyoy
User
Beiträge: 3
Registriert: Sonntag 6. Dezember 2020, 23:25

das habe ich bis jetzt programmiert. es geht um die methode getschlange. die methode soll einen doppelten schlangennamen vermeiden
from tkinter import *
from random import choice
class M(object):
schlangen = ["Kreuzotter","Ringelnatter","Python","Zornnatter"]
def __init__(self):
self.__schlange = ""
self.__lastschlange = ""
def getschlange(self):
self.__schlange = choice(M.schlangen)
while self.__schlange == self.__lastschlange:
self.getschlange()
self.__schlange = self.__lastschlange
else:
return self.__schlange
def getTitle(self):
return "Schlangennamen"
class V(object):
def __init__(self, komm):
self.fenster = Tk()
self.label = Label(master=self.fenster, text="",font=("ARIAL",28), fg="red")
self.button = Button(master=self.fenster, text="weitere Schlange", width=20, bg="green", fg="white", font=("ARIAL",28), command = komm)
self.bildKreuzotter = PhotoImage(file=".gif")
self.bildRingelnatter = PhotoImage(file=".gif")
self.bildPython = PhotoImage(file=".gif")
self.bildZornnatter = PhotoImage(file=".gif")
self.label.pack(padx = 10,pady =10)
self.button.pack(padx = 10,pady=10)
self.bildKreuzotter.pack(padx = 10,pady =10)
self.bildRingelnatter.pack(padx = 10,pady =10)
self.bildPython.pack(padx = 10,pady =10)
self.bildZornnatter.pack(padx = 10,pady =10)
def anzeigen(self, schlange):
self.label.config(text=schlange)
def setTitle(self, wert):
self.fenster.title(wert)
class C(object):
def __init__(self):
self.m = M()
self.view = V(self.kommando)
self.view.setTitle(self.m.getTitle())
def kommando(self):
schlange = self.m.getschlange()
self.view.anzeigen(schlange)

if __name__ == "__main__":
C()
Benutzeravatar
__blackjack__
User
Beiträge: 14085
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@jamyoy: Jetzt fehlt noch was daran nicht funktioniert. Wenn Fehlermeldungen kommen, bitte gleich mitliefern, damit man das nicht erst selbst herausfinden muss. Du hast die Fehlermeldung ja bereits.

Und die hat nichts mit dem beschriebenen Problem zu tun. `PhotoImage`-Objekte haben keine `pack()`-Methode. Was sollte das denn bringen?

Der gesamte Quelltext hat keine einzige Leerzeile. Üblich sind zwei zwischen Funktionen und Klassen und eine zwischen Methoden.

Sternchen-Importe sind Böse™. Da holt man sich gerade bei `tkinter` fast 200 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.

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

Namen sollten dem Leser vermitteln was der Wert dahinter bedeutet und nicht zum Rätselraten zwingen. Ich rate mal das `M`, `V`, und `C` für `Model`, `View`, und `Controller` stehen sollen. Vergiss diesen MVC-Quatsch gleich wieder. Der ist wie alle Entwurfsmuster dazu da ein Problem zu lösen. Wenn man das Problem gar nicht hat, dann braucht man auch die Lösung nicht. Du pustest da nur unnötig Code auf und machst den unnötig schwerer zu verstehen.

Entscheide Dich für Deutsch *oder* Englisch, am besten Englisch. Am schlimmsten sind Namen wo es *im* Namen gemischt ist.

Vergiss doppelte führende Unterstriche. Das ist *nicht* ``private`` aus anderen Programmiersprachen. Implementierungsdetails werden in Python durch *einen* führenden Unterstrich gekennzeichnet.

Der rekursive Aufruf in `getschlange()` ist auf jeden Fall falsch. Rekursion ist kein Ersatz für einfache Schleifen, zumal der Aufruf ja sogar in einer Schleife steht, die den Job eigentlich erledigen sollte.

Die `bild<Schlangenname>`-Attribute riechen sehr Stark danach, dass das eigentlich *eine* Datenstruktur sein sollte. Eventuell ein Wörterbuch das Schlangennamen auf `PhotoImage`-Objekte abbildet?

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk
import random
from functools import partial

from more_itertools import repeatfunc, unique_justseen

SNAKE_NAMES = ["Kreuzotter", "Ringelnatter", "Python", "Zornnatter"]


class MainWindow(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title("Schlangennamen")
        font = ("Arial", 28)
        self.label = tk.Label(self, font=font, fg="red")
        self.label.pack(padx=10, pady=10)
        tk.Button(
            self,
            text="weitere Schlange",
            fg="white",
            bg="green",
            font=font,
            command=self.change_snake,
        ).pack(padx=10, pady=10)

        self.snake_name_to_image = dict.fromkeys(
            SNAKE_NAMES, tk.PhotoImage(file="test.gif")
        )
        assert len(SNAKE_NAMES) >= 2
        self.random_snake_names = unique_justseen(
            repeatfunc(partial(random.choice, SNAKE_NAMES))
        )
        self.change_snake()

    def change_snake(self):
        self.label["text"] = next(self.random_snake_names)


def main():
    window = MainWindow()
    window.mainloop()


if __name__ == "__main__":
    main()
Was man trennen kann, und wenn es etwas komplexer wird auch sollte, ist die Programmlogik von der GUI. Das Problem was man damit löst ist testbaren Code zu haben. Momentan besteht die Programmlogik ja nur aus einem einzigen simplen Objekt: dem Iterator über die zufälligen Schlangennamen.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
jamyoy
User
Beiträge: 3
Registriert: Sonntag 6. Dezember 2020, 23:25

Vielen Dank.
Jedoch bin ich erst ganz am Anfang und das Programm ist noch nicht 100 % nachzuvollziehen. Mir geht es nur darum das Problem, das nicht zweimal dieselbe Schlange hintereinander angezeigt wird(bei zufälliger Wiedergabe), zu lösen:
class M(object):
schlangen = ["Kreuzotter","Ringelnatter","Python","Zornnatter"]
def __init__(self):
self.__schlange = ""
self.__lastschlange = ""
def getschlange(self):
self.__schlange = choice(M.schlangen)
while self.__schlange == self.__lastschlange:
self.getschlange()
self.__schlange = self.__lastschlange
else:
return self.__schlange

Wie man die Bilder einfügt habe ich jetzt versatnden.
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Aber dass man statt zwei Unterstriche nur eins schreibt, kannst Du schon nachvollziehen? Oder dass M kein guter Name ist.
Das choice muß in die Schleife und der rekursive Aufruf muß weg.
`schlange` muß kein Attribut sein, last reicht.
Benutzeravatar
__blackjack__
User
Beiträge: 14085
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Und wirklich, vergiss MVC bis Du das tatsächlich mal brauchst. Man pflastert nicht auf ”Vorrat” irgendwelche Entwurfsmuster über das Programm.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
MaximalMax
User
Beiträge: 18
Registriert: Sonntag 3. Mai 2020, 00:51

Hallo, ich habe dir jetzt mal eine Lösung geschrieben, da ich das in Eile geschrieben habe, ist es kein Clean Code.
So müsste es gehen, falls es einen Error gibt, gehe in die Shell und gib ein:

Code: Alles auswählen

pip install random
Hier ist die Code-Lösung:

Code: Alles auswählen

import random

words = ["YouTube", "Instagram", "Twitter", "Snapchat"]
counter = 4

while counter != 0:
    random_word = random.choice(words)
    print(random_word)
    words.remove(random_word)
    counter -= 1
Einen schönen Tag dir noch!
Benutzeravatar
sparrow
User
Beiträge: 4540
Registriert: Freitag 17. April 2009, 10:28

@MaximalMax:

random ist ein Modul aus der Standardbibliothek - das muss man nicht mit pip installieren.
Warum hat counter zufällig den Wert der Anzahl der Elemente in "words"? Ist das so gewollt? Wenn ja: Warum nimmst du dann nicht die Anzahl der Elemente in words?
Soll die Schleife so lange laufen, bis die Liste leer ist? Du entfernst je Durchlauf einen Eintrag - also könntest du dir "counter" komplett sparen - die Bedingung wäre dann, dass "words" keine leere Liste ist.

Übrigens ist nicht die Anforderung des Threadstarters, dass jedes Wort aus der Liste nur 1 Mal gewählt wird - die einzige Anforderung ist, dass das _letzte Wort_ nicht noch einmal gewählt wird.
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn man eine Anzahl an zufälligen Werten aus einer Liste möchte, dann benutzt man random.sample.
Hier mal 3 aus 4 Wörtern:

Code: Alles auswählen

words = ["YouTube", "Instagram", "Twitter", "Snapchat"]
print(random.sample(words, 3))
Wenn man alle vier Wörter in zufälliger Reihenfolge möchte, dann ist random.shuffle auch eine Möglichkeit.
Beides ist aber nicht das, was die/der Fragestellende möchte.

Sonstige Anmerkungen: wenn man eine fixe Anzahl an Durchläufen möchte, dann benutzt man keine while-Schleife, sondern eine for-Schleife: `for counter in range(4)`
Antworten