Tkinter, MultiThreading, Prozessstop/-pause

Fragen zu Tkinter.
Antworten
Benutzeravatar
Imaginärteil
User
Beiträge: 12
Registriert: Sonntag 7. Februar 2016, 18:57

Guten Abend, liebe Gemeinde!

Ich habe folgendes Problem und folgendes Ziel:
Was ich erstellen will, ist in eine mit dem GUI auf Tkinter basierte Anwendung,
die mit längeren Prozessen umgehen können soll (sprich Prozess abbrechen, evtl. pausieren).

Es liegt eine *.txt-Datei vor, in der strukturiert Einträge eines Wörterbuchs stehen (Ich bin in JSON-Mgmt noch nicht wirklich involviert, verstehe die Dokumentation noch nicht richtig davon, um es wirklich nutzen zu können). Diese wird nach einem Eintrag mit geg. Eigenschaft durchsucht (etwa Wortart, Bedeutung Sprache A, Bedeutung Sprache B). Da das Durchsuchen länger braucht, liefert das Fenster keine Antwort mehr (Keine Rückmeldung), was zur Folge hat, dass keine Interaktion mehr möglich ist. Was ich noch bräuchte, ist die Option, einen Suchprozess nutzergesteuert zu beenden oder zu pausieren oder noch anderweitig das Programm nutzen zu können.
Ich bin mir sicher, dass ich dazu mit MultiThreading arbeiten muss. Was sich als Problem gestaltet, ist, dass Tkinter-Anwendungen schlecht mit MultiThreading umgehen kann (so heißt es, meint Google). Es heißt, der Main-Loop müsse dafür im MainThread laufen. Nach meiner Interpretation ist der MainThread alles das, was nicht in einem neuen Thread ausgeführt wird. Die Mainloop-Anweisung steht am Ende, nicht in einem neuen Thread. Dazu soll der Nebenthread, in dem genannter Suchprozess läuft, einerseits vom Nutzer über die Tkinter-Oberfläche terminierbar sein und andererseits soll der Thread nach jedem Vorgang einen Ladebalken auf der Oberfläche ansprechen, sodass Fortschritt erkennbar ist und Einträge zeigen, die das Programm durch geg. Suchbefehl gefunden hat.

Ich habe schon versucht von einem Nebenthread aus einen Ladebalken anzusprechen und ".step()en" zu lassen. Das hat nicht geklappt.
Das hier ist der Code, mit dem ich es versucht habe (Erf. Module sind bereits importiert):

Code: Alles auswählen

lock = threading.Lock()
wins1 = tkinter.Tk()
wins2 = ttk.Progressbar(wins1,orient="horizontal",length=450,mode="determinate",maximum=100)
wins2.pack()
def qaa():
    wins2.step(50)

wins1.mainloop()

qa = threading.Thread(target=qaa,daemon=True)
qa.start()
Dieser sollte für mich als einfaches Parade-Beispiel dienen. Geht jedoch schlecht, wenn er nicht funktioniert:

Code: Alles auswählen

Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Program Files\Python35\lib\threading.py", line 914, in _bootstrap_inner
    self.run()
  File "C:\Program Files\Python35\lib\threading.py", line 862, in run
    self._target(*self._args, **self._kwargs)
  File ".../Check Vs 2.py", line 57, in qaa
    wins2.step(50)
  File "C:\Program Files\Python35\lib\tkinter\ttk.py", line 1023, in step
    self.tk.call(self._w, "step", amount)
RuntimeError: main thread is not in main loop
Ich bin da mit meinem Wissen am Ende. Ich weiß nicht, wie ich von einem anderen Thread aus das Fenster ansprechen kann. Und zum anderen bin ich auch gerade ratlos, was es mit dem Pausieren und Abbrechen auf sich hat. Wie man vom MainThread aus (Button auf Tkinter Fenster), den Ablauf in bspw. Thread-1 vollständig unterbricht. Und wie man das Hauptfenster am "Leben" erhält.

Vielen Dank im Vorraus
mfG Im().
Siehe x²=-1. Durch mich ist der Punkt nicht auf einem Strahl, sondern auf einer Ebene. Bewacht von Euler. Bin mit seinem Werk verwandt. Seine Identität befasst sich mit mir. Durch mich ist jetzt so einiges möglich. Hurra!
BlackJack

@Imaginärteil: Der gezeigte Quelltext macht nicht das was Du beschreibst, denn ``wins1.mainloop()`` blockiert so lange bis das Hauptffenster geschlossen wird, also kann auch erst danach der Thread gestartet werden.

Trotzdem sieht man an dem Beispiel das Du versuchst `wins2` von einem anderen Thread aus zu verändern. Das solltest Du nicht tun. Üblicherweise verwendet man Queues um Daten zwischen Threads auszutauschen und regelmässiges Abfragen der Queue auf der GUI-Seite zum Beispiel mittels der `after()`-Methode auf Tkinter-Widgets.

Zum pausieren und abbrechen muss der Code so geschrieben sein, dass er regelmässig prüft ob er pausieren oder abbrechen soll. Da können `threading.Event`-Objekte beispielsweise nützlich sein.

Eine alternative zu Threads wäre es wenn man den Code in kleine Schritte runterbrechen kann, die immer kurz etwas tun und dann zur Hauptschleife zurückkehren und die man mit `after()` oder `after_idle()` aufrufen lässt.
Benutzeravatar
Imaginärteil
User
Beiträge: 12
Registriert: Sonntag 7. Februar 2016, 18:57

btw mach ich das jetzt eigentlich auch schon ein paar Jahre, aber ich merke dann immer, wie viel ich doch nicht verstehe, bei einer Sprache, die übrigens auf unseren Schulrechnern nicht installiert ist (wenn ich in der Schule so ein Programm mitnehmen würde, hätte ich also ein Problem :D), was das angeht, muss ich mich mal schlau machen v. wg. Kompilieren in ausführbare Programme, für Endsysteme, auf denen Python nicht installiert ist, aber das ist eine andere Geschichte.

Dankeschön für die Antwort!

Vielleicht diese Idee?

Code: Alles auswählen

q = Queue()
def s(t):
	while True:
		q.get()
		wins2.step()
Oder nur so?

Code: Alles auswählen

q = Queue()
while True:
	q.get()
	wins2.step()
	haeng_dich_bloss_nicht_auf_und_mach_weiter_im_hauptprogramm()
##[...]
Oder zum Abbrechen einfach:

Code: Alles auswählen

q = 0 ## Ich sach nur "Imaginaerer Hashtag #DasMussDochFunktionieren"
und beim erneuten Starten dann q einfach wieder neu definieren. So einfach. Wenn's stimmt.
Vom Logischen her müsste er dann gar nichts mehr machen, weil man aus einer Queue, die keine Queue ist, keinen Befehl mehr holen kann. Müsste also theoretisch einem Abbrechen gleichen.

Das ganze Programm (alle Zeilen im Haupt-Thread) vielleicht in einen while-True-Schleife packen? Da müsste man für jede Befehlskette eine Variable erstellen, die aussagt ob jene bereits durchgeführt worden ist. Ich würde mir dann ellenlange if-Schleifen vorstellen und lauter unübersichtliche Operationen. Au Weiah. Das wäre das erste Mal, dass ich Methoden wie after() und after_idle() erst höre. In welcher Syntax die man einsetzt ist ebenso fremd. Als Referenz wie f = object.after() zu nutzen? Oder als einfache Methode object.after()? Un wo in welchen Zusammenhang? Ich habe mir schon Finger wund gegoogelt aber ich finde keine Referenz in der ich den Zusammenhang und die Funktionsweise verstehe. Bin eh total schlecht im Googeln. Ich kann mir das einfach nicht vorstellen, wie solche Methoden in einem einzigen Programmablauf sinnvoll sind und was sie tun. Man muss ja eine Vorstellung haben, was bestimmte Befehle wie die in einer Multi-Thread-Architektur machen. Was sie repräsentieren. Wie so ein Wikipedia-Eintrag den man verstehen will aber nicht kann, weil man dazu immer 1000 von Fremdbegriffen nachsuchen muss, die wiederum auch total kompliziert erklärt sind., so fühl ich mich nur gerade dabei.
Denn z. B. qaa.Event ist ein Event-Objekt. Wow. Super. Events muss man triggern können oder müssen was tun, damit es Events sind, wie ich es aus dem RPG-Maker kenne. Wenn meine Interpretationsgabe mich nicht täuscht, ist ein Event salopp einfach ein Boolean, auf das vom jeden Thread aus zugegriffen werden kann.
Und noch ein anderes Problem ist, ich kann keine Schleife (while True: ...) im MainThread machen, weil das entweder die mainloop Anweisung dauerhaft blockiert oder anders herum. Es scheint so etwas zu sein, was man überall in das Programm einweben muss. Kann ich mir einfach null vorstellen. Also angenommen der Suchprozess läuft, dann müsste im Main-Thread eine Schleife sein, die immer wieder überprüft, ob Fortschritt passiert ist durch eine sekundäre Queue, in der alle Fortschritte einzeln eingetragen sind und folglich jedes Mal wins2.step() ausführen. Im Thread-1 müsste der Suchprozess stattfinden, wobei nach jedem Fortschritt an die sekundäre Queue ein Eintrag weitergegeben wird. Bleibt dann aber auch das Fenster aktiv?

Ich verstehe auch immer noch nicht, welcher Befehl allein das Fenster am Leben erhält und wie man Python vorschreiben kann es dauerhaft ansprechbar sein zu lassen. Sollte ich demnächst immer noch nicht auf den Trichter kommen, baller ich einfach den kompletten Quelltext hier rein. Auf der Suche nach der Lösung dafür habe ich mir eigentlich schon die komplette Programmstruktur von diesem Entwurf von unten bis oben vollständig zerschossen. Ich erkenne einfach gar nichts mehr. Ich musste mich blau probieren, weil er die Queue nicht vollständig durchgeführt hat. Habe ich den q.join() dekommentiert und dann gestartet, freezte das Programm bei der allerletzten Bearbeitung der Liste. WARUM AUCH IMMER (verstehe ich bis heute nicht).
Memo an mich selbst: Backuppe deine Programme, bevor du sie mit solchen Aktionen vollkommen zerstörst.

Das waren jetzt die ganzen die Gedanken, die mir zur Idee gekommen sind.
Also kurzgesagt bin ich einfach gerade total verwirrt, nach dem ganzen erneuten Googeln daraufhin jetzt. Buffer Overflow. VM out of heap. Ich wollte das eigentlich alles "einfach mal machen", heute, und auch heute fertig werden. Aber ich glaube, ich muss noch mal eine Nacht drüber schlafen. Was ich nicht glaubte, war, dass das so ein kompliziertes und verwurstetes und verzwirbeltes Konstrukt werden würde. Wird es aber. Deswegen muss ich mir noch mal alles neu zusammengrübeln. Verdammte Objektorientierte Modellierung.

Code: Alles auswählen

wins1 = tkinter.Tk(dontfreeze=True)
## Hach, wenns doch nur so einfach wäre.
Siehe x²=-1. Durch mich ist der Punkt nicht auf einem Strahl, sondern auf einer Ebene. Bewacht von Euler. Bin mit seinem Werk verwandt. Seine Identität befasst sich mit mir. Durch mich ist jetzt so einiges möglich. Hurra!
BlackJack

@Imaginärteil: Die erste Variante hat `q` als modulglobale Variable, was man nicht machen sollte, und bei der zweiten ist ja sogar der gesamte Code auf Modulebene.

An einen Namen eine Zahl zu binden um damit einen AttributeError zu provozieren ist keine gute Idee.

Das sieht auch grundsätzlich nicht so aus als würde das funktionieren, denn Du hast da eine Endlosschleife mit einem blockierenden Aufruf auf der Queue und Code der die GUI ändert. Das kann ja so weder im Hauptthread stehen, denn dann blockiert das die GUI, noch kann das in einem anderen Thread laufen, denn es verändert die GUI.

`after()` und `after_idle()` sind Methoden auf allen Tk-Widget-Objekten (ist also im Grunde egal auf welchem man die aufruft) mit denen man Funktionen/Methoden registrieren kann die entweder nach einer bestimmten Zeit von der GUI-Hauptschleife aufgerufen werden, oder sobald die GUI-Hauptschleife gerade alle GUI-Ereignisse verarbeitet, so dass kurz Zeit ist etwas anderes zu machen. Das hat per se erst einmal nichts mit Multithreading zu tun, sondern ist ein Mechanismus um eigene Aktionen mit denen von der GUI in einem Thread zu ”vermischen”.

`threading.Event()` ist in der Tat ein threadsicher gekapselter Wahrheitswert, den man setzen, löschen, abfragen, und auf das ”gesetztwerden” blockierend warten kann, ohne „busy waiting“ zu betreiben.

Weder ereignisbasierte Programmierung, was GUI-Programmierung in der Regel ist, noch nebenläufige Programmierung (Threads/Prozesse die zusammen arbeiten) sind wirklich einfach. Bei beidem hat man nicht mehr einen linearen Programmablauf den man einfach so im Code ”runter lesen” kann.

Wenn der Suchprozess läuft, dann darf im GUI-Thread keine Schleife stehen die den Fortschritt abfragt, denn die würde die GUI blockieren, da hätte man auch gleich den Suchprozess in diesem Thread durchführen können. Der Fortschritt muss regelmässig abgefragt werden in dem man dem Tk sagt es soll so eine Funktion nach einer gewissen Zeit aufrufen. Die fragt dann kurz den Fortschritt ab und aktualisiert den Fortschrittsbalken und sagt Tk dann, dass sie wieder nach einer gewissen Zeit aufgerufen werden soll. Zwischen diesen aufrufen kann die GUI-Hauptschleife sich darum kümmern dass das Fenster nicht ”einfriert”.

Das Fenster wird durch eine laufende GUI-Hauptschleife am leben gehalten. Also dadurch das man in Rückruffunktionen die von der GUI-Hauptschleife aufgerufen werden, nichts macht was so lange dauert das der Benutzer das an einer eingefrorenen GUI bemerkt.

Zu den Sicherheitskopien: Dafür benutzt man Versionskontrollen wie Git oder Mercurial.
Benutzeravatar
Imaginärteil
User
Beiträge: 12
Registriert: Sonntag 7. Februar 2016, 18:57

i.
Wo liegen bei der Namensgebung der Variablen die Grenzen? Wann funktioniert es denn nicht mehr? Wenn man die Grenzen kennt und es funktioniert, dann spricht ja nichts dagegen, Namen sind ja da zur Orientierung (und manche können mit Zahlen mehr anfangen als mit Namen). Angenommen, man hat eine Grafik auf dem 2. Monitor und hat dort jede Eigenschaft und Variable kurz und bündig als Zahl notiert. Das würde ja gehen. Wenn das nicht geht, kann man immer noch auf das Alphabet zurückgreifen, sprich a,b,c,d,...x,y,z,aa,ab,...ax,ay,az,ba,bb,...zx,zy,zz,aaa,... u. s. w.
Den Namen sind ja gut und schön, aber irgendwann werden die lang. Höheres Verschreibrisiko.

ii.
Habe ich das richtig verstanden? Die .after()-Methode induziert meiner Interpretation nach eine periodische Schleife, deren Funktion callback mit Argument *args nach delay_ms * 10^(-3) s periodisch immer wieder ausgeführt wird. Sollte ich die dann induzieren, wenn der Suchprozess in die Wege geleitet wird, sprich an den Anfang der Funktion packen, auf die ein Button verweist? Mit einer Funktion f (Mathematiker-Reflex, hier nur zum Verständnis, im Projekt wird ein anderer Name verwendet), in der steht:

Code: Alles auswählen

## Sei window_progressbar hier das ttk-Progressbar-Widget im Haupt:Fenster
q_fortschritt = Queue()
def f():
    while(not q_fortschritt.empty()):
        fortschritt = q.get()
        window_progressbar.step()
        q.task_done()
(Irgendwie funktioniert TAB gerade nicht im Browser, deswegen habe ich vier Leerzeichen genommen)

iii.
Das mit .after_idle() begreife ich nicht so ganz, das verstehe ich als einfachen Aufruf, der getätigt wird, wenn das Fenster im Leerlauf ist. Heißt Leerlauf in diesem Falle, dass der Suchbutton gedrückt wurde und alles im Hintergrund läuft und das Fenster nichts mehr tut? Oder ist es genau das Gegenteil, als Aufruf, wenn der Suchprozess abgeschlossen ist?

iv.
Ich weiß ja im Generellen nicht so ganz, wie ich das umzusetzen habe, damit das Fenster wach bleibt. Ich stelle mir vor, dass wenn die Suche läuft, der Text des Buttons "Suchen" zu "Abbrechen" gesetzt wird und dessen "onClick-Event" dann die Funktion bspw. abbrechen() wird, die ein Event aktiviert, das in der for-Schleife im Suchthread, in dem noch gesucht wird, break aktiviert und somit den Suchprozess anhält. Problem wird nur das join(). Wo setze ich das am besten hin? Oder brauche ich das überhaupt?
Siehe x²=-1. Durch mich ist der Punkt nicht auf einem Strahl, sondern auf einer Ebene. Bewacht von Euler. Bin mit seinem Werk verwandt. Seine Identität befasst sich mit mir. Durch mich ist jetzt so einiges möglich. Hurra!
BlackJack

@Imaginärteil: Die Anzahl Leute die sich anhand einer Zahl eine Bedeutung eher merken können als mit einem sprechenden Bezeichner dürfte gegen Null gehen. Es sind jedenfalls Ausreisser die man sich nicht als Vorbilder für lesbaren Quelltext hernehmen sollte.

Angenommen man hat keinen zweiten Monitor — und warum überhaupt eine *Grafik* mit Zahlen und Beschreibungen‽ Ich habe da gerade die Zettelwirtschaft vor Augen die man vor 20 bis 40 Jahren oft nebenbei beim Programmieren herumfliegen hatte, wo man sich tatsächlich solche Notizen machte, allerdings weil man sich damals mit Sprachen herumschlagen musste, die keine Namensräume boten und wo teilweise aus technischen Gründen keine sinnvolle Länge für Bezeichner möglich war. BASIC-Programme für 8-Bit-Homecomputer, teilweise mit Namen die auf zwei Zeichen plus einem Sonderzeichen für den Datentyp. Diese Zeiten sind $GOTT sei Dank vorbei. Ausser wenn man sich heute immer noch gerne mit diesen Maschinen beschäftigt. :-)

Gute Namne müssen, und sollten auch nicht wirklich lang sein, aber lang genug um präzise und beschreibend zu sein. Lang müssen sie nicht werden weil es Namensräume gibt, und man selten mehr 10 Dinge unterschiedlich benennen muss. Der Kontext (Package/Modul, Funktion, Klasse, Methode) kommt zum Namen ja erklärend hinzu, ohne das man ihn im Namen selbst noch einmal verewigen muss. Bezüglich der Verschreiber: Das wäre selbst dann kein valider Grund nichtssagende kurze Namen zu verwenden, wenn nicht bereits die simpelsten Editoren heutzutage schon Autovervollständigung bieten würden. Zum einen hat man sich bei Kurznamen auch schnell mal verschrieben und irgendwo `x2` statt `x3` geschrieben, was wesentlich schlechter durch drüberlesen zu finden ist als wenn man `total_sum` und `interest_rate` verwechselt hätte, zum anderen wird Quelltext wesentlich öfter gelesen als geschrieben/verändert, und da sollte man lieber das Lesen und Verstehen vereinfachen, statt irgendwo ein paar Tastendrücke sparen zu wollen damit man weniger Gefahr läuft sich zu vertippen. Verschreiber werden leicht durch (automatisierte) Tests gefunden.

`after()` ist von sich aus nicht periodisch sondern lässt eine Funktion/Methode nur *einmal* ausführen. Aber die kann an ihrem Ende sich selbst wieder mit `after()` für einen weiteren Aufruf registrieren.

`after_idle()` ist im Grunde das gleiche, nur dass man keine Zeit angibt und Tk die Funktion so schnell wie möglich aufruft wenn es für die GUI nichts zu tun gibt. Das ist fast immer der Fall, denn die allermeiste Zeit wartet eine GUI auf den Benutzer, denn der ist wesentlich langsamer als eine CPU.
Benutzeravatar
Imaginärteil
User
Beiträge: 12
Registriert: Sonntag 7. Februar 2016, 18:57

Ok, das mit after_idle(...) funktioniert.
Die Anweisung selbst funktioniert.

Ich habe jetzt Threads eingerichtet, die immer wieder vom entsprechenden Queue (jew. Fortschritt und Ergebnis) Aufgaben bekommen und deren Operation auch im Hauptfenster ausführen (wie gesagt durch after_idle().).
Aber auch wenn er jetzt immer zum Mainloop zurückkehren sollte, tut er es anscheinend nicht.
Eine interessante Sache:
Zeilen, die dies zu beheben scheinen, sind diese hier:

Code: Alles auswählen

lock = threading.Lock()
und in der Funktion, die jeden Eintrag einzeln durchsucht:

Code: Alles auswählen

with lock:
	print(...)
Fehlen diese Zeilen, ist es dasselbe Ergebnis wie zuvor: Das Fenster hängt sich auf.
Und: ich verstehe einfach beim besten Willen nicht, warum es ohne die Zeilen nicht funktioniert. Das gibt keinen Sinn!

Braucht selbst das Kommandozeilenfenster Reaktion, damit das Programm sich nicht aufhängt?!
(Ich will das Konsolenfenster eigentlich gar nicht benutzen!)

Ebenfalls verstehe ich überhaupt nicht, was threading.Lock() überhaupt tut und inwiefern with lock: die Art und Weise ändert, mit der der gegebene Block ausgeführt wird.
Siehe x²=-1. Durch mich ist der Punkt nicht auf einem Strahl, sondern auf einer Ebene. Bewacht von Euler. Bin mit seinem Werk verwandt. Seine Identität befasst sich mit mir. Durch mich ist jetzt so einiges möglich. Hurra!
BlackJack

@Imaginärteil: Was Du beschreibst kann so eigentlich nicht sein. Da fehlt jetzt aber Code um zu sehen was Du denn nun *tatsächlich* tust.
Benutzeravatar
Imaginärteil
User
Beiträge: 12
Registriert: Sonntag 7. Februar 2016, 18:57

OK, ich versuche es mal abzubilden:

Code: Alles auswählen

suchQueue = Queue()
fortschrittQueue = Queue()
ergebnisQueue = Queue()
lock = threading.Lock()
def einzelsuchen(eingang):
	suchErfolg = False
	##...##
	if(not eintrag == "UNGÜLTIG"):
		eintrag_wort = word() # Als Klasse bereits definiert
		with lock:
			print(eintrag)
		if(eintrag[1] in eintrag[2]):
			suchErfolg = True
	if suchErfolg:
		ergebnisQueue.put(eintrag_wort)

def sucher():
	while True:
		task=suchQueue.get()
		einzelsuchen(task)
		suchQueue.task_done()

SRED_a = threading.Thread(target = sucher)
SRED_a.start()

def fortschrittMeter():
	while (not fortschrittQueue.empty()):
		fortschrittQueue.get()
		ladebalken.step()
		fortschrittQueue.task_done()
	ladebalken.after(100,fortschrittMeter)
def ergebnisMeter():
	while (not ergebnisQueue.empty()):
		ergebnis = ergebnisQueue.get()
		liste.insert("end",ergebnis)
		ergebnisQueue.task_done()
	liste.after(100,ergebnisMeter)
def suchen():
	liste.delete(0,"end")
	for aktuelleNummer in range(1,dic_anz_eintraege+1):
		anfragemitnummer = ...
		suchQueue.put(anfragemitnummer)
		fortschrittQueue.put("1")

main_window = tkinter.Tk(className = "...")

... PACKEN JEDES EINZELNEN ELEMENTS DES GUI ...

main_window.mainloop()
Ich muss aber sagen, dass ich nur after() verwendet habe. Ich habe hier noch nicht after_idle() benutzt.
Ich hoffe da ist irgendwo der markante Punkt dabei. Es ist wirklich so. Wenn ich die with lock Sache entferne freezt das Fenster.
Siehe x²=-1. Durch mich ist der Punkt nicht auf einem Strahl, sondern auf einer Ebene. Bewacht von Euler. Bin mit seinem Werk verwandt. Seine Identität befasst sich mit mir. Durch mich ist jetzt so einiges möglich. Hurra!
BlackJack

@Imaginärteil: Da sieht man ja jetzt immer noch nicht den Programmablauf weil zu viel fehlt. Auf der anderen Seite ist sicher viel dabei was zum Problem nichts beiträgt.

Was auffällt ist aber, dass das alles ganz furchtbar unsauber gelöst ist, mit globalen Variablen, und das die Namensschreibweisen nicht dem Style Guide for Python Code entsprechen. Die Einrücktiefe sollte vier Leerzeichen pro Ebene betragen, nicht drei. Leerzeilen zwischen den Funktionen würden der Lesbarkeit dienen. Es sind auch deutlich zu viele Klammern in dem Quelltext. Kann es sein das Du versuchst eigentlich in einer anderen Programmiersprache als Python zu programmieren?

`eintrag` ist entweder eine Zeichenkette oder ein Tupel (oder etwas ähnliches), das ist nicht gut. Ein Name sollte immer nur an *einen* (duck) Typ gebunden werden, sonst verwirrt man den Leser und braucht Sonderbehandlungen für die besonderen Werte. Für ”Fehlerwerte” gibt es zudem Ausnahmen als Sprachmittel. Die magischen Indexwerte sind auch nicht gut. Hätte man da kein `collections.namedtuple` verwenden können? Oder eine eigene Klasse?

Wenn man mit `task_done()` arbeitet, sollte man sehr penibel sicherstellen dass die Methode auch tatsächlich aufgerufen wird, auch wenn die Verarbeitung eines Elements beispielsweise zu einer Ausnahme führt. Sonst ist ein `join()` am Ende eine Operation die niemals erfolgreich beendet wird, und das Programm hängt an der Stelle.

`einzelsuchen()` ist ein komisches Wort. Und `sucher()` kein guter Funktionsname weil er keine Tätigkeit beschreibt.
Benutzeravatar
Imaginärteil
User
Beiträge: 12
Registriert: Sonntag 7. Februar 2016, 18:57

Ich habe hier beim Schreiben mit Tabulatoren gearbeitet, liegt am Browser, dass es drei Einrückungen hat.
Ich halte auch ein join() im Moment nicht sinnvoll, weil das Suchen eigentlich immer "in Bereitschaft ist".
Kann es vielleicht auch an Windows liegen? An einer Fehleinstellung? Ich überlege im Moment sowieso, die Platte bald zu formatieren.
Die grundlegende Tätigkeits-verwaltungsmechanik steht im Beispiel schon drinnen. Ich wäre dann wohl oder übel dazu gezwungen einfach das ganze Ding da rein zu schmeißen.
Siehe x²=-1. Durch mich ist der Punkt nicht auf einem Strahl, sondern auf einer Ebene. Bewacht von Euler. Bin mit seinem Werk verwandt. Seine Identität befasst sich mit mir. Durch mich ist jetzt so einiges möglich. Hurra!
Antworten