Hallo zusammen,
ich schaue mir gerade den Code von`pySerial` an und die Entwickler schreiben eine Basis-Klasse mit leeren Methoden und überschreiben die dann in den Klassen, die davon erben. Ich frage mich, wieso?
Wenn die Klasse doch gar keine Funktionalität besitzt, wieso brauche ich die dann?
Hier geht's direkt zu der Klasse
Der Thread-Support, den `pySerial` hier bietet ist wohl noch nicht ausreichend getestet, zumindest steht in der Doku This implementation is currently in an experimental state. Use at your own risk.. Da es aber schon in der Doku erwähnt wird, denke ich nicht dass die Klasse nicht fertig ist und da noch was reinkommt.
Danke und Grüße
Dennis
Sinn einer Basis-Klasse mit leeren Methoden
Da sollte man einiges ändern, zumindest die Basisklasse auch wirklich nutzen:
Code: Alles auswählen
class Protocol(object):
"""
Protocol as used by the ReaderThread. This base class provides empty
implementations of all methods.
"""
def __init__(self):
self.buffer = bytearray()
self.transport = None
def connection_made(self, transport):
"""Store transport"""
self.transport = transport
def data_received(self, data):
"""Called with snippets received from the serial port"""
def connection_lost(self, exc):
"""Forget transport"""
self.transport = None
if isinstance(exc, Exception):
raise exc
def handle_packet(self, packet):
"""Process packets - to be overridden by subclassing"""
raise NotImplementedError('please implement functionality in handle_packet')
class Packetizer(Protocol):
"""
Read binary packets from serial port. Packets are expected to be terminated
with a TERMINATOR byte (null byte by default).
The class also keeps track of the transport.
"""
TERMINATOR = b'\0'
def data_received(self, data):
"""Buffer received data, find TERMINATOR, call handle_packet"""
self.buffer.extend(data)
while self.TERMINATOR in self.buffer:
packet, self.buffer = self.buffer.split(self.TERMINATOR, 1)
self.handle_packet(packet)
class FramedPacket(Protocol):
"""
Read binary packets. Packets are expected to have a start and stop marker.
The class also keeps track of the transport.
"""
START = b'('
STOP = b')'
def __init__(self):
super().__init__()
self.in_packet = False
def connection_lost(self, exc):
"""Forget transport"""
self.in_packet = False
del self.buffer[:]
super().connection_lost(exc)
def data_received(self, data):
"""Find data enclosed in START/STOP, call handle_packet"""
for byte in serial.iterbytes(data):
if byte == self.START:
self.in_packet = True
elif byte == self.STOP:
self.in_packet = False
self.handle_packet(bytes(self.buffer)) # make read-only copy
del self.buffer[:]
elif self.in_packet:
self.buffer.extend(byte)
else:
self.handle_out_of_packet_data(byte)
def handle_out_of_packet_data(self, data):
"""Process data that is received outside of packets"""
pass
Danke für die Antwort. Das heißt, dass macht man nicht so und ist keine Bibliothek von der ich mir etwas abschauen könnte.
Noch etwas anders, weil mir das auch wieder in diesem Zusammenhang aufgefallen ist. Ich programmiere ja nur zum Hobby und manche Bibliotheken "brauche" ich nur, wenn ich Code in Foren nachvollziehen will, von dem her kann ich mir einfach nicht alles merken und muss viel in der Doku nachschauen. Wäre es eigentlich nicht viel sinnvoller, wenn die Beispiele in der Doku gleich auch das `with`-Statement verwenden würden oder wenn wenigstens irgendwo darauf hingewiesen wird? Hier hatte ich es noch halb im Kopf und habe dann irgendwann die Basis-Klasse mit `__enter__` und `__exit__` gefunden. Ich meine auch andere Bibliotheken haben den Kontextmanager implementiert, aber weisen nicht darauf hin. Ja klar, kann ja jeder dokumentieren wie er will, ich dachte nur dass ist so eine sinnvolle Funktion, ich würde das auf jeden Fall erwähnen. (Vielleicht habe ich es auch überlesen oder in der Doku nicht gefunden, das will ich nicht ausschließen)
Grüße
Dennis
Noch etwas anders, weil mir das auch wieder in diesem Zusammenhang aufgefallen ist. Ich programmiere ja nur zum Hobby und manche Bibliotheken "brauche" ich nur, wenn ich Code in Foren nachvollziehen will, von dem her kann ich mir einfach nicht alles merken und muss viel in der Doku nachschauen. Wäre es eigentlich nicht viel sinnvoller, wenn die Beispiele in der Doku gleich auch das `with`-Statement verwenden würden oder wenn wenigstens irgendwo darauf hingewiesen wird? Hier hatte ich es noch halb im Kopf und habe dann irgendwann die Basis-Klasse mit `__enter__` und `__exit__` gefunden. Ich meine auch andere Bibliotheken haben den Kontextmanager implementiert, aber weisen nicht darauf hin. Ja klar, kann ja jeder dokumentieren wie er will, ich dachte nur dass ist so eine sinnvolle Funktion, ich würde das auf jeden Fall erwähnen. (Vielleicht habe ich es auch überlesen oder in der Doku nicht gefunden, das will ich nicht ausschließen)
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
- __blackjack__
- User
- Beiträge: 14097
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Dennis89: Das kann schon Sinn machen in Bibliotheken wenn man bestimmte „hooks“ hat, die aber optional sind. Sirius3 ist jetzt von den abgeleiteten Klassen ausgegangen die wir *kennen*. Es kann ja durchaus sein, dass die noch andere implementieren wollen oder das beliebige *Benutzer* da später noch eigene Klassen von ableiten wollen, die mit den vorhandenen abgeleiteten Klassen weniger gemein hat.
`Protocol` ist als Begriff so etwas ähnliches wie „Interface“, das heisst diese Klasse dient auch als Dokumentation, die man für Interfaces in Python ja sonst nur informell macht/machen kann.
`Protocol` ist als Begriff so etwas ähnliches wie „Interface“, das heisst diese Klasse dient auch als Dokumentation, die man für Interfaces in Python ja sonst nur informell macht/machen kann.
“It is easier to change the specification to fit the program than vice versa.” — Alan J. Perlis
- DeaD_EyE
- User
- Beiträge: 1254
- Registriert: Sonntag 19. September 2010, 13:45
- Wohnort: Hagen
- Kontaktdaten:
Das hat schon einen Grund, dass die Basisklasse Methoden als Platzhalter hat, die durch Transport aufgerufen werden. Wenn man z.B. auf connection_made nicht reagieren will, muss man diese Methode noch nicht anlegen. Dann hat man z.B. keine Referenz zu Transport im Protocol. Will man hingegen einen Traceback bei connection_lost abfangen, muss man diese Methode implementieren, weil in der Basisklasse die Exception ausgelöst wird, ausgelöst durch Transport.
Wenn man hingegen in seiner Basisklasse Platzhalter anlegt und erzwingen will, dass die vererbende Klasse die Methoden implementiert, sind AbstractBaseClasses eine Anlaufstelle.
Wenn man hingegen in seiner Basisklasse Platzhalter anlegt und erzwingen will, dass die vererbende Klasse die Methoden implementiert, sind AbstractBaseClasses eine Anlaufstelle.
Code: Alles auswählen
from abc import ABC, abstractmethod
class Base(ABC):
@abstractmethod
def foo(self, x: int, y: int):
pass
@abstractmethod
def bar(self):
pass
class Foo(Base): pass
class Bar(Base):
def foo(self):
pass
def bar(self):
pass
for cls in (Bar, Foo):
try:
obj = cls()
except TypeError as e:
print(e)
else:
print(obj)
obj.foo()
PS: obj.foo() auskommentieren und mypy spuckkt einen Fehler weniger aus. Möglicherweise gibt es da eine bessere Lösung. TypeHints sind nicht einfach und oft stellt man sich selbst das Bein.[deadeye@nexus ~]$ python abstract.py
<__main__.Bar object at 0x7f96a3336f90>
Can't instantiate abstract class Foo without an implementation for abstract methods 'bar', 'foo'
[deadeye@nexus ~]$ mypy abstract.py
abstract.py:26: error: Cannot instantiate abstract class "Foo" with abstract attributes "bar" and "foo" [abstract]
abstract.py:31: error: Missing positional arguments "x", "y" in call to "foo" of "Base" [call-arg]
Found 2 errors in 1 file (checked 1 source file)
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
- DeaD_EyE
- User
- Beiträge: 1254
- Registriert: Sonntag 19. September 2010, 13:45
- Wohnort: Hagen
- Kontaktdaten:
Das ist der Denkfehler. Wenn ich connecion_made nicht verwenden will, weil ich keine Referenz zum Transport benötige, muss ich diese Methode nicht erstellen. Würde die Basisklasse z.B. bei connection_made automatisch den Transport dem Protokoll zuweisen, dann muss man die Methode erstellen, wenn man die Referenz nicht will.Sirius3 hat geschrieben: Samstag 23. August 2025, 07:12 Es ist ja offensichtlich, dass eine Methode connection_made ein Transportobjekt setzt und ein connection_lost dieses Objekt wieder löscht. Und das reduziert auch einiges an kopiertem Code.
Ja es gibt es Szenarien, wo man ausschließlich Daten bekommt und nichts senden kann, weil es keinen TX gibt.
Das hat schon seine Berechtigung und außerdem ist asyncio genauso gestaltet und ein weiterer Grund ist der Support für Asyncio. Ich hab schön öfters pySerial mit Asyncio genutzt. Das mit den Threads würde ich eher nicht nutzen.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Guten Morgen,
danke für eure Antworten.
Was verstehe ich hier noch nicht ganz?
Grüße
Dennis
danke für eure Antworten.
Ich verstehe diesen Ansatz nicht. Für mich macht es Sinn wenn ich von einer Klasse erbe, wenn die Methoden hat, die auf mein Objekt auch zutreffen und das man dann nach belieben einzelne überschreibt. Ich sehe aber keinen Sinn von einer Klasse mit fast, ausschließlich leeren Methoden zu erben, weil dann muss ich die so oder so in meiner Klasse definieren. Der Ersteller der Klasse gibt mir so gesehen eigentlich nur eine Empfehlung für Methoden-Namen. `connectin_lost` hat etwas Code, ja, aber ja sehr allgemeingültig. (Sehe da übrigens viel Gemeinsamkeiten zu deinem Code von gestern, Zufall blackjack?)__blackjack__ hat geschrieben: Freitag 22. August 2025, 22:29 Es kann ja durchaus sein, dass die noch andere implementieren wollen oder das beliebige *Benutzer* da später noch eigene Klassen von ableiten wollen, die mit den vorhandenen abgeleiteten Klassen weniger gemein hat.
Was verstehe ich hier noch nicht ganz?
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
- DeaD_EyE
- User
- Beiträge: 1254
- Registriert: Sonntag 19. September 2010, 13:45
- Wohnort: Hagen
- Kontaktdaten:
Wenn man es gut dokumentiert, muss man noch nicht einmal im Quellcode nachsehen: https://docs.python.org/3/library/async ... o-protocol
Für mich ist folgender Satz der wichtigste: Subclasses of abstract base protocol classes may implement some or all methods.
Es geht auch darum, anderen Entwickler die größtmögliche Freiheit zu gewährleisten und das geht nicht, wenn die Basis-Klassen zu restriktiv sind.
Anwender sollen erstmal den High-Level-Code verwenden und in Spezialfällen z.B. für UDP eine Protocol-Klasse erstellen.
Und falls man keine Lust hat sich mit dem Low-Level-Code zu beschäftigen, könnte man ChatGPT nach bekannten Modulen befragen. Hier ein Beispiel für UDP-Server, wo man normalerweise Protocol selbst implementieren müsste: https://chatgpt.com/share/68aada15-c1e0 ... 13f68b5e58
Ich würde dann z.B. dieses Paket nutzen: https://pypi.org/project/asyncio-dgram
Für mich ist folgender Satz der wichtigste: Subclasses of abstract base protocol classes may implement some or all methods.
Es geht auch darum, anderen Entwickler die größtmögliche Freiheit zu gewährleisten und das geht nicht, wenn die Basis-Klassen zu restriktiv sind.
Anwender sollen erstmal den High-Level-Code verwenden und in Spezialfällen z.B. für UDP eine Protocol-Klasse erstellen.
Und falls man keine Lust hat sich mit dem Low-Level-Code zu beschäftigen, könnte man ChatGPT nach bekannten Modulen befragen. Hier ein Beispiel für UDP-Server, wo man normalerweise Protocol selbst implementieren müsste: https://chatgpt.com/share/68aada15-c1e0 ... 13f68b5e58
Ich würde dann z.B. dieses Paket nutzen: https://pypi.org/project/asyncio-dgram
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Nicht nur für die Methoden-Namen, sondern auch für die Signatur. Zudem besteht so im Code die Möglichkeit, die Beziehung nachzuvollziehen. Es gibt verschiedene etablierte Konzepte, die auf diesem Mechanismus (Basisklasse, die zum Überschreiben gedacht ist) aufbauen (z.B. SAX-Parser oder das Visitor-Pattern; für beides gibt es auch Beispiele in der Standardbibliothek: https://docs.python.org/3/library/xml.s ... entHandler https://docs.python.org/3/library/ast.h ... odeVisitor).Dennis89 hat geschrieben: Sonntag 24. August 2025, 08:31 Der Ersteller der Klasse gibt mir so gesehen eigentlich nur eine Empfehlung für Methoden-Namen.
Danke für die weiteren Antworten.
Naja ich sag mal so, ich verstehe es etwas. Vielleicht kann man das auch besser nachvollziehen, wenn man selbst mal Teil von so größeren Objekten war.
Grüße
Dennis
Naja ich sag mal so, ich verstehe es etwas. Vielleicht kann man das auch besser nachvollziehen, wenn man selbst mal Teil von so größeren Objekten war.
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]