richtiges Zählen

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Moin,

hab am WE mein RPi, an welchem ein Taster angeschlossen ist, mal wieder ausgegraben und mir überlegt, wie in Python eigentlich "korrekt" gezählt wird. Also ich meine, wie sämtliche Flankenwechsel aufsummiert werden könnten. Dazu sind mir drei Arten eingefallen (ohne das wunderschöne "global")

mit einer Klasse:

Code: Alles auswählen

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

from __future__ import print_function
from gpiozero import Button
import signal


class Counter(object):
    count = 0

    def __init__(self):
        Counter.count += 1

    def counting(self):
        return self.count


def counting_sth():
    print(Counter().count)


def main():
    try:
        button = Button(21)
        button.when_pressed = counting_sth
        signal.pause()
    except KeyboardInterrupt:
        pass
    finally:
        button.close()
 
 
if __name__ == '__main__':
    main()
mit itertools.count:

Code: Alles auswählen

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

from __future__ import print_function
from gpiozero import Button
import signal
from itertools import count


def do_sth(my_counter):
    print(counting_smth(my_counter))


def counting_smth(my_counter):
    return my_counter.next()


def main():
    try:
        my_counter = count(1)
        button = Button(21)
        button.when_pressed = lambda: do_sth(my_counter)
        signal.pause()
    except KeyboardInterrupt:
        pass
    finally:
        button.close()
 
 
if __name__ == '__main__':
    main()
mittels Dekorator

Code: Alles auswählen

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

from __future__ import print_function
from gpiozero import Button
import signal
from functools import wraps


def counter(func):
    @wraps(func)
    def counting():
        counting.count += 1
        return func()
    counting.count = 0
    return counting


@counter
def counting_smth():
    print('{}'.format(counting_smth.count))


def main():
    try:
        button = Button(21)
        button.when_pressed = counting_smth
        signal.pause()
    except KeyboardInterrupt:
        pass
    finally:
        button.close()
 
 
if __name__ == '__main__':
    main()
Nun stellt sich mir die Frage,
- was wäre die empfehlenswerteste Methode?
- ist die Klasse so ok bzw, kann man diese verbessern bzw. mach das Verwenden einer Klasse hier überhaupt einen Sinn?
- wie müsste ich die Klasse abändern, wenn ich mehrere Taster/Pins ansprechen würde? (meine OOP Begabung ist limitiert)
BlackJack

@lackschuh: Die Lösung mit der Klasse ist falsch, das ist ja letztendlich doch wieder eine globale Variable. Exemplare der Klasse werden nicht wiklich benutzt, denn die `__init__()` wird ja letztendlich nur wegen ihres globalen Effekts aufgerufen wie eine Funktion.

Die `counting()`-Methode wird überhaupt nicht verwendet.

Bei `itertools.count` frage ich mich was die ganzen Funktionen sollen die da nacheinander aufgerufen werden und immer nur das `count`-Objekt weiterreichen.

Auch beim Dekorator habe ich den Sinn so überhaupt nicht verstanden. Das ist einfach nur magisch und verwirrend.

Ich persönlich würde wahrscheinlich tatsächlich eine Klasse schreiben. Denn an den Zähler bei `itertools.count` kommt man von aussen nicht heran ohne ihn zu verändern und der Dekorator ist IMHO verwirrend.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8
from __future__ import print_function
import signal
from gpiozero import Button
 
 
class Counter(object):
 
    def __init__(self):
        self.count = 0

    def __iter__(self):
        return self

    def __next__(self):
        self.count += 1
        return self.count
 
    next = __next__

 
def counting_sth(counter):
    print(next(counter))
 
 
def main():
    try:
        counter = Counter()
        with Button(21) as button:
            button.when_pressed = lambda: counting_sth(counter)
            signal.pause()
    except KeyboardInterrupt:
        pass
 
 
if __name__ == '__main__':
    main()
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Hallo

Vielen Dank für die rasche Anregung.
Ja, das mit itertools und den zwei Funktionen ist unglücklich geschrieben. Die ``do_sth()`` kann man getrost weglassen.
Was meinst du mit ``von aussen``?

Die Sache mit dem Dekorator habe ich mal in einem anderen Programm für's Zählen verwendet und übernommen.
viewtopic.php?f=7&t=34847&hilit=dekorat ... g&start=15

Gruß
BlackJack

@lackschuh: Der Zähler bei einem `itertools.count`-Objekt ist im Objekt gekapselt. Man kommt an den aktuellen Wert von aussen nicht heran. Die einzige Möglichkeit ist `next()` zu verwenden, was aber den Zähler selbst in dem Objekt verändert.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich würde einfach count aus dem itertools Modul nehmen. Sehe hier keinen Vorteil bei einer eigenen Klasse.
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Danke für die Inputs. Ich widme mich dem gigantischen OOP Thema und erweitere die Klasse und stelle sie dann hier zur Korrektur rein. Mit itertools würde sich das ganze auf 25 Zeilen verkürzen mittels BJ's Input:

[codebox=python file=Unbenannt.txt]#!/usr/bin/env python
# coding: utf-8

from __future__ import print_function
from gpiozero import Button
import signal
from itertools import count


def counting(counter):
print(counter.next())


def main():
try:
counter = count(1)
with Button(21) as button:
button.when_pressed = lambda: counting(counter)
signal.pause()
except KeyboardInterrupt:
pass


if __name__ == '__main__':
main()[/code]
Antworten