Threading in Python 3

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.
PythonCodingFun
User
Beiträge: 49
Registriert: Mittwoch 22. September 2021, 14:01

Hallo liebe Community :-)

ich habe in Python 3 eine Klasse geschrieben (mit Getter und Setter) diese hab ich in einem anderen Thread ausgeführt (mit _thread ) aber wie kann ich diesen Thread via setter noch nachträglich Parameter übergeben, also nach dem ebendieser Thread gestartet ist ?

Über Rückmeldung würde ich mich sehr freuen.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

_thread darf nicht nicht benutzen, das erkennt man am Unterstrich.
Daten dürfen nicht in unterschiedlichen Threads verändert werden, dafür gibt es passende Datenstrukturen, wie Events, Queues, etc.
Was in Deinem Fall richtig wäre, können wir nicht sagen, weil der Kontext und Code fehlen.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Du solltest weder Setter und Getter noch _thread verwenden. Namen mit führendem Unterstrich bedeuten in Python: Implementierungsdetail, wer das verwendet, tut das auf eigene Gefahr. Geich im ersten Absatz der Dokumentation zu _thread steht: "The threading module provides an easier to use and higher-level threading API built on top of this module." Das solltest du verwenden. Da im Inhaltsverzeichnis der Dokumentation zur Standardbibliothek threading mehrere Einträge über _thread steht, frage ich mich, warum soviele Anfänger die Dokumentation von unten nach oben (d.h. von hinten nach vorne) zu lesen scheinen.

Und was Getter und Setter angeht: Das macht man in Python nicht. Python ist nicht Java. Wer Java programmieren will, soll Java programmieren, nicht Python.

Daten an laufende Threads übergibt man am einfachsten mittels einer queue.Queue. Dann braucht man sich auch keine Sorgen um konkurrierende Schreibzugriffe zu machen.
In specifications, Murphy's Law supersedes Ohm's.
PythonCodingFun
User
Beiträge: 49
Registriert: Mittwoch 22. September 2021, 14:01

pillmuncher hat geschrieben: Mittwoch 13. Oktober 2021, 14:51

Und was Getter und Setter angeht: Das macht man in Python nicht. Python ist nicht Java. Wer Java programmieren will, soll Java programmieren, nicht Python.
In dem Buch https://www.springer.com/de/book/9783658264963 wird aber geschrieben das man mit Getter und Setter Arbeiten soll wenn man Python zum OOP verwendet will.

Und Getter setter gibt es nicht nur in Java sondern auch in C# usw.
PythonCodingFun
User
Beiträge: 49
Registriert: Mittwoch 22. September 2021, 14:01

pillmuncher hat geschrieben: Mittwoch 13. Oktober 2021, 14:51

Daten an laufende Threads übergibt man am einfachsten mittels einer queue.Queue. Dann braucht man sich auch keine Sorgen um konkurrierende Schreibzugriffe zu machen.
und mit Queue kann man ein einen bereits laufenden Thread Daten übergeben ?!
Zuletzt geändert von PythonCodingFun am Mittwoch 13. Oktober 2021, 15:41, insgesamt 1-mal geändert.
PythonCodingFun
User
Beiträge: 49
Registriert: Mittwoch 22. September 2021, 14:01

Sirius3 hat geschrieben: Mittwoch 13. Oktober 2021, 14:48
Was in Deinem Fall richtig wäre, können wir nicht sagen, weil der Kontext und Code fehlen.
Ich will einen weiteren Thread aufmachen der die Datenbank trigger ausgibt, dazu verwende ich die Lib psycopg [url]https://www..org/docs/advanced.html#asynchronous-notifications[/url]
Zuletzt geändert von PythonCodingFun am Mittwoch 13. Oktober 2021, 15:22, insgesamt 1-mal geändert.
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es gibt eine erstaunlich grosse Anzahl schlechter Werke ueber Python. Das scheint eines davon zu sein. Es ist seit *Jahrzehnten* bekannt, dass getter/setter in Python nicht idiomatisch sind. Wer das nicht weiss, hat offensichtlich bestenfalls oberflaechliche Kenntnisse in Python. Die Vorschaukapitel auf der Webseite bestaetigen diese Vermutung.

Was deine eigentliche Frage angeht: ja, man kann die Daten uebergeben, aber nicht, ohne Code und dessen Ablauf umzuschreiben. So ein Message-Passing kann ja nicht einfach per magischer Fernwirkung funktionieren, das muss schon einprogrammiert werden.
PythonCodingFun
User
Beiträge: 49
Registriert: Mittwoch 22. September 2021, 14:01

Also ganz ähnlich zu dem Beispiel https://www.psycopg.org/docs/advanced.h ... ifications hab ich auch eine Endlosschleife und ich wollte diese Schleife halt stoppen/beenden (dachte mit Getter/setter) und wollte argumente übergeben die dies bezwecken. Nur das die Funktionalität in einem anderen Thread läuft, und ich will den Thread nicht hart killen oder der gleichen, sondern "sanft" beenden.

Gibt es eine gute Ideen/Ansätze wie man das realisieren kann ?
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn es nur um ein simples boolsches "running" geht, kannst du das auch einfach via Attribut machen. Also sowas:

Code: Alles auswählen

class NotificationHandler:

   def __init__(self):
        self.running = True
        t = threading.Thread(target=self._loop)
        t.start()
    def _loop(self):
          while self.running:
                  # tu was

notification_handler = NotificationHandler()
time.sleep(10)
notification_handler.running = False
Ja, ganz strenggenommen muss man ein threading.Event oder sowas benutzen, aber da wir nunmal in CPython das GIL haben, mache ich das ueblicherweise ohne.
PythonCodingFun
User
Beiträge: 49
Registriert: Mittwoch 22. September 2021, 14:01

__deets__ hat geschrieben: Mittwoch 13. Oktober 2021, 16:42
Ja, ganz strenggenommen muss man ein threading.Event oder sowas benutzen, aber da wir nunmal in CPython das GIL haben, mache ich das ueblicherweise ohne.
Dumme Frage bitte nicht böse sein :-) : Was ist Cpython ? Ist das dass klassische "Pure" Python ?!

und warum sleep ?
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich bin nicht "boese". Eher verzweifelt. Ist es wirklich so schwer, sich einen Begriff, den man nicht kennt, einfach mal kurz zu ergoogeln? Das beantwortet dann innerhalb von nur den ersten zwei Suchergebnissen schon in der Vorschau von Google diese Frage mit "ja". Du hast mehr Zeit gebraucht, das als Frage zu formulieren.

Das sleep steht nur als "mach was im Programm und beende den Thread spaeter" da.
Benutzeravatar
__blackjack__
User
Beiträge: 13067
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Der Autor von dem Buch hat ein Jahr vor „Python lernen in abgeschlossenen Lerneinheiten“ ein Buch mit dem Titel „Java lernen in abgeschlossenen Lerneinheiten“ geschrieben. Und oh Wunder die Bücher sind gleich aufgebaut, enthalten viel gemeinsamen Text, und die gleichen Beispiele. Der Autor hat einfach mehr oder weniger Java durch Python ersetzt, ohne Python wirklich zu kennen/können, denn das sieht alles sehr nach Java und so gar nicht nach Python aus.

Aus den Beispielen die man sich auf der Springer-Seite anschauen kann, wieder mal der blanke Horror:

Code: Alles auswählen

class Person:
    
    __anzahl = 0
    
    def __init__(self, name, vorname, adresse):
        self.__name     = name
        self.__vorname  = vorname
        self.__adresse  = adresse
        Person.__anzahl = Person.__anzahl + 1
    
    def getAnschrift(self):
        return self.__vorname + " " + self.__name + ", " + \
               self.__adresse.getAdresse()
    
    def getPerson(self):
        return self

    @staticmethod
    def getAnzahl():
        return Person.__anzahl
`getPerson()` gibt es im Beispiel im Java-Buch auch, entsprechend mit ``return this;``. Was der Quatsch soll, wird da auch nicht erklärt. Also die Methode kann weg.

`__anzahl` ist ein globaler und nutzloser Wert, kann auch weg. Würde man auch in Java nicht machen.

``private`` gibt es in Python nicht, und ``protected`` auch nicht. Es gibt öffentliche und nicht-öffentliche Attribute über die Konvention *einen* führenden Unterstrich vor nicht-öffentliche Namen zu setzen. Weshalb das `_thread`-Modul nicht verwendet werden sollte, denn das ist nicht für den öffentlichen Gebrauch.

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

Und dieses Zeichenketten und Werte zusammengestückel mit ``+`` (und ggf. `str()`) macht man in Python auch nicht. Dafür gibt es f-Zeichenkettenliterale. Bleibt also das hier:

Code: Alles auswählen

class Person:
    def __init__(self, name, vorname, adresse):
        self._name = name
        self._vorname = vorname
        self._adresse = adresse

    def get_anschrift(self):
        return f"{self._vorname} {self._name}, {self._adresse.get_adresse()}"
So sieht `Adresse` im Buch aus:

Code: Alles auswählen

class Adresse:
    
    def __init__(self, str, nr, plz, ort):
        self.__strasse      = str
        self.__nummer       = nr
        self.__wohnort      = ort
        if (plz >= 10000) and (plz <= 99999):
            self.__postleitzahl = plz
        else:
            print("Falscher Postleitzahlwert")
    
    def getAdresse(self):
        return self.__strasse + " " + str(self.__nummer) + ", " + \
               str(self.__postleitzahl) + " " + self.__wohnort
    
    def getStrasse(self):
        return self.__strasse
    
    def getPlz(self):
        return self.__postleitzahl
    
    def setStrasse(self, str):
        self.__strasse = str
    
    def setPlz(self, plz):
        if (plz >= 10000) and (plz <= 99999):
            self.__postleitzahl = plz
Zu dem was zu `Person` zu sagen war, kommen hier noch die Abkürzungen bei den Namen hinzu, wobei `str` blöderweise schon für den `str`-Datentyp verwendet wird. So etwas sollte man nicht machen, weil das verwirrend ist, und man so in der `__init__()` dann auch `str` gar nicht mehr verwenden kann. Es erzeugt auch unnötig Aufwand beim Lesen wenn die Namen der Argument nicht den Namen der Attribute entsprechen, denen sie dann zugewiesen werden.

Die Strasse hat ein triviales Getter/Setter-Paar. Damit ist das de fakto öffentlich, also würde man in Python einfach direkt auf das Attribut zugreifen.

Bei der Postleitzahl ist es nicht ganz so trivial, weil dort eine Bereichsprüfung im Setter steckt. Das würde man in Python durch ein Property lösen. Und dann auch so, dass man den Test nur *einmal* im Code haben muss, und nicht zweimal. Codewiederholungen sollte man vermeiden (auch in Java und anderen Programmiersprachen), weil das unnötige Arbeit macht und fehleranfällig ist, weil man immer alle Kopien synchron halten muss.

Die Bereichsüberprüfung der Postleitzahl lässt sich in Python lesbarer über die Verkettung der Vergleichsoperatoren schreiben. Und statt eine Textausgabe zu machen oder geräuschlos den Wert ganz einfach nicht zu setzen, wenn er ausserhalb der Grenzen ist, würde man hier eine Ausnahme auslösen. Würde man auch in Java machen, das ist aber aus dem Java-Buch so übernommen.

In Python sähe die Adress-Klasse so aus:

Code: Alles auswählen

class Adresse:
    def __init__(self, strasse, nummer, postleitzahl, wohnort):
        self.strasse = strasse
        self._nummer = nummer
        self._postleitzahl = None
        self.postleitzahl = postleitzahl
        self._wohnort = wohnort

    @property
    def postleitzahl(self):
        return self._postleitzahl

    @postleitzahl.setter
    def postleitzahl(self, postleitzahl):
        if not 10_000 <= postleitzahl <= 99_999:
            raise ValueError(f"Postleitzahl {postleitzahl!r} ungültig")

        self._postleitzahl = postleitzahl

    def get_adresse(self):
        return (
            f"{self.strasse} {self._nummer},"
            f" {self.postleitzahl} {self._wohnort}"
        )
Und sie wäre wahrscheinlich in der gleichen Datei, also im gleichen Modul wie `Person`. Denn eine Datei pro Klasse hat der Autor wohl auch von Java übernommen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
narpfel
User
Beiträge: 644
Registriert: Freitag 20. Oktober 2017, 16:10

Bei einem `getPerson` in einer `Person`-Klasse würde ich ganz stark vermuten, dass der Autor auch Java nicht kann. :shock:

Und Postleitzahlen hat er anscheinend auch noch nie gesehen, zum Beispiel 01067 für Dresden. Oder hat die Post da etwa was falsch gemacht und mehr als vier Millionen Menschen ungültige Postleitzahlen zugeteilt‽
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Immerhin reichte das Niveau für eine Professur🥳
PythonCodingFun
User
Beiträge: 49
Registriert: Mittwoch 22. September 2021, 14:01

Dann sagen die Leute, Bücher von großen Verlagen sind immer besser, als das was so im Internet steht ;-)

Da ich, ein Angfänger in Python, dachte mir so hey ein Prof. der ein Buch in einem großen Verlag schreibt, toll das was drin steh kann ja nicht verkehrt sein^^

So also bevor ich auf den Deckel bekomm (wegen getter oder setter) dann bitte eine Email an den Autor ;-) :-D
Zuletzt geändert von PythonCodingFun am Mittwoch 13. Oktober 2021, 19:30, insgesamt 2-mal geändert.
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wer sagt denn sowas? Hier jedenfalls keiner.

Und jetzt hast su eine wertvolle Lektion gelernt: Professoren kochen auch nur mit Wasser, und manchmal ist das von ziemlich fragwürdiger Qualität.
narpfel
User
Beiträge: 644
Registriert: Freitag 20. Oktober 2017, 16:10

Hilfe. Der gute Mensch hat auch noch in Sachsen studiert. :shock:
Benutzeravatar
__blackjack__
User
Beiträge: 13067
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@narpfel: Beim Java-Buch sind mir die statischen ``anzahl``-Attribute negativ aufgefallen und bei einem `Kreis`-Beispiel ist eine hässliche Asymmetrie bei `getMittelpunkt()` das ein Array aus zwei Zahlen zurückgibt, und `setMittelpunkt()` das zwei einzelne Zahlen als Argumente nimmt. Das Beispiel ist dann genau so nach Python übertragen.

``enum`` in Java und das `enum`-Modul in Python sind in beiden Büchern kurz vor Ende und die Beispiele nutzen jeweils nicht, das diese Typen deutlich mächtiger sind, als beispielsweise ``enum`` in C. Und selbst *das*, dass man jeder Konstanten auch einen numerischen Wert geben kann, werden in den Beispielen bei beiden Büchern nicht genutzt. Hier der Horror in Python:

Code: Alles auswählen

import enum

class Note(enum.Enum):
    sehr_gut     = 1
    gut          = 2
    befriedigend = 3
    ausreichend  = 4
    mangelhaft   = 5


print("Anzahl der Elemente:", len(Note))
for note in Note:
    if note.name == "sehr_gut":
        wert = 1
    elif note.name == "gut":
        wert = 2
    elif note.name == "befriedigend":
        wert = 3
    elif note.name == "ausreichend":
        wert = 4
    else:
        wert = 5
    print(note, ": Name =", note.name, "=", wert)
Schlimmster Fehler ist das Vergleichen des Namens mit Zeichenketten. Genau damit man nicht irgendeine Zeichenkette vergleichen kann, sondern das ein eigener Datentyp ist, statt das man sich da einfache Konstanten definiert, ist ja gerade der Witz von `Enum`-Typen. Ebenfalls falsch, weil gefährlich ist das ``else`` am Ende. Wenn jemand auf die Idee käme da noch `Note.ungenuegend` hinzuzufügen mit dem Wert 6, würde dafür auch der Wert 5 ausgegeben werden. Wenn man den Vergleich so machen würde, wie das eigentlich gedacht ist, und sich an die Namenskonventionen haltend, würde das so aussehen:

Code: Alles auswählen

class Note(enum.Enum):
    SEHR_GUT = 1
    GUT = 2
    BEFRIEDIGEND = 3
    AUSREICHEND = 4
    MANGELHAFT = 5

print("Anzahl der Elemente:", len(Note))
for note in Note:
    if note is Note.SEHR_GUT:
        wert = 1
    elif note is Note.GUT:
        wert = 2
    elif note is Note.BEFRIEDIGEND:
        wert = 3
    elif note is Note.AUSREICHEND:
        wert = 4
    elif note is Note.MANGELHAFT:
        wert = 5
    else:
        assert False, f"unbekannte Note {note!r}"

    print(note, ": Name =", note.name, "=", wert)
Letztlich natürlich völliger Unsinn diesen ganzen Code zu schreiben, weil man den Wert der Konstante ja von der Konstante selbst abfragen kann:

Code: Alles auswählen

class Note(enum.Enum):
    SEHR_GUT = 1
    GUT = 2
    BEFRIEDIGEND = 3
    AUSREICHEND = 4
    MANGELHAFT = 5


print("Anzahl der Elemente:", len(Note))
for note in Note:
    print(note, ": Name =", note.name, "=", note.value)
Das Java-Beispiel sieht so aus:

Code: Alles auswählen

public class Test {
    enum Note { sehr_gut, gut, befriedigend, ausreichend, mangelhaft };

    public static void main(String[] args)
    {
        System.out.printf("Anzahl der Noten: %d\n", Note.values().length);
        for (Note note: Note.values())
        {
            int wert = 0;
            switch (note)
            {
                case sehr_gut:     wert = 1; break;
                case gut:          wert = 2; break;
                case befriedigend: wert = 3; break;
                case ausreichend:  wert = 4; break;
                case mangelhaft:   wert = 5; break;
            }
            System.out.printf("%s = %d\n", note, wert);
        }
    }
}
In Java hat jeder Enum-Wert eine Ordnungszahl, auf deren Grundlage man hier den Notenwert ganz einfach berechnen kann:

Code: Alles auswählen

public final class Test {
    private Test() {}

    enum Note { sehr_gut, gut, befriedigend, ausreichend, mangelhaft }

    public static void main(String[] args)
    {
        System.out.printf("Anzahl der Noten: %d\n", Note.values().length);
        for (var note: Note.values()) {
            System.out.printf("%s = %d\n", note, note.ordinal() + 1);
        }
    }
}
Falls man den Zusammenhang zwischen Ordnungszahl und Notenwert stärker an den Aufzählungstyp binden/darin kapseln möchte, kann man dem ``enum``-Typ eine Getter-Methode dafür verpassen:

Code: Alles auswählen

public final class Test {
    private Test() {}

    enum Note {
        sehr_gut, gut, befriedigend, ausreichend, mangelhaft;

        public int getWert() {
            return ordinal() + 1;
        }
    }

    public static void main(String[] args)
    {
        System.out.printf("Anzahl der Noten: %d\n", Note.values().length);
        for (var note: Note.values()) {
            System.out.printf("%s = %d\n", note, note.getWert());
        }
    }
}
Als letztes reguläres Kapitel im Python-Buch ist sind „Dictionaries“ ganz kurz angerissen, sonst werden nur Listen verwendet. Und teilweise als Arrays bezeichnet, und auch mal mit 0en gefüllt, um dann diese 0en durch einzelne Zuweisungen durch `Person`- oder `Adresse`-Objekte ersetzt. An der Stelle möchte man als Python-Programmierer gerne das (zu leichte) Buch nach dem Autor werfen… Im Java-Buch kommen gar keine Datenstrukturen jenseits von Arrays vor. Nicht mal Interfaces werden in dem Java-Buch erwähnt! Generics schon mal gar nicht. Irgendwie hören beide Bücher auf bevor alle wichtigen Grundlagen behandelt wurden.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
narpfel
User
Beiträge: 644
Registriert: Freitag 20. Oktober 2017, 16:10

Mein Favorit bis jetzt ist `RegAusdruck4.py`:

Code: Alles auswählen

import re

public class Zeichenkette 
{
	public static ArrayList<String[]> findMuster(String s, String r)
	{
		ArrayList<String[]> liste = new ArrayList<String[]>();
		Matcher m = Pattern.compile(r).matcher(s); 
		while(m.find())
		{
			String elem[] = {m.group(), String.valueOf(m.start()), String.valueOf(m.end())}; 
			liste.add(elem);
		}
		return liste;
	}	

	public static void main(String[] args) 
	{
		String s = "234 3323 22123 12312343 2312312312345";
		ArrayList<String[]> liste = findMuster(s, "(123)+");
		for(String elem[] : liste)
			System.out.printf("%s: (%s - %s)\n", elem[0], elem[1], elem[2]);
	}	
}
Immerhin werden da keine Strings mit `+` zusammengebaut. :shock:
Benutzeravatar
__blackjack__
User
Beiträge: 13067
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@narpfel: Wo ist das denn her? Ich habe hier die 2. Auflage von dem Buch, da steht das so:

Code: Alles auswählen

import re

# ------------ Eingabe ------------
# 1. Zeichenkette
text = "02123 12343 2312312312345"

# 2. Regulärer Ausdruck
reg = r"(123)+"

# ----------- Berechnung ----------
pattern = re.compile(reg)
liste   = [[m.start(), m.end(), m.group()] for m in pattern.finditer(text)]

# --------------- Ausgabe ---------
print(liste)
Was hier blöd ist, ist das `reg` und `pattern` genau falsch herum benannt sind. Naja, und das man die Abkürzung `regex` vielleicht nicht noch mal mit `reg` abkürzen sollte.

Und kleiner Schönheitsfehler: In der Liste würde ich das als Tupel speichern und nicht als Liste.

Aber hey, im Java-Buch gibt es ja doch Beispiele von Datenstrukturen, sogar mit Generics. Den Kritikpunkt muss ich dann also zurückziehen. Wobei sich das ganze anscheinend auf `ArrayList<T>` beschränkt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten