try schleife erkennt except nicht

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
Hugin
User
Beiträge: 7
Registriert: Freitag 10. Januar 2020, 10:50

Hallo zusammen,

bin hier schon 2 Tage am grübeln über folgendes:
#Es handelt sich um eine Übung in einem Buch. Die verlangt ein Script zu schreiben das Einträge in eine Liste vornimmt.
#Das Programm schaute am Schluss ein bischen anders aus aber es hat gemacht was es sollte. Es hat halt alles eingetragen was man ihm vorgeschrieben hat.

Ich wollte nun noch ein paar Abfragen einbauen (Übungen aus den vorherigen Kapiteln)

hier mal der Code:

Code: Alles auswählen

f = open("Bestellliste.txt", "a")

while True:
    artikelnummer = eval(input("Geben Sie bitte die 5 Stellige Artikelnummer ein, zum abbrechen bitte 'quit' eingeben : "))
    artikelnummer = (str(artikelnummer))
    a = (len(artikelnummer))
    if artikelnummer == "quit":
        break
    try:
        if a < 5:
            print ("Artikelnummer zu kurz")            
        elif a > 5:
            print ("Artikelnummer zu lang!")     
    except ValueError:
        print ("Bitte nur 5 Stellige Zahlen eingeben!")
    except NameError:
        print ("nur Zahlen!")   
    finally:
        if a == 5:
            print("erledigt!")
            f.write (artikelnummer+"\n")
    f.close
print ("Auf wiedersehen!")
Es funktioniert soweit.. ausser 2 Ausnahmen:

problem1 = Das erste ist das wenn ich Buchstaben eingebe, eigentlich die except Schleife laufen sollte mit ValueError oder NameError
diese springt jedoch nicht an und das Programm wird mit dem "Error-Wert" abegebrochen. Obwohl die finally Schleife wirkt!

Warum zum T***** schaltet das except nicht ???

problem2 = Die zweite wäre das wenn eine 0 oder 5x 0 eingegeben wird alles auseinanderfällt.
z.B. wird die Artikelnummer, die ja 5 Stellen haben muss, 01234 nicht erkannt.
oder 0000000000 wird als zu kurz ausgegeben.
Kann mir schon vorstellen das es daran liegt das ich die "int" eingabe in einen "str"ing umwandle um die len() abzufragen.
Wie kann ich trotzdem eine 0er Artikelnummer in die Liste bringen und trotzdem durch die Schleife laufen lassen?

Mir raucht schon der Schädel....

Grüße
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

Das Problem ist das eval, dass man eh nicht benutzen sollte. Du musst die ganze Zeit mit Strings arbeiten. isdigit ist hilfreich. a ist ein sehr schlechter Name für eine Variable. Innerhalb des try-Blocks kann weder ein ValueError noch ein NameError auftreten, weil nur Zahlen mit Zahlen verglichen werden. Ein NameError ist auch immer ein Programmierfehler und sollte nicht abgegangen werden (müssen). Das close ist an der falschen Stelle und wird auch nicht aufgerufen. Um solche Fehler zu vermeiden, benutze das with-Statement.
Benutzeravatar
sparrow
User
Beiträge: 4535
Registriert: Freitag 17. April 2009, 10:28

Zu Problem 1:
Damit der except-Block ausgeführt wird, muss im try-Block ja irgendwo eine Ausnahme auftreten.
Bei dir stehen im try-Block 2 if-Bedingungen und 2 print-Anweisungen. Wie genau soll es denn da zu einer Exception kommen, wegen der der Except-Block ausgeführt wird?

Gewöhn dir besser sofort an Variablen vernünftige Namen zu geben. Sprechende Namen, die dem Leser (also dir) sagen, was sie sind.

Was in finally steht, sollte mit in den try-Block. Und zwar als letzter else-Zweig. Finally verwendet man um "aufzuräumen". Zum Beispiel offene Verbindungen oder Dateien kontrolliert zu schließen.
Apropos schließen: Du schließt deine geöffnete Datei nicht wieder. Close wird nicht aufgerufen, dafür fehlen die Klammern dahinter.
Besser: benutze open mit dem with-Statement. Dann kannst du dir das Schließen sparen.
"f" ist wieder ein schlechter Name.

Und zuletzt: Hinter den Methodennamen und die öffnende Klammer gehört kein Leerzeichen. Also print( nicht print ( etc.
Benutzeravatar
kbr
User
Beiträge: 1507
Registriert: Mittwoch 15. Oktober 2008, 09:27

@Hugin: ich habe dir den Code in ein sauberes Gerüst umgeschrieben, so dass du nun die Prüfung auf Ziffern hinzufügen kannst:

Code: Alles auswählen

with open('bestell_liste.txt', 'a') as fobj:
    while True:
        value = input('Bitte 5 stellige Artikelnummer eingeben (quit zum beenden): ')
        if value.lower() == 'quit':
            break
        if len(value) != 5:
            print('Nur 5 stellige Artikelnummern erlaubt.')
        else:
            # prüfe ob value nur Ziffern enthält
            # falls ja: speichere value
            pass
print('exit') 
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Ich würde das komplett ohne Try/Except lösen und mir eine Funktion schreiben, die prüft ob die eingegebene Zahl dem gewünschten Format entspricht. (Hier: 5 Zeichen, nur Zahlen)

Beispiel:

Code: Alles auswählen

def is_valid_item_number(item_number):
    return len(item_number) == 5 and not any(number.isalpha() for number in item_number)


while True:
    eingabe = input("> ")
    if is_valid_item_number(eingabe):
        with open("Bestellliste.txt", "a", encoding="UTF-8") as f:
            f.write(f"{eingabe}\n")
            print("Erledigt!")
    else:
        print("Die Artikelnummer darf nur 5 Zeichen lang sein und darf nur Zahlen enthalten!")
Benutzeravatar
sparrow
User
Beiträge: 4535
Registriert: Freitag 17. April 2009, 10:28

@Jankie: So beraubt man sich der Möglichkeit auf den tatsächlichen Fehler hinzuweisen. Stimmt die Länge oder der Inhalt nicht?

Der Einsatz von .isalpha() ist hier falsch, denn du schließt so nur Buchstaben aus.
Die Zeichen-für-Zeichen-Überprüfung könntest du dir auch sparen, denn isalpha() prüft auf eine ganze Zeichenkette, ob sie nur aus Buchstaben besteht. Das Ergebnis ist also identisch.

Code: Alles auswählen

>>> is_valid_item_number("§$%/$")
True
Zuletzt geändert von sparrow am Montag 20. Januar 2020, 11:13, insgesamt 1-mal geändert.
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Wenn ein genaures Format gewünscht ist stimme ich dir zu, aber wenn es - wie hier in dem Beispiel - nur zwei Bedinungen gibt würde ich das dem Benutzer noch zutrauen den Fehler den er gemacht hat zu finden.

Habe gemerkt das man mit .isdigit() auch ganze Strings testen kann, also hier noch mal der überarbeitete Code, bei dem auch keine Sonderzeichen mehr gehen.

Code: Alles auswählen

def is_valid_item_number(item_number):
    return len(item_number) == 5 and item_number.isdigit()


while True:
    eingabe = input("> ")
    if is_valid_item_number(eingabe):
        with open("Bestellliste.txt", "a", encoding="UTF-8") as f:
            f.write(f"{eingabe}\n")
            print("Erledigt!")
    else:
        print("Die Artikelnummer darf nur 5 Zeichen lang sein und darf nur Zahlen enthalten!")
Benutzeravatar
__blackjack__
User
Beiträge: 14030
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Jankie: `isdigit()` ist auch falsch vermute ich mal, denn genau wie `int()` kennt auch `isdigit()` deutlich mehr Zeichen als nur 0 bis 9 als Ziffern.

Code: Alles auswählen

In [6]: "୨୩".isdigit()                                                          
Out[6]: True
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Wie prüft man das denn richtig? Ich hab jetzt einfach noch eine Bedingung hinzugefügt, also .isascii(). Wenn jemand eine Antwort weiß kann er mir die gerne auch per PN schicken, damit ich den Thread hier nicht vollspamme mit Sachen die nur peripher zu Thema gehören.

Code: Alles auswählen

def is_valid_item_number(number):
    return len(number) == 5 and number.isascii() and number.isdigit()
Benutzeravatar
kbr
User
Beiträge: 1507
Registriert: Mittwoch 15. Oktober 2008, 09:27

Du könntest z.B. mit einem regulären Ausdruck auf fünf aufeinander folgende Ziffern testen. Oder versuchen, jedes Zeichen im input in einen Integer zu konvertieren. Oder prüfen, ob jedes Zeichen in string.digits ist. Geht vielleicht auch noch anders, aber die drei Möglichkeiten fallen mir gerade ein. Die zweite Möglichkeit gefällt mir am wenigsten. Die letzte ist vermutlich die einfachste. Mit der ersten holt man direkt den Knüppel aus dem Sack 8)
Benutzeravatar
sparrow
User
Beiträge: 4535
Registriert: Freitag 17. April 2009, 10:28

Die zweite Möglichkeit fällt aus, wie __blackjack__ bereits gesagt hat:

Code: Alles auswählen

int("୨୩")
Benutzeravatar
kbr
User
Beiträge: 1507
Registriert: Mittwoch 15. Oktober 2008, 09:27

Ah, hatte ich überlesen. Gut, dass sie mir am wenigsten gefiel ... :)
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

RegEx erkennt "୨୩" auch als Zahl.

Code: Alles auswählen

import re
print(re.findall(r'\d+', "TEST ୨୩ 30"))
> ['୨୩', '30']


So würde es auch noch gehen, aber dann fängt es an unübersichtlich zu werden.

Code: Alles auswählen

def is_valid_item_number(item_number):
    return len(item_number) == 5 and all(number in map(str, range(0, 10)) for number in item_number)
Benutzeravatar
kbr
User
Beiträge: 1507
Registriert: Mittwoch 15. Oktober 2008, 09:27

Es kommt darauf an, dass reguläre Ausdrücke korrekt verwendet werden:

Code: Alles auswählen

>>> import re
>>> print(re.match(r'[0-9]{5}', "56a89"))
None
>>> print(re.match(r'[0-9]{5}', "56୨୩89"))
None
>>> print(re.match(r'[0-9]{5}', "56789"))
<_sre.SRE_Match object at 0x10561b7e8>
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Frage ist doch, ob Ziffern aus anderen Kulturkreisen denn nun wirklich keine Ziffern sind. Ich würde isdigit als richtig ansehen.
Ansonsten der reguläre Ausdruck "[0-9]{5}".
Hugin
User
Beiträge: 7
Registriert: Freitag 10. Januar 2020, 10:50

Woow.. erstmal, danke für eure mühen!

Werd das mal versuchen mit den Tipps die Ihr mir gegeben habt.
Habs im vorfeld mit den typen direkt probiert sprich int(), float(), eval war nur ne notlösung als Versuch.
Inzwischen bin ich bei einfach nur wieder bei input(). Jeder type bringt hier seine eigenen probleme mit.

Aber ich werd es wohl von Grund auf neu anpacken und mich mit den funktionen und den ideen hier versuchen.
Hab hier ja genug inspiration bekommen.

Die re.match variante ist sehr interessant !

Und gern "hier" weiter diskutieren!
Antworten