AttributeError: 'int' object has no attribute 'get'

Fragen zu Tkinter.
Antworten
Loppox
User
Beiträge: 4
Registriert: Montag 13. Januar 2020, 21:48

Montag 13. Januar 2020, 21:57

Guten tag an alle die das hier lesen,
Ich bin ein kompletter newbie in Pyhton und wollte mit Tkinter ein Spiel programmieren in welchen man eine Zahl von 1-10 raten muss, falls man falsch liegt die Versuche/Leben gesagt bekommt und ob die Zahl größer oder kleiner ist. Nur hab ich jetzt das Problem das ich den Fehler AttributeError: 'int' object has no attribute 'get' bekomme und keine ahnung habe wie ich den fixen kann.
Hier ist der Code:

Code: Alles auswählen

from tkinter import *
import random

def eingabe():
   eingabe_get= enttry.get()
   le_zahl= Zahlen.get()
   Leben= 3
   Leben = Leben-1
   if Leben == 0:
       fenster.quit
       window = tk.Toplevel(root)
       welcome2 = Label(window2)
       welcome2.grid(row=1,column=0)
       welcome2.config(text="Sie haben Veloren!")
   else:
       if le_zahl == eingabe_get:
           welcome.config(text="Sie haben Gewonnen! Die Zahl war: "+ str(le_zahl))
       elif eingabe_get < le_zahl:
           welcome.config("Die Zahl ist größer. Sie haben noch " +str(Leben)+" Versuche")
       elif eingabe_get > le_zahl:
           welcome.config("Die Zahl ist kleiner. Sie haben noch " + str(Leben) + " Versuche")

Zahlen=random.randrange(1,10)

fenster = Tk()
fenster.title("Rate!")
Labelo= Label(fenster,text="In diesem Spiel musst eine Zahl zwischen 1 und 10 Raten.")
Labelo.grid(row=0,columnspan=2)
welcome= Label(fenster)
welcome.grid(row=2,columnspan=2)

ausgabe = Label(fenster,text="Gebe hier die Zahl an:")
ausgabe.grid(row=1,column=0)
enttry = Entry(fenster,bd=5,width=5)
enttry.grid(row=1,column=1)

b = Button(fenster,text="Eingabe",command=eingabe)
b.grid(row=3,column=0)
quit= Button(fenster,text="Verlassen",command=fenster.quit)
quit.grid(row=3,column=1)


fenster.mainloop()
Der Code sieht nicht so schön aus. Ich danke schon mal vorab allen die Helfen :)
Benutzeravatar
sparrow
User
Beiträge: 1937
Registriert: Freitag 17. April 2009, 10:28

Montag 13. Januar 2020, 23:06

Grundsätzlich ist die komplette Fehlemeldung inklusive Traceback hilfreich, weil die zeigt wo der Fehler auftritt.

Hier scheint "Zahlen" nicht zu sein, was du vermutest.
Benutzeravatar
__blackjack__
User
Beiträge: 6385
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Dienstag 14. Januar 2020, 12:42

@Loppox: Eingerückt wird in Python mit vier Leerzeichen pro Ebene.

Sternchen-Importe sind Böse™. Gerade im Fall von `tkinter` holt man sich damit hunderte Namen ins Modul von denen nur ein Bruchteil benutzt wird. Und nicht nur solche die im `tkinter`-Modul definiert werden, sondern auch alles was dieses Modul seinerseits von woanders importiert.

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 (MixedCase).

Ein Name soll dem Leser vermitteln was der Wert der daran gebunden wird im Kontext des Programms bedeutet. Kryptische Abkürzungen, Pre- oder Suffixe, einbuchstabige Namen, nummerierte Namen, und insbesondere inhaltlich *falsche* Namen sind ganz schlecht. `zahlen` ist beispielsweise falsch. An den Namen werden keine Zahlen — mehrzahl — gebunden, sondern genau *eine* Zahl. Etwas mehr Information darüber was diese Zahl für eine Bedeutung hat, wäre auch nicht schlecht. Also beispielsweise `geheime_zahl` oder `zu_ratende_zahl`.

Bei `labelo` habe ich keine Ahnung was der Name bedeuten soll. Das Objekt braucht im Grunde aber auch gar keinen Namen. Genau wie `ausgabe`. Das oder `ausgabe_label` wäre auch besser als Name für das Label auf dem dann auch tatsächlich eine variable Ausgabe gemacht wird und das unsinnigerweise `welcome` heisst.

Funktionen sind üblicherweise nach der Tätigkeit benannt die sie ausführen. `eingabe()` ist keine Tätigkeit, jedenfalls nicht die Tätigkeit die diese Funktion ausführt. Das ist eher `bewerte_eingabe()` oder `werte_eingabe_aus()`.

Gute Namen sind *wichtig*. Von vornherein. Das kann den Unterschied machen ob man den Code versteht oder nicht, bzw. ob man ihn richtig oder falsch versteht und dann deswegen Fehler macht. Das betrifft nicht nur andere Leser sondern auch die Person die den Code schreibt.

Programmieren heisst unter anderem Wiederholungen in Code und Daten zu vermeiden um Fehler zu vermeiden und Code leichter anpassbar zu machen. Die Unter- und Obergrenze der Zufallszahlen steht jeweils zwei mal im Programm. Da besteht die Gefahr das man einen Fehler macht, entweder beim ersten Schreiben, oder wenn man diese Grenzen mal ändern möchte. Diese beiden Werte sollte man nur *einmal* im Programm stehen haben, als Konstanten.

`randrange()` ist die falsche Funktion wenn man auch die 10 selbst als Möglichkeit haben möchte.

Da auf Modulebene wie weiter oben schon geschrieben Variablen nichts zu suchen haben, müssen Funktionen und Methoden alles was sie ausser Konstanten benötigen als Argument(e) übergeben bekommen. Bei GUI-Code kommt man da im Grunde nicht wirklich um objektorientierte Programmierung herum. Das ist deswegen vielleicht auch nicht wirklich das richtige für den Anfang, insbesondere wenn man noch so viele grundsätzliche Probleme mit Programmlogik hat wie in der `eingabe()`-Funktion zu sehen sind. Die Funktion greift einfach so auf magische Weise auf die zu ratende Zahl, das Eingabe-`entry` und das Label für die Ausgabe zu.

`eingabe_get` ist wieder ein falscher Name. Das `_get` hat da nichts verloren, das ist die Eingabe des Benutzers. Punkt.

Bei `le_zahl` versteht wahrscheinlich keiner ausser Dir was `le_` wohl bedeuten mag. Diese Zeile ist ja aber sowieso Unsinn weil ganze Zahlen keine `get()`-Methode haben und mir echt nicht klar ist was da überhaupt passieren soll(te).

Der Rest der Funktion ist einfach nur logischer Unsinn. `Leben` wird an den Wert 3 gebunden. In der nächsten Zeile wird dann 1 davon abgezogen, also hat `Leben` den Wert 2. Und dann kommt ein ``if`` das testet ob `Leben` 0 ist — was natürlich *nie* der Fall ist, denn wir wissen ja das es an der Stelle den Wert 2 haben *muss*.

Falls durch eine Laune des Univesums Mathematik an der Stelle anders funktionieren sollte ist der Code in dem ``if``-Zweig total Banane. Die erste Zeile bewirkt nichts. Falls man das `quit()` tatsächlich aufrufen würde, wäre an der Stelle die GUI-Schleife zuende und man könnte kein `Toplevel` mehr anzeigen. In der nächsten Zeile haben wir dann einen `NameError` weil `root` nirgends definiert ist. Wäre es das, hätten wir eine Zeile weiter einen `NameError` weil `window2` nirgends definiert ist. Die Nummer hat da nichts zu suchen. Und auch `welcome2` ist wieder ein schlechter Name. Aber dieser Zweig wird ja sowieso nie betreten.

Im ``else``-Zweig wird versucht ein Text den der Benutzer eingeben hat mit einer Zahl zu vergleichen. Die sind *immer* ungleich, weil es keine Zahl gibt die gleich einer Zeichenkette ist. Das heisst es wird dann grundsätzlich in den ``elif``-Zweig gegangen, wo versucht wird festzustellen ob die Texteingabe kleiner der Zahl ist, was zu einem `TypeError` führt, weil so ein Vergleich zwischen Zeichenketten und Zahlen noch weniger Sinn ergibt.

Was man hier machen will ist die Eingabe des Benutzers in eine ganze Zahl zu wandeln, damit man Zahlen miteinander vergleicht.

Zeichenketten und Werte mit `str()` und ``+`` zusammenstückeln ist eher BASIC als Python. Python hat dafür Zeichenkettenformatierung mit der `format()`-Methode und ab Python 3.6 auch f-Zeichenkettenliterale.

Ungetestet:

Code: Alles auswählen

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

UNTERGRENZE = 1
OBERGRENZE = 10


def bewerte_eingabe(geheime_zahl, entry, label):
    eingabe = int(entry.get())
    if geheime_zahl == eingabe:
        label.config(text=f"Sie haben Gewonnen! Die Zahl war: {geheime_zahl}")
    elif eingabe < geheime_zahl:
        label.config(text="Die Zahl ist größer.")
    elif eingabe > geheime_zahl:
        label.config(text="Die Zahl ist kleiner.")
    else:
        assert False, "should never happen"


def main():
    geheime_zahl = random.randint(UNTERGRENZE, OBERGRENZE)

    fenster = tk.Tk()
    fenster.title("Rate!")

    tk.Label(
        fenster,
        text=(
            f"In diesem Spiel musst Du eine Zahl zwischen {UNTERGRENZE}"
            f" und {OBERGRENZE} raten."
        ),
    ).grid(row=0, columnspan=2)

    tk.Label(fenster, text="Gebe hier die Zahl an:").grid(row=1, column=0)
    entry = tk.Entry(fenster, bd=5, width=5)
    entry.grid(row=1, column=1)

    ausgabe_label = tk.Label(fenster)
    ausgabe_label.grid(row=2, columnspan=2)

    tk.Button(
        fenster,
        text="Eingabe",
        command=partial(bewerte_eingabe, geheime_zahl, entry, ausgabe_label),
    ).grid(row=3, column=0)
    tk.Button(fenster, text="Verlassen", command=fenster.quit).grid(
        row=3, column=1
    )
    fenster.mainloop()


if __name__ == "__main__":
    main()
Das mit den drei Leben könnte man da mit einer `tkinter.IntVar` reinbasteln, aber eigentlich will man da wirklich eine Klasse schreiben.
long long ago; /* in a galaxy far far away */
Loppox
User
Beiträge: 4
Registriert: Montag 13. Januar 2020, 21:48

Dienstag 14. Januar 2020, 13:52

Danke für die Antworten und Tipps :D
Antworten