Klassen-Modul (Template) für Sensoren - Stilfrage(n)

  • Moinsens!

    Ich bastel mir gerade ein Klassen-Modul zum Auslesen eines Sensors. Für den Anfang hab ich mir mal den BH1750 (Helligkeit) genommen. Aber dieses Klassen-Modul soll quasi ein Template werden, um dann für andere Sensoren (BME280, DHT22, DS18B20, ... ) kopiert / angepaßt zu werden.

    Funktionieren tut's soweit. Aber da ich in Python noch nicht so firm bin, frage ich mal in die Runde, ob der u.a. Source-Aufbau so richtig und sinnvoll ist.

    Das Haupt-Programm, in dem der Sensor geladen und ausgewertet wird:
    classtest.py

    Und dann das Klassen-Modul zum Auslesen des Sensors:
    bh1750_class.py

    Ich versuche eigentlich grundsätzlich, globale Variablen nicht direkt anzusprechen, sondern immer über get/set-Routinen zu gehen. Sehe ich das richtig, daß Python aber kein echtes "private" kann, sondern das nur mit den '__' etwas verschleiert? Ich würd's eigentlich nicht so witzig finden, wenn jemand im Hauptprogramm direkt z.B. die __LoopThread Variable verändern kann. Ok, wäre sein Problem, aber ein echtes "private" wäre schon schön...

    Außerdem würde ich gerne einige Getter "public" und die zugehörigen Setter "private" haben. Das Objekt selbst soll ja durchaus in der Lage sein, die Werte über den Setter zu aktualisieren, aber das Hauptprogramm soll die Werte eigentlich nur noch über die Getter auslesen können.

    Ist Threading hier ok? Ich könnte das ja auch umstellen und mit self.after(... arbeiten. Das würde das Starten/Stoppen des Sensors auch vielleicht etwas einfacherer / sicherer machen, als irgendwo einen wilden Thread im Zaum zu halten.

    Gruß, Michael

    Einmal editiert, zuletzt von miriki (19. Dezember 2016 um 06:18)

  • Klassen-Modul (Template) für Sensoren - Stilfrage(n)? Schau mal ob du hier fündig wirst!

  • Hallo,

    machen wir's mal kurz und schmerzlos: `bh1750_class.py` ist komplett nicht-idomatisches Python und stilistisch richtig schlecht und falsch. Kommst du aus der Java-Ecke? Stilistisch passt das eher dahin...

    "Private" gibt's nicht in Python, von daher solltest du erst gar nicht versuchen, irgendwas zu verbergen. Per Konvention werden lediglich Attribute und Methoden mit einem Underscore am Anfang gekennzeichnet, welche nur innerhalb der Klasse genutzt werden (sollen), aber nicht von außerhalb. Grundssätzlich kann man aber auf _alles_ von außen zugreifen, wenn man will.

    Getter und Setter wie du sie nutzt, sind in Python komplett überflüssig. Python unterstützt und nutzt den direkten Zugriff auf Attribute.
    Die __init__ Methode kommt normalerweise an den Anfang der Klasse und die Default-Wert werden nicht über setter zugewiesen, siehe oben.
    Du definierst außerdem eine Reihe von Variablen auf Klassenebene, die dann für _alle_ Instanzen der Klasse global gelten. Willst du aber vermutlich nicht?

    Auch wenn's hart klingt: löschen, passenden Abschnitt im Python-Tutorial lesen, neu schreiben. Das ist am einfachsten und wahrscheinlich auch am effektivsten.

    Gruß, noisefloor

  • In Python sind getter und setter verpoent. Und das mit gutem Grund, es gibt bessere Methoden dazu. Java hat die getter/setter-Konvention auch nicht aus einem guten OO-Prinzip oder aehnlichem, sondern weil ihnen ein Feature fehlt, das Python und C# haben - Attribut-Zugriffe, die zu Properties gewandelt werden. Also so etwas:

    Python
    class Foo(object):
    
    
          def __init__(self, v):
                 self._v = v
    
          @property
          def v_squared(self):
                 return self._v**2

    Hier siehst du uebrigens auch, wie man in Python einen Zugriff von aussen als public, aber ohne "setter" umsetzt.

    In deinem Fall koennte eine Property zB die eigentliche Berechnung anstossen. In Java gibt es einen solchen Mechanismus nicht. Um also zu verhindern, dass man gegen ein Interface arbeitet, welches dann ein Attribut statt einer Berechnung erwartet, macht man *alles* *immer* in getter/setter, so dass man client-Code nicht anfassen muss. Es ist also eine Schwaeche. Und wenn man von seiner IDE 70% des Codes generiert bekommt, ist das ein Zeichen, dass da etwas fehlt in der Sprache. Und last but not least: auch auf private Member kann ich in Java von aussen zugreifen - es ist ein bisschen komplizierter, aber nicht unmoeglich.

    Auch Mikrooptimierungen wie das setzen nur, wenn der Wert ungleich ist, sind fuer diesen Fall ueberkandidelt und im Zweifel sogar langsamer. Falls Geschwindigkeit hier Trumpf gewesen sein sollte.

    Statt permanent prints ein und auszukommentieren, bietet sich die Verwendung des logging-Moduls an.

    Code
    import logging
    
    
    logger = logging.getLogger(__name__)
    
    
    ...
    logger.debug(...)

    In deinem Start-Skript kannst du das logging jetzt konfigurieren, so dass du zB nur bestimmte Level zu sehen bekommst, oder auch bestimmte Logger an/ausschaltest.

    Das entscheidende Problem aber ist deine Festlegung, einen Thread zu benutzen. Das sprichst du ja schon selbst an. Es ist eine schlechte Idee, fuer jeden Sensor einen Thread zu starten. Ausserdem verletzt es ein wichtiges Entwurfsprinzip - die Separation of concern. Deine Klasse sollte die Dinge machen, die spezifisch fuer den konkreten Sensor sind. Nach aussen sollte sie eine Schnittstelle anbieten, die moeglichst gleich ist fuer alle Sensoren. Das hat natuerlich Grenzen, aber zumindest fuer etwas simples wie Temperatur oder Feuchtigkeit - das sieht von aussen ja gleich aus. Einfach eine Property zB "value", die einen float-Wert wiedergibt.

    Die Frage, wie der Sensor getrieben wird, steht auf einem anderen Blatt. Du kannst da auch gerne eine Hilfsfuktionalitaet anbieten, wie zB ThreadedSensorDriver, dem man dann gleich mehrerer Sensoren uebergibt, und der sie managt. Oder einen TimedSensorDriver, der dasselbe durch einen timer-call, wie von after geliefert, tut.

  • Seid mal nicht gleich so böse, miriki versucht es wenigstens. Aber wie ihr schon geschrieben habt sind getter und setter in Python nicht vorgesehen. Hier mal einen Link: http://www.python-kurs.eu/python3_properties.php
    Die Englische Version ist etwas ausführlicher: http://www.python-course.eu/python3_properties.php

  • Hallo,

    raspiprojekt: Sorry, aber der Link ist schlecht (bzw. Müll) - das ist doch genau so falsches bzw. nicht-ideomatisches Python. Stilistisch kaum ein Unterschied zum Beispiel des TE.

    Wie oben bereits erwähnt, ist es im offiziellen Python Tutorial besser und richtiger erklärt: https://docs.python.org/3/tutorial/classes.html.

    BTW: python-kurs.eu steht _nicht_ im Ruf, Python gut (im Sinne von richtig und ideomatisch) zu vermitteln. Gutes Beispiel: siehe oben.

    Gruß, noisefloor

  • Kommst du aus der Java-Ecke? Stilistisch passt das eher dahin...


    Eher VB.Net, wenn auch mit Pascal (später Delphi) aufgewachsen. Aber ja, auch Java (Android) und ein wenig C#. (Der Rest wie Logo, Lisp, Natural, Cobol, Forth, Algol, Comal usw. spielt hier wohl weniger eine Rolle...)


    Zitat

    "Private" gibt's nicht in Python


    Ok, so hatte ich es verstanden, ist also bestätigt. Muß ich nicht gut finden, aber mich damit abfinden.

    Zitat

    Getter und Setter wie du sie nutzt, sind in Python komplett überflüssig.


    Sie sind, so wie sie jetzt da stehen, auch noch nicht endgültiges Stadium.

    Setter: Neben dem Test, ob der Wert sich ändert, sollen ggf. noch weitere Aktionen durchgeführt werden. Das kann vom einfachen Setzen eines "dirty" Flags bis hin zur kompletten Neu-Initialisierung so ziemlich alles sein.

    Getter: Ich will tlw. Werte erst dann berechnen / auslesen, wenn sie abgerufen werden. Andererseits setze ich tlw. gelesene Werte im Getter danach auf "None", um dem Hauptprogramm z.B. zu signalisieren, daß (noch) kein aktualisierter Wert vorliegt.

    Zitat

    Die __init__ Methode kommt normalerweise an den Anfang der Klasse und die Default-Wert werden nicht über setter zugewiesen, siehe oben.


    Ok, leicht umzusetzende Kosmetik.

    Zitat

    Du definierst außerdem eine Reihe von Variablen auf Klassenebene, die dann für _alle_ Instanzen der Klasse global gelten. Willst du aber vermutlich nicht?


    Richtig. Das hatte ich auch schon so gelesen, war mir aber nicht sicher, ob ich das so richtig verstanden hatte. In dem Beispiel wurde ein Mitarbeiter-Zähler global mitgeschleift. Ja, _so_ will ich die globalen Variablen eigentlich wirklich nicht haben. Sie sollen lokal (und privat, ja...) in der jeweiligen Instanz sein.


    Gruß, Michael

  • noisefloor: Der Kurs ist zumindest für Anfänger gut geeignet und aus meiner Sicht nicht wirklich schlecht. Er ist nicht ganz so kompliziert geschrieben wie die originale Doku vom Python. Datenkapselung als ein Grundprinzip der OOP sollte man auch bei Python beachten. Zudem werden im Kurs auch keine Getter und Setter benutzt. Nur weil die Variablen und Methoden zum besseren Verständnis so benannt sind, müssen es noch lange keine sein. Aber ich bin da kompromissbereit, letztendlich gibt es beinahe soviele Programmierstile wie es Programmierer gibt . Es lohnt also kaum, sich an verschiedenen Stilen zu stören.

  • In Python sind getter und setter verpoent.


    Na, das kann man so pauschal auch nicht sagen. Eine Google-Suche nach "python @property" ergibt endlos viele Treffer auf Diskussionen, ob nun das eine oder andere "richtig" oder "besser" oder "stilvoller" oder was-auch-immer sei.

    Recht schön ist eine bei Stack Overflow.

    Was ich unterstütze: Keine Getter/Setter, die einfach nur die Werte durchreichen. Ohne weitere Prüfung oder Adaption der Werte kann man auch den direkten Zugriff auf die Variable zulassen. Besser _lesbar_ finde ich die Getter/Setter-Schnittstelle aber grundsätzlich immer. Schade nur, daß es eben in Python durch das fehlende "private" jederzeit ausgehebelt werden kann. Aber wie gesagt: Wenn jemand meint, von "außen" z.B. die __LoopThread Variable verändern zu müssen, dann ist das sein ureigenstes Problem.


    Zitat

    macht man *alles* *immer* in getter/setter, [...] Es ist also eine Schwaeche.


    Das kann man unterschiedlich sehen. Ich mag Java wirklich nicht besonders, aber ich war schon immer ein Freund von (im Kopf des Source, Stichwort "option explicit" in BASIC) deklarierten Variablen und finde die zwangsweise Schnittstelle über Getter/Setter zum Objekt eine sinnvolle Fortführung.

    Zitat

    Auch Mikrooptimierungen wie das setzen nur, wenn der Wert ungleich ist, sind fuer diesen Fall ueberkandidelt und im Zweifel sogar langsamer. Falls Geschwindigkeit hier Trumpf gewesen sein sollte.


    Nein, das war nur Vorbereitung für eigentlich weiterführende Aktionen. Wie gesagt: Nur Durchreichen ist eigentlich nicht meine Intention. Das "IF" sollte später also auch schon seinen "ELSE" Zweig bekommen.


    Zitat

    Statt permanent prints ein und auszukommentieren, bietet sich die Verwendung des logging-Moduls an.


    Ja, gesehen hatte ich das schon, aber mich noch nicht weiter mit beschäftigt. Bislang reicht mir ein simples "print", aber stimmt schon, für später ist das sicher sinnvoll.


    Zitat

    Es ist eine schlechte Idee, fuer jeden Sensor einen Thread zu starten.


    Das ist eben das, wo ich mir noch nicht so ganz sicher bin, warum...

    Letztendlich, was macht es für einen Unterschied, ob ich eine "while true" Schleife in einen Hintergrund-Thread verbanne oder eine "self.after" Schleife (etwas mehr) im Vordergrund laufen lasse? Ich finde es eigentlich sympathischer, wenn die Methoden im Objekt selbst "abgeschlossen" werden, also quasi das "exit sub" erreichen und nicht in einer Schleife hängen bleiben.

    Zitat

    Einfach eine Property zB "value", die einen float-Wert wiedergibt.


    Die "typische" Eigenschaft eines Objekts ist ja immer wieder gerne "Value". Ich bin mir noch nicht ganz sicher, ob ich das benutzen werde oder nicht. Ich tendiere eher zu "getLumen" in diesem Fall, weil es z.B. beim BME280 ganze 3 Werte gibt, die der Sensor liefert. Das könnte ich in Python natürlich auch per Tuple oder sonst irgendwie lösen. Aber "getTemperature", "getHumidity" und "getPressure" sind mir da irgendwie sympathischer.

    Gruß, Michael

  • Hallo,

    Zitat

    Setter: Neben dem Test, ob der Wert sich ändert, sollen ggf. noch weitere Aktionen durchgeführt werden.


    Dann ist aber kein Setter im eigentlichen Sinne, sondern eine Methode.

    Zitat

    Getter: Ich will tlw. Werte erst dann berechnen / auslesen, wenn sie abgerufen werden.


    Das ist dann aber keine Getter, sondern entweder eine Methode oder eine Property - je nach dem, was / sinnvoll gewünscht ist.

    Zitat

    Sie sollen lokal (und privat, ja...) in der jeweiligen Instanz sein.


    Dann gehört alles in die __init__ Methode. Und, wie gesagt, echt privat gibt's nicht. Beispiel:

    [code=php]>>> class Foo():
    def __init__(self, visible):
    self.visible = visible
    self.__not_directly_visible = 'Secret?'


    >>> my_foo = Foo('Hello World')
    >>> dir(my_foo)
    ['_Foo__not_directly_visible', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'visible']
    >>> my_foo.__not_directly_visible
    Traceback (most recent call last):
    File "<pyshell#15>", line 1, in <module>
    my_foo.__not_directly_visible
    AttributeError: 'Foo' object has no attribute '__not_directly_visible'
    >>> my_foo.__dict__
    {'_Foo__not_directly_visible': 'Secret?', 'visible': 'Hello World'}
    >>> my_foo._Foo__not_directly_visible
    'Secret?'
    >>> my_foo._Foo__not_directly_visible = 'No Secret!'
    >>> my_foo._Foo__not_directly_visible
    'No Secret!'
    >>>[/php]

    Gruß, noisefloor

  • Dann ist aber kein Setter im eigentlichen Sinne, sondern eine Methode. [...]
    Das ist dann aber keine Getter, sondern entweder eine Methode oder eine Property - je nach dem, was / sinnvoll gewünscht ist.


    Ich würd mir eine klare Definition wie im VB.Net wünschen. Da hab ich durch "public property ..." geklärt, daß es eine Property ist, danach folgen get und/oder set Methoden. Ich find auch die klare Unterscheidung in "sub" und "function", also eine Methode, die einen Funktionswert zurückliefert, lesbarer. Aber man kann eben nicht alles haben... ;)


    Zitat

    Dann gehört alles in die __init__ Methode.


    Versteh ich das richtig: Ich brauch keine Deklaration im globalen Header, um im __init__ den Wert einer Variablen zu setzen und sie danach für alle Methoden in der Klasse verfügbar zu machen? Also verkürzt:


    Und das
    a) gibt keine Fehlermeldung bei Aufruf von z.B. "machwas( 3 )"?
    b) erzeugt eine _lokale_ Variable im Objekt
    c) ist auch eine (nur) halbwegs private Variable


    Zitat

    Und, wie gesagt, echt privat gibt's nicht. Beispiel:


    Yep, so hatte ich tutorialspoint, letzter Abschnitt "data hiding", verstanden.

    Gruß, Michael

  • Hallo,

    Zitat

    Versteh ich das richtig: Ich brauch keine Deklaration im globalen Header, um im __init__ den Wert einer Variablen zu setzen und sie danach für alle Methoden in der Klasse verfügbar zu machen?


    Grundsätzlich richtig - dein Beispiel funktioniert aber so nicht, siehe unten.

    Zitat

    a) gibt keine Fehlermeldung bei Aufruf von z.B. "machwas( 3 )"?


    `machwas()` ist eine Methode von `xxx`. Wenn du eine Instanz von xxx erzeugst, funktioniert das auch.

    Zitat

    b) erzeugt eine _lokale_ Variable im Objekt


    Also in dem `machwas()` deines Beispiels berechnest du zwar `zzz` - damit passiert aber nichts weiter und du kommst an `zzz` auch nicht drin. Da fehlt dann mindestens ein `return zzz` Statement.

    Dein Beispiel sähe funktionierend so aus:

    [code=php]>>> class XXX():
    ... def __init__(self, yyy):
    ... self.__yyy = yyy
    ... def mach_was(self, n):
    ... return self.__yyy*n
    ...
    >>> xxx = XXX(10)
    >>> xxx.mach_was(2)
    20[/php]

    Wie gesagt, das mit den 2 Underscores ist nicht wirklich sinnvoll. Per Konvention ist ein führender Underscore der Hinweis, dass das Attribut nur intern benutzt wird. Python forciert das aber nicht, das ist "nur" eine gängige Konvention.

    Gruß, noisefloor

    Einmal editiert, zuletzt von noisefloor (19. Dezember 2016 um 16:53)


  • `machwas()` ist eine Methode von `xxx`. Wenn du eine Instanz von xxx erzeugst, funktioniert das auch.


    Das war mir klar. Mir ging's mehr um die Verwendung von __yyy. Ich renn sonst immer gerne mal in die Fehlermeldung (wegen eines Vertippens), daß ich eine Variable vor Initialisierung benutzen würde. Und ich hätte jetzt auch erstmal gedacht, daß __yyy nur eine lokale Variable im __init__ bleibt und danach wieder weg ist, somit für machwas nicht (mehr) zur Verfügung steht.

    Anders formuliert: Eine Variable, die ich im __init__ mit einem Wert belege (und damit "erzeuge"), bleibt danach Klassen-weit verfügbar, auch wenn ich sie nicht vorher bereits im globalen Klassen-Teil deklariert habe?

    Mir ist auch klar, daß zzz mit z.B. return auch irgendwie weiter verarbeitet werden muß. Mir ging's nur um die Verwendung von __yyy.


    Zitat

    Wie gesagt, das mit den 2 Underscores ist nicht wirklich sinnvoll.


    Das sieht tutorialspoint aber etwas anders... ;)

    Zitat

    Python forciert das aber nicht, das ist "nur" eine gängige Konvention.


    Die _2_ Underscores werden da aber durchaus anders behandelt. Das geht über eine Konvention hinaus.

    Zitat

    An object's attributes may or may not be visible outside the class definition. You need to name attributes with a double underscore prefix, and those attributes then are not be directly visible to outsiders.
    [...]
    Python protects those members by internally changing the name to include the class name. You can access such attributes as object._className__attrName.


    Also auch hier kein echtes "private", aber schon ein bißchen verstecken und verschleiern.

    Gruß, Michael

  • Hallo,

    [quote]Anders formuliert: Eine Variable, die ich im __init__ mit einem Wert belege (und damit "erzeuge"), bleibt danach Klassen-weit verfügbar, auch wenn ich sie nicht vorher bereits im globalen Klassen-Teil deklariert habe?
    Nein, ist sind die Attribute für die Instanz einer Klasse - nicht Klassen-weit.

    Zum Thema Underscore: https://www.python.org/dev/peps/pep-0008/. Da wird die Konvention über single underscore und das Verhalten double underscores erklärt. die PEP 8 kann man so wie so 1x lesen, dass ist quasi der heiligen Gral für stilistisch wertvolles und ideomatisches Programmieren mit Python.

    Diese Webseite http://igorsobreira.com/2010/09/16/dif…-in-python.html zeigt IMHO auch noch mal schön, wann man __ nutzen kann und warum.

    Gruß, noisefloor

  • Das getter und setter ein Ding in Python waeren ist mir neu - und ich arbeite damit schon ~19 Jahre. Die Stackoverflow-Frage beantwortet das uebrigens IMHO klar darauf hin, dass die Attributs-Zugriff-Variante zu bevorzugen ist.

    Und auch die Aesthetik von

    f.setMyProperty(g.getSomeProperty() * h.getOtherProperty())

    vs

    f.myProperty = g.someProperty * h.otherProperty

    ist in meinen Augen klar zum Vorteil des letzteren, da ich dort in einem Blick trennen kann, was berechnet, und wem das Ergebnis zugewiesen wird.

    YMMV, aber mein Verdacht ist, dass du dich von den Erfahrungen in anderen Sprachen zu sehr leiten laesst. Da kommt's jetzt drauf an, was du erreichen willst: idiomatisch Python programmieren lernen, oder eben dein Ding machen. Beides ist ok, wenn du dich mit letzterem bei mir bewirbst, wirst du abgelehnt :)

    Zur private/public Diskussion ist genug gesagt, ich moechte aber noch mal das Thread-Thema aufgreifen: eine Bibliothek, die mich mit unkontrollierten Threads beglueckt, sehe ich kritisch. Threads sind zum einen *hart*. Es sieht einfach aus, aber du bekommst zB eine beliebiege Reihenfolge von unzusammenhaengenden I2C-Aufrufen auf deinen Bus. Ob und welche Konsequenzen das hat, haengt jetzt von den intimen Details der smbus-Modul-Implementierung ab. Kennst du die? Kannst du garantieren, dass da nix durcheinander kommt? Selbst wenn das ok geht (kann man erst nach Quellcode-Studium der C-Implementierung sagen, und nur durch Kenntnisse der Threading-Behandlung in Python, zB ueber das GIL), bleiben immer noch genug Probleme uebrig:

    - Zeitpunkt der Ermittlung und Abfrage der Werte liegen auseinander. Wie lange haengt vom Zufall des Schedulings ab, und wird bei zunehmender Anzahl von Threads immer unvorhersehbarer. Einfluss kann ich als Programmierer darauf offensichtlich dank deines "geschlossenen" Interfaces *nicht* nehmen. Ich moechte vielleicht Sensor A schnell, und Sensor B nur ab und zu auslesen. Geht nicht.

    - Der PI ist resourcen-beschraenkt. Mehrere Threads bedeuten ggf. langsamere Ausfuehrung deines Haupt-Threads, weil das Scheduling und Task-Switching halt nicht umsonst ist.

    - Ereignisbasierte Programmierung durch zB callbacks wird schwieriger - die laufen wenn man nicht aufpasst in verschiedenen Threads, und treten sich auf die Fuesse.

    Insgesamt finde ich dafuer eine gute Loesung zu finden wesentlich relevanter als die Frage, ob und wo man nun getter/setter nutzt oder wie privat etwas sein soll.

  • Und auch die Aesthetik von

    f.setMyProperty(g.getSomeProperty() * h.getOtherProperty())
    vs
    f.myProperty = g.someProperty * h.otherProperty
    ist in meinen Augen klar zum Vorteil des letzteren,

    Ich mußte jetzt erstmal kräftig ein paar Beiträge zurück blättern, um zu verstehen, daß (und warum) wir vermeintlich unterschiedliche Auffassungen haben. Eigentlich meinen wir (auch noisefloor) nämlich das Gleiche. Was aber in meiner ganz ursprünglichen Frage noch halbgar war, hatte ich schon lange erweitert, aber nicht weiter erwähnt.

    Ich hatte ursprünglich nur get/set in der Art:

    Code
    def getValue():
      return __Value
    def setValue( v ):
      __Value = v

    Das hatte ich zwischenzeitlich erweitert mit:

    Code
    property Value( getValue, setValue )

    Somit habe ich einen pseudo-direkten Zugriff auf die Attribute, kanalisiert aber über die get/set-Methoden, die nebenbei auch noch ggf. weitere Aktionen (oder wenigstens Prüfungen) durchführen.

    Ja, auch ich finde die 2. Methode übersichtlicher. Ich hatte die Diskussion mit

    Zitat

    Getter / Setter sind 'böse'


    nur insofern falsch verstanden, als daß ich es so aufgefaßt hatte, daß man immer lieber direkten Zugriff auf die Attribute zulassen soll und eben keine get/set-Methoden zwischenschalten soll (darf).

    Zitat

    idiomatisch Python programmieren lernen, oder eben dein Ding machen. Beides ist ok, wenn du dich mit letzterem bei mir bewirbst, wirst du abgelehnt :)


    Idiomatisch nur um der Idio(ma)tie Willen? Nein. Es "vernünftig" machen, aber ohne krampfhaft auf Teufel komm raus mir alles vorschreiben zu lassen? Ja. Mich woanders bewerben? Ganz sicher nicht. ;)

    Zitat

    ich moechte aber noch mal das Thread-Thema aufgreifen:


    Gut, daß Du es machst, denn ich hätte demnächst zu diesem Punkt hier eh nachgehakt. Mein aktuelles Projekt ärgert mich und ich will nicht ausschließen, daß "Threading" vielleicht einer der Auslöser für die Probleme ist, die ich gerade damit habe. Mir fehlt aber im Moment auch noch die Idee, wie ich es anders hinbekommen könnte.

    Zitat

    eine Bibliothek, die mich mit unkontrollierten Threads beglueckt, sehe ich kritisch.


    Naja, das würde ich nicht so sehen. Gerade auf dem relativ weit unten angesiedelten Niveau einer Interpreter-Sprache ist das "unkontrolliert" eigentlich in sehr enge Grenzen verwiesen. Ein Binary aus einem C-Kompilat, das seinerseits vielleicht auch noch weitere, fremde Binaries verwendet, ist für mich da schon sehr viel eher ein Grund für Skepsis.

    Ich sehe eine Bibliothek aber eben gerne als "black box", bei der ich mich von "außen" nicht darum kümmern muß, wie sie arbeitet. Das bedeutet, daß ich, am Beispiel dieser simplen Sensoren, mich eben auch nicht darum kümmern will, wie ein i2c-Bus funktioniert und wann / wie ich Werte aus dem Sensor auslesen kann. Ich will die Bib einfach nur benutzen, um ein Objekt zu erschaffen, das mir Werte auf Abruf liefert. Soweit aus Sicht des Anwendungs-Programmierers...

    Der Bibliotheks-Programmierer wiederum muß sich darum kümmern, mit den nur absolut notwendigen Parametern zumindest ein funktionierendes Objekt zu erschaffen. Dazu gibt's dann sinnvollerweise Default-Werte und alles darüber hinaus sind dann "Bonscher", die die Sache runder machen. Auch der Bib-Programmierer muß sich aber normalerweise nicht mehr um die Schicht darunter kümmern, sondern kann seinerseits auch wieder diese als "black box" betrachten. So muß die Bib z.B. nicht wissen, mit welchen Pegeln und welchen Timings der i2c-Bus arbeitet. Es gibt eine API, die regelt, mit welchem Befehl ich einen Modus setze, mit welchem ich einen Wert (oder Datenblock) auslese usw.

    Zitat

    Threads sind zum einen *hart*. Es sieht einfach aus, aber du bekommst zB eine beliebiege Reihenfolge von unzusammenhaengenden I2C-Aufrufen auf deinen Bus. Ob und welche Konsequenzen das hat, haengt jetzt von den intimen Details der smbus-Modul-Implementierung ab. Kennst du die?


    Nein, kann und will ich auch gar nicht. Wenn der low-level-Treiber spackt, gibt's eine Fehlermeldung an den Programmierer des Treibers. ;)


    Zitat

    Zeitpunkt der Ermittlung und Abfrage der Werte liegen auseinander. Wie lange haengt vom Zufall des Schedulings ab, und wird bei zunehmender Anzahl von Threads immer unvorhersehbarer.


    Das ist jetzt aber sehr allgemein und hat eigentlich mit dem "Spielkram", den der RasPi und z.B. ein DS18B20 liefert, nichts mehr zu tun. Eine Hochgeschwindigkeits-Präzisions-Temperaturmessung, bei der ich Werte mit 3 Nachkommastellen im Millisekunden-Intervall bekomme (ja, sowas führen wir bei uns in der Firma durch), führe ich garantiert nicht mit einem RasPi durch. Da bin ich schon froh, wenn er eine sleep(1000) nach nur 1100 abgearbeitet hat.

    Zitat

    Ich moechte vielleicht Sensor A schnell, und Sensor B nur ab und zu auslesen. Geht nicht.


    Klar geht das, aber noch nicht mit dem Source aus den Anfängen dieses Threads. Ich bin aber gerade dabei, die Sensoren als Klassen zu kapseln. Und da hindert mich ja niemand, 2 oder mehr Sensoren zu "erschaffen". Ob die Architektur des Bus-Systems oder die des low-level-Treibers das noch mitmachen, mag dann auf einem anderen Blatt stehen...


    Zitat

    - Der PI ist resourcen-beschraenkt. Mehrere Threads bedeuten ggf. langsamere Ausfuehrung deines Haupt-Threads, weil das Scheduling und Task-Switching halt nicht umsonst ist.
    - Ereignisbasierte Programmierung durch zB callbacks wird schwieriger - die laufen wenn man nicht aufpasst in verschiedenen Threads, und treten sich auf die Fuesse.
    Insgesamt finde ich dafuer eine gute Loesung zu finden wesentlich relevanter als die Frage, ob und wo man nun getter/setter nutzt oder wie privat etwas sein soll.

    Ich finde einen Hintergrund-Thread, der Meßwerte akzeptabel aktuell und mit Timestamp bereit stellt, quasi ähnlich einem Cache, im Moment noch die einzig sinnvolle Lösung. Wenn das "main" Programm die Parameter allerdings in irrsinnige Bereiche schiebt und z.B. den Sensor jede Millisekunde aktiviert, dann wird's blöd. Aber das kann die Bib ja in gewissen Grenzen durch die Setter eindämmen. Wenn der DHT22 z.B. nur alle 2 Sekunden einen Wert liefert, dann kann setInterval(v) prima dafür sorgen, daß das Polling nicht häufiger angeschubst wird.

    Ich hatte mich seinerzeit aber selbst ins Bockshorn gejagt: Ich schrieb sowas in der Art wie "threading oder mittels after()". Und dabei hatte ich vergessen, daß after() ja wohl nur in der GUI zur Verfügung steht, während man in der CLI darauf verzichten muß. Oder gibt's da eine Alternative?

    Und auf Ereignisse + Callback würde ich auch gerne verzichten. Die sind mir (zumindest in den anderen Sprachen, die ich bislang verwende) immer sehr suspekt und machen leider auch viel Probleme.

    Wie gesagt: Bislang (habe noch nichts Gegenteiliges feststellen können) ist mir ein Hintergrund-Thread noch am ehesten sympathisch. Die aktivierende Methode wird sauber durchlaufen und beendet (keine "while AppRunning:" Endlos-Schleife). Einzig die etwas aus der Hand gegebene Kontrolle beim threading ist mir nicht immer ganz geheuer. Führt dies doch auch mal dazu, daß das "main" mit einem Fehler abbricht und der Thread glatt weiter läuft. Irgendwie auch blöd... ;) Aber bisherige Versuche mit bis zu 6 Sensoren brachten den RasPi nun wirklich noch nicht an seine Leistungsgrenzen. Ich sehe eher die Echtzeit-Sprektum-Analyse eines internet-Radio-Streams als Problem, aber weiß Gott nicht die Sensor-Abfragen im 10-Sekunden-Takt. ;)

    Gruß, Michael

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!