Frage zu Klassen

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
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

Hi Leute,

ich habe ein paar Verständnisfragen zu objektorientierter Programmierung. Folgendes Beispiel zeigt den grundlegenden Aufbau des Objektes Reader, das ich bauen möchte.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-


from threading import Lock


class Counter(object):
    def __init__(self, value=0):
        self.value = value
        self.lock = Lock()

    def increase(self, value):
        with self.lock:
            self.value += value

    def get_and_reset(self):
        with self.lock:
            result = self.value
            self.value = 0
        return result


class Reader(object):
    def __init__(self, digital_input, frequency, interval):
        self.digital_input = digital_input
        self.frequency = frequency
        self.interval = interval
        self.count = Counter()

        # Example for some work to calculate
        self.count.increase(self.frequency)
        self.count.increase(self.digital_input)
        self.count.increase(self.interval)

    def show_values(self):
        return self.count.get_and_reset() / 20


def main():
    digital_input = 2
    frequency = 100
    interval = 2
    test = Reader(digital_input, frequency, interval)
    print test.show_values()


if __name__ == '__main__':
    main()
So weit so gut, alles funktioniert wie es soll. Jetzt möchte ich aber im Init-Bereich des Objektes Reader die unten aufgeführten Funktionen ansiedeln, um sie direkt im Init-Bereich abzurufen. Wie gehe ich dabei am besten vor? Einfaches Einfügen und Aufrufen gibt mir Fehlermeldungen aus.

Im grunde möchte ich realisieren, dass ich das Objekt Reader erstellen kann und dadurch automatisch der Thread mit den unten aufgeführten Funktionen startet. Anschließend sollen über objektinterne Funktionen die Counter-Werte verarbeitet werden und verschiedene Ausgaben ermöglicht werden (z.B. wie oben die Funktion show_values).

Code: Alles auswählen

def blink_led(digital_output):
    piface.digital_write(digital_output, 0)
    time.sleep(0.05)
    piface.digital_write(digital_output, 1)


def iter_changes(event, frequency, digital_input):
    for value, _ in groupby(iter_values(event, frequency, digital_input)):
        yield value


def iter_values(event, frequency, digital_input):
    while not event.wait(1.0/frequency):
        yield piface.digital_read(digital_input)


def read_input(count, event, frequency, digital_input):
    for value in iter_changes(event, frequency, digital_input):
        if value == 1:
            count.increase()
            blink_led(digital_input)


def start_thread_read_input(count, event, frequency, digital_input):
    thread = Thread(target=read_input, args=(count, event, frequency, digital_input))
    thread.setDaemon(True)
    thread.start()
Hier ist mein Versuch ... funktioniert aber irgendwie nicht :(. Ich glaube so darf ich den Counter nicht übergeben oder?

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import time
from itertools import groupby
from threading import Lock
from threading import Thread
from threading import Event
import pifacedigitalio as piface


class Counter(object):
    def __init__(self, value=0):
        self.value = value
        self.lock = Lock()

    def increase(self):
        with self.lock:
            self.value += 1

    def get_and_reset(self):
        with self.lock:
            result = self.value
            self.value = 0
        return result


class Reader(object):
    def __init__(self, digital_input, frequency, interval):
        self.digital_input = digital_input
        self.frequency = frequency
        self.interval = interval
        self.count = Counter()
        self.event = Event()

        piface.digital_write(digital_input, 1)

        # user defined functions
        def blink_led(par_digital_input):
            piface.digital_write(par_digital_input, 0)
            time.sleep(0.05)
            piface.digital_write(par_digital_input, 1)

        def iter_values(par_event, par_frequency, par_digital_input):
            while not par_event.wait(1.0/par_frequency):
                yield piface.digital_read(par_digital_input)

        def iter_changes(par_event, par_frequency, par_digital_input):
            for value, _ in groupby(iter_values(par_event, par_frequency,
                                                par_digital_input)):
                yield value

        def read_input(par_count, par_event, par_frequency, par_digital_input):
            for value in iter_changes(par_event, par_frequency,
                                      par_digital_input):
                if value == 1:
                    par_count.increase()
                    blink_led(par_digital_input)

        # start of thread
        self.thread = Thread(target=read_input,
                             args=(self.count, self.event,self.frequency, 
                                   self.interval))
        self.thread.setDaemon(True)
        self.thread.start()

    def show_values(self):
        return self.count.get_and_reset()


def main():
    piface.init()
    digital_input = 1
    frequency = 100
    interval = 5
    event_a = Event()
    test = Reader(digital_input, frequency, interval)
    
    timestamp = int(time.time() / interval) * interval
    timestamp += interval
    while not event_a.wait(max(0, timestamp-time.time())):
        timestamp += interval
        print test.show_values()


if __name__ == '__main__':
    main()
Ich würde mich sehr über ein paar Anregungen freuen!

Gruß
mobby
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

mobby hat geschrieben:Einfügen und Aufrufen gibt mir Fehlermeldungen aus.
mobby hat geschrieben:Hier ist mein Versuch ... funktioniert aber irgendwie nicht
Du postest über 150 Zeilen Code, aber keine Fehlermeldung und keine Fehlerbeschreibung. Weniger Code und mehr Fehlermeldung, dann kann Dir bestimmt geholfen werden.
a fool with a tool is still a fool, www.magben.de, YouTube
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@mobby: keine Ahnung was hier nicht funktioniert. Gibt es Fehlermeldungen? Was ist das erwartete Verhalten und was ist das beobachtete Verhalten?

Man kann mit "from ... import" eine Liste an Namen importieren, und muß nicht jeden einzeln angeben. Funktionen innerhalb von Funktionen zu definieren macht fast nie Sinn, hier auf jeden Fall nicht. Wenn Du wirklich mit einer bestimmten Frequenz etwas lesen willst, ist wait viel zu ungenau. Durch das yield kannst Du ja nicht wissen, wie viel Zeit vergangen ist, bis wait wieder aufgerufen wird. Um das Verändern eines Wertes herauszufinden, würde ich kein groupby verwenden, weil das viel komplizierter zu durchschauen ist als eine einfache if-Abfrage. Was sollen Die par_-Präfixe an den ganzen Argumenten? Soll das Programm die Schläge beim Golf zählen? Etwas sprechendere Namen helfen Dir und uns, das Problem besser zu verstehen. Z.B. was hat par_digital_input mit interval zu tun? Warum schleifst Du überall Events durch, wenn Du sie sowieso nicht benutzt? Um zu warten gibt es einfach time.sleep. Und wenn Du eine Schleife per Event abbrechen willst, kannst Du das in der äußersten Schleife auch tun, weil dann einfach das yield in der innersten Schleife nicht mehr zurückkehrt.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@Sirius3: Danke für die Antwort. Es gab in dem Sinne keine Fehlermeldungen, es hat einfach nur nicht funktioniert. Ich habe den Code jetzt so gebaut wie es auf jeden Fall funktioniert. Deine Anregungen habe ich einfließen lassen. Groupby brauche ich weil ich Impulse an einem digitalen Eingang lese und diese sich aus einem Flankenwechsel von 0 1 0 bzw. 0 111 0 oder 0 n*1 0 abzeichnen. Stimmt das mit dem yield jetzt?

Was mir eigentlich dabei auf dem Herzen lag war folgendes: Ich möchte nicht, dass über das initialisierte Objekt Funktionen wie z.B. read_input() aufgerufen werden können. Das brauche ich ja nur intern. Es soll eigentlich nur die Funktion show_values() verfügbar sein. Wie könnte ich das realisieren? Bzw. ist das überhaupt Sinn und Zweck eines Objektes?

Hier auf jeden Fall mal der funktionierende Code:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import time
from itertools import groupby
from threading import Lock, Thread
import pifacedigitalio as piface


class Counter(object):
    def __init__(self, value=0):
        self.value = value
        self.lock = Lock()

    def increase(self):
        with self.lock:
            self.value += 1

    def get_and_reset(self):
        with self.lock:
            result = self.value
            self.value = 0
        return result


class Reader(object):
    def __init__(self, digital_input, frequency):
        self.digital_input = digital_input
        self.frequency = frequency
        self.count = Counter()

        piface.digital_write(digital_input, 1)

        # start of thread
        self.thread = Thread(target=self.read_input)
        self.thread.setDaemon(True)
        self.thread.start()

    # user defined functions
    def blink_led(self):
        piface.digital_write(self.digital_input, 0)
        time.sleep(0.05)
        piface.digital_write(self.digital_input, 1)

    def iter_values(self):
        while True:
            yield piface.digital_read(self.digital_input)
            time.sleep(1.0/self.frequency)

    def iter_changes(self):
        for value, _ in groupby(self.iter_values()):
            yield value

    def read_input(self):
        for value in self.iter_changes():
            if value == 1:
                self.count.increase()
                self.blink_led()

    # functions to use by object
    def show_values(self):
        return self.count.get_and_reset()


def main():
    piface.init()
    digital_input = 0
    frequency = 100
    interval = 5
    test = Reader(digital_input, frequency)

    timestamp = int(time.time() / interval) * interval
    while True:
        timestamp += interval
        time.sleep(max(0, timestamp-time.time()))
        print test.show_values()


if __name__ == '__main__':
    main()
BlackJack

@mobby: Man könnte entweder alle anderen Methoden als ”intern” Kennzeichnen in dem man den Namen mit einem Unterstrich beginnt, oder man schreibt sich eine Funktion die so einen Reader erstellt und dann nur die Methode zurück gibt die von aussen aufgerufen werden soll. Oder man dokumentiert das einfach das nur eine bestimmte Methode aufgerufen werden soll. Wobei ich in so einem Fall diese Methode dann als `__call__()` implementieren würde und dann kann man dokumentieren das `Reader`-Objekte aufrufbar sind.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@BlackJack: Vielen dank für die Rückmeldung. Für mich ist die Variante der "internen" Kennzeichnung optimal. Kombiniert mit einer Dokumentation komme ich mit diesem Ansatz schnellstmöglich zum Ziel.

Gruß
mobby
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

//Nachtrag:

Ich hätte noch einen kleinen Nachtrag. Mir ist aufgefallen, dass ich das Event() Objekt vorallem dafür genutzt habe, damit sich der Thread beenden kann. Also wie kritisch ist das mit der Genauigkeit? Ich habe meine sensoren mit beiden Varianten (sleep und event) getestet und keinen Unterschied feststellen können. Kann mir dazu jemand Genaueres sagen?
Antworten