Aus einem Script mit einem Webserver kommunizieren ...

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

Nabend
Ich war die letzten Wochen beruflich und privat eingespannt und kam nicht dazu hier weiter zu arbeiten.
Jetzt möchte ich mal weiter machen.
Die Antwort hat mir schon einmal zum Teil weitergeholfen.
Mal schauen ob ich dich richtig verstanden habe:
value ist aber keine Konstante und muß deshalb in __init__ initialisiert werden.
Das heißt alle Klasseneigenschaften, nichts anderes soll "value" sein, müssen innerhalb von __init__ initialisiert werden?
Ich kannte das aus anderen Sprachen anders, aber wenn es so besser ist - gerne.
Auch Klasse-Konstanten sollte man in Großbuchstaben schreiben (TRIGGER_DIFFERENCE)
Das wusste ich noch nicht.
In dem Fall soll aber "trigger_difference" keine Konstante, sondern eine Klasseneigenschaft mit einem Vorgabewert sein.
Müsste also nach __init__ verschoben werden?
die Doppelunterstriche sind alle unschön, stören und sollten durch einfache ersetzt werden.
Ich wollte diese Variablen auf "private"-Status setzen und hatte hier -> http://www.hdm-stuttgart.de/~maucher/Py ... assen.html unter "6.1.3. Sichtbarkeit" gelesen das man dann einen "__" nutzen soll.
Aber in meinem Fall ist dann "protected" -> "_" richtig?
floats sollte man erst bei und durch die Ausgabe runden
Werde ich ändern und dann durch die javascript die Ausgabe runden.
was sollen die tmp_-Päfixe an allen Argumenten von __init__?
Ich dachte so wäre der Code leichter lesbar, da diese Variablen ja nur temporär genutzt werden.
So kann man sich täuschen :wink:
Wenn Du keine Liste willst, dann übergibt BrickletTemperature einfach nur eine Callback-Funktion, anstatt der Liste
Hier hänge ich noch ein fest, hoffe das ich die Tage einen Durchbruch habe.
BlackJack

Die verlinkte Klassenbeschreibung ist ja gruselig. Da versucht jemand die Ideen von einer anderen Programmiersprache 1:1 auf Python zu übertragen.

Klassenattribute werden dort als „statische Attribute” bezeichnet — an denen ist aber nichts statisch, die sind genau so dynamisch wie alle anderen Attribute auch. Sie sind halt nur auf dem Klassenobjekt statt auf Exemplaren die aus der Klasse erzeugt werden.

Am Anfang wird auch zwischen „Klassen” und „Objekten” unterschieden. Nur sind Klassen in Python auch Objekte, „Klasse” ist also zu „Objekt” gar keine scharfe Abgrenzung.

Attribute können nicht privat im Sinne von von aussen nicht zugänglich sein, wie da fälschlicherweise behauptet wird. Und *deklarieren* kann man das schon gar nicht. Python hat nur sehr wenige Deklarationen. Wenn ich jetzt nichts vergessen habe nur ``global`` und bestimmte ``__future__``-Importe, und in Python 3 ``nonlocal``. Alles andere findet nach dem Compilieren zur Laufzeit statt.

Der Satz „Methoden sind Funktionen, die innerhalb einer Klasse definiert werden. Sie sind innerhalb der Klasse sichtbar und benutzbar. Von aussen kann auf Methoden nur über eine Referenz auf ein Objekt dieser Klasse zugegriffen werden […]” macht auch keinen Sinn denn was bedeutet denn hier „innerhalb der Klasse sichtbar und benutzbar”? Auch *in* den Methoden kann man auf andere Attribute oder Methoden nur über eine Referenz zugreifen. Das Objekt ist da ja an den Namen `self` gebunden. Ohne geht es nicht. Das steht dann ja auch gleich danach im selben Absatz.

„Häufig werden Klassenattribute verwendet um die aktuell instanzierten Objekte einer Klasse zu zählen.” — *WTF*, das wird *nie* gemacht. Echt nicht. Nur in Tutorials die nichts taugen. In realem Code nicht.

Es gibt nur eine Sichtbarkeit und die ist ”public”, wobei das natürlich wenig Sinn macht eine Bezeichnung zu haben für etwas was gegen nichts anderes abgegrenzt werden muss. Das zwanghafte fantasieren von ``public``, ``private``, und ``proteced`` als Unterscheidung ist irgendwie so eine Macke von Java-Programmierern glaube ich. Doppelte führende Unterstriche sind nicht ``privat`` — man kann auf solche Attribute von aussen problemlos zugreifen, die heissen real bloss anders weil der Klassenname da noch eingebastelt wird („name mangling”). Das wird nicht gemacht damit man nicht darauf zugreifen kann, sondern um Namenskollisionen bei Mehrfachvererbung zu verhindern. Da aber keiner ernsthaft Mehrfachvererbung verwendet braucht man das so gut wie *nie*. Ein führender Unterstrich ist eine Konvention um Attribute zu kennzeichnen die nicht zur öffentlichen API eines Objekts gehören. Das würde ich aber nicht als ``protected`` bezeichnen.

Das was der Text als Konstruktor bezeichnet ist keiner. Die `__init__()`-Methode *initialisiert* ein bereits bestehendes Objekt. Im Gegensatz zu einigen anderen Sprachen (z.B. Java) die das als Konstruktor bezeichnen ist die Unterscheidung in Python wichtig weil Python tatsächlich auch eine Methode kennt in der man in den Konstruktionsprozess eingreifen kann, also einen echten Konstruktor und das ist die `__new__()`-Methode.

Beim Text zum „Destruktor” wird es abenteuerlich. Was da steht ist schlicht *falsch*. Weder kann man mit ``del(objref)`` ein Objekt explizit löschen noch löst das zwangsläufig den Aufruf der `__del__()`-Methode aus. Das ist gefährlicher Unsinn. Das vorhandensein von `__del__()` kann sogar dazu führen das ein Objekt unter bestimmten Umständen niemals gelöscht wird und damit auch der Code in der Methode nicht ausgeführt wird. Man sollte sich mit der Speicherverwaltung von Python schon genau auskennen bevor man daran denkt diese Methode zu definieren. Das ist ein ziemliches Minenfeld. Anfängern würde ich diese Methode gar nicht erst zeigen, oder nur mit der Warnung: Nicht implementieren, sehr gefährlich.

Beim „Erzeugen von Objekten” werden die runden Klammern im Text als „geschweifte Klammern” bezeichnet und: „Enthält der Konstruktor als Parameter nur self, dann können die geschweiften Klammern nach dem Klassennamen ganz weggelassen werden.” — Äh, was bitte? Das ist nicht Python was da beschrieben wird.

Das Beispiel mit der Kontoklasse ist dann auch so ein typisch kaputter Code von jemandem der offensichtlich Java oder so kann und nach kurzer Zeit meint er hätte Python verstanden. Mit ”privaten” Attributen und einem Examplarzähler der in der `__init__()`- hoch und der `__del__()`-Methode runtergezählt wird. Mal davon abgesehen das so ein globaler Zähler ein schlechter Entwurf ist, funktioniert das so nicht zuverlässig. Da wird von Garantien ausgegangen die von der Sprache nicht gegeben werden.

In der `einzahlen()`-Methode werden Typen geprüft — wer das so macht soll gefälligst keine dynamisch typisierte Programmiersprache verwenden. Wobei die Art — `type()`-Ergebnisse mit ``==`` und Typen zu vergleichen — auch noch mal schlecht ist. Wenn man so etwas schon machen muss nimmt man dafür `isinstance()`.
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

wow ... da habe ich wohl genau die richtige Seite gefunden :o
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

so, habe die Klasse jetzt soweit umgebaut

Code: Alles auswählen

class BrickletTemperature:

    def __init__(self, uid, connection, logging, queue, value=0.0, trigger_difference=0.1):
        self._bricklet = Temperature(uid, connection)                                               # private
        self.value = value                                                                          # public
        self.trigger_difference = trigger_difference                                                # public
        self._uid = uid                                                                             # private
        self._logging = logging                                                                     # private
        self._queue = queue                                                                         # private
        self._logging.debug('Tinkerforge ... Temperatur-Bricklet "%s" initialisiert' % uid)

    def set_callback(self, timeframe=5000):
        self._bricklet.set_temperature_callback_period(timeframe)
        self._bricklet.register_callback(self._bricklet.CALLBACK_TEMPERATURE, self.__changed)
        self._logging.debug('Tinkerforge ... Temperatur-Bricklet "%s" Callback gesetzt' % self._uid)

    def read(self):
        return self._bricklet.get_temperature() / 100.0

    def __changed(self, tmp_value):
        # get new temperature
        tmp_value = (tmp_value / 100.0)

        # only if difference is higher or equial to 0.1
        if abs(self.value - tmp_value) >= self.trigger_difference:

            # old temperature set to new temperature
            self.value = tmp_value
            self._logging.debug('Tinkerforge ... Temperaturänderung bei "%s" -> %f' % (self._uid, self.value))

            # create json-string ( sensor-id <> sensor-type <> sensor-name <> sensor-value )
            tmp_json = json.dumps(
                ["send_data", self._uid, "sensor", "temperature", self.value])

            # send new temperature to all clients
            for consumer in self._queue:
                consumer(tmp_json)
                self._logging.debug('websockets .... Temperaturänderung bei "%s" -> Warteschlange ' % self._uid)
Ist das jetzt soweit in Ordnung oder sind noch grobe Schnitzer drin?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

So ein paar Dinge fallen da auf den ersten Blick noch auf. Zeilen sollten in Python nicht länger als 80 Zeichen sein, dann solltest du die Zeile umbrechen oder dich fragen, ob wirklich so viel in eine Zeile gehört. Dann wird das ganze etwas leserlicher. Auch solltest du keine Kommentare hinter Code schreiben, so wie in den Zeilen 4 bis 9. Das ist sehr unübersichtlich. Schreibe Kommentare am besten vor die betreffende Zeile.

Die Kommentare aus Zeilen 4 bis 9 solltest du am besten ganz weglassen, die haben keinen Zusätzlichen nutzen. Durch den führenden Unterstrich, oder eben das Fehlen desselben, ist vollkommen offensichtlich, ob diese privat sind oder nicht. Das gilt auch für deine anderen Kommentare. Die beschreiben viel zu offensichtliche Dinge. Kommentare sollten einen Mehrwert bieten und nicht den Code noch einmal in Worten beschreiben. Kommentare sind dazu gedacht, dass der Ablauf und die Zusammenhänge beschrieben werden und nicht das Programm noch einmal 1:1 in Umgangssprache zu beschreiben.

Für "logging" solltest du dir noch einen besseren Namen überlegen, den Namen gibt es schon als Modul in der Standardbibliothek. Die selben Namen sollten besser nicht verwendet werden, das führt nur zur Verwirrung. "logger" wäre zum Beispiel eine Alternative.

"__changed" solltest du noch anpassen, die doppelten führenden Unterstriche gehören hier nicht. Die sind dazu gedacht, um Namenskonflikte bei der Vererbung zu vermeiden. Es findet aber gar keine Vererbung statt.

Die 100.0 aus read und aus Zeile 22 solltest du noch zu eine Konstante zusammenfassen. Zumindest nehme ich mal an, dass diese zusammengehören. Vielleicht machst du gleich eine Funktion draus, die die Temperaturen umwandelt. Aus der read-Funktion könntest du auch ein Property machen, dann hat man von außen einen schöneren Zugriff.
Das Leben ist wie ein Tennisball.
BlackJack

Ich persönlich würde den Logger nicht übergeben sondern einen im Modul für das Modul erstellen und den benutzen. Vielleicht auch einen für die Klasse wenn man das später mal so fein konfigurieren möchte.
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

Vielleicht machst du gleich eine Funktion draus, die die Temperaturen umwandelt. Aus der read-Funktion könntest du auch ein Property machen, dann hat man von außen einen schöneren Zugriff.
Ich persönlich würde den Logger nicht übergeben sondern einen im Modul für das Modul erstellen und den benutzen.
Ihr denkt aber schon daran das ich blutiger Anfänger bin :wink:
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

der_Mausbiber hat geschrieben:Ihr denkt aber schon daran das ich blutiger Anfänger bin :wink:
Natürlich. Deshalb versuchen wir auch alles um das zu verändern ;-)
Das Leben ist wie ein Tennisball.
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

so, habe die Klasse jetzt weiter überarbeitet und jetzt bin ich schon einmal soweit :

Code: Alles auswählen

class BrickletTemperature:
    _QUOTIENT = 100.0

    def __init__(self, uid, connection, logging_daemon, queue, value=0.0, trigger_difference=0.1):
        self._bricklet = Temperature(uid, connection)
        self._value = value
        self.trigger_difference = trigger_difference
        self._uid = uid
        self._logging_daemon = logging_daemon
        self._queue = queue
        self._logging_daemon.debug('Tinkerforge ... Temperatur-Bricklet "%s" initialisiert' % uid)

    def set_callback(self, timeframe=5000):
        self._bricklet.set_temperature_callback_period(timeframe)
        self._bricklet.register_callback(self._bricklet.CALLBACK_TEMPERATURE, self._changed)
        self._logging_daemon.debug('Tinkerforge ... Temperatur-Bricklet "%s" Callback gesetzt' % self._uid)

    def read(self):
        self._value = self._bricklet.get_temperature() / self._QUOTIENT
        return self._value

    def _changed(self, tmp_value):
        tmp_value = (tmp_value / self._QUOTIENT)

        if abs(self._value - tmp_value) >= self.trigger_difference:
            self._value = tmp_value
            self._logging_daemon.debug('Tinkerforge ... Temperaturänderung bei "%s" -> %f' % (self._uid, self._value))

            tmp_json = json.dumps(
                ["send_data", self._uid, "sensor", "temperature", self._value])

            for consumer in self._queue:
                consumer(tmp_json)
                self._logging_daemon.debug('websockets .... Temperaturänderung bei "%s" -> Warteschlange ' % self._uid)

    temperature = property(read)
Zeilen sollten in Python nicht länger als 80 Zeichen sein, dann solltest du die Zeile umbrechen oder dich fragen, ob wirklich so viel in eine Zeile gehört.
Arbeite mit pycharm und keine Zeile ist länger als 80 Zeichen, 2 Zeilen sind nah dran - aber nur nah dran.
Ansonsten unterbreche ich.
Auch solltest du keine Kommentare hinter Code schreiben, so wie in den Zeilen 4 bis 9. Das ist sehr unübersichtlich. Schreibe Kommentare am besten vor die betreffende Zeile.
Kommentare sind weg
Für "logging" solltest du dir noch einen besseren Namen überlegen, den Namen gibt es schon als Modul in der Standardbibliothek. Die selben Namen sollten besser nicht verwendet werden, das führt nur zur Verwirrung. "logger" wäre zum Beispiel eine Alternative.
geändert
"__changed" solltest du noch anpassen, die doppelten führenden Unterstriche gehören hier nicht. Die sind dazu gedacht, um Namenskonflikte bei der Vererbung zu vermeiden. Es findet aber gar keine Vererbung statt.
Hatte ich wieder gemacht um den "private" Status deutlich zu machen - habe ich geändert.
Die 100.0 aus read und aus Zeile 22 solltest du noch zu eine Konstante zusammenfassen.
Erledigt.
Aus der read-Funktion könntest du auch ein Property machen, dann hat man von außen einen schöneren Zugriff.
Ich hoffe ich habe das so richtig gemacht.


So, und jetzt das was ich nicht hinbekomme, vielleicht habt ihr da noch ein Fetzen Code für mich?
Ich persönlich würde den Logger nicht übergeben sondern einen im Modul für das Modul erstellen und den benutzen.
Ich verstehe nicht wie das gemeint ist.
Wenn Du keine Liste willst, dann übergibt BrickletTemperature einfach nur eine Callback-Funktion, anstatt der Liste
Das bekomme ich nicht auf die Reihe, vorallem weil damit wohl auch noch Änderungen am Server-Skript einhergehen.
Ist aber nicht so schlimm, geht auch erst einmal so.


Ansonsten vielen Dank.
Ohne euch wäre ich nie bis hierher gekommen.

Danke :)
Antworten