Code aus .h Datei "entschlüsseln"

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
YAPD
User
Beiträge: 120
Registriert: Dienstag 27. Juli 2021, 23:23
Wohnort: Frankfurt am Main

Hi Zusammen,

kann mir jemand vielleicht erklären, was der äquivalente Code in
reinem Python für folgenden Code wäre :

Code: Alles auswählen

typedef enum {
    START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD,
    IN_QUOTED_FIELD, ESCAPE_IN_QUOTED_FIELD, QUOTE_IN_QUOTED_FIELD,
    EAT_CRNL,AFTER_ESCAPED_CRNL
} ParserState;
Ich weiß dass es typedef so in Python nicht gibt, man müsste es praktisch
umschreiben. Ist "ParserState" dann ein Objekt ?

VG
YAPD
-----
Yet Another Python Developer
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@YAPD,

das ist eine Deklaration für ein Enum in C.
"ParseState" ist der Name des Enum.

Enum gibt es in Python auch. Da muss man allerdings direkt Werte angeben:

Code: Alles auswählen

from enum import Enum, auto

class ParserState(Enum):
    START_RECORD = auto()
    START_FIELD = auto()
    ESCAPED_CHAR = auto()
    IN_FIELD = auto()
    IN_QUOTED_FIELD = auto()
    ESCAPE_IN_QUOTED_FIELD = auto()
    QUOTE_IN_QUOTED_FIELD = auto()
    EAT_CRNL = auto()
    AFTER_ESCAPED_CRNL = auto()

Statt auto() kann man auch beliebige Werte angeben. auto() nummeriert einfach die Zahlen von 1.. bis durch.
Benutzeravatar
YAPD
User
Beiträge: 120
Registriert: Dienstag 27. Juli 2021, 23:23
Wohnort: Frankfurt am Main

rogerb hat geschrieben: Samstag 14. August 2021, 14:42 @YAPD,

das ist eine Deklaration für ein Enum in C.
"ParseState" ist der Name des Enum.

Enum gibt es in Python auch. Da muss man allerdings direkt Werte angeben:

Code: Alles auswählen

from enum import Enum, auto

class ParserState(Enum):
    START_RECORD = auto()
    START_FIELD = auto()
    ESCAPED_CHAR = auto()
    IN_FIELD = auto()
    IN_QUOTED_FIELD = auto()
    ESCAPE_IN_QUOTED_FIELD = auto()
    QUOTE_IN_QUOTED_FIELD = auto()
    EAT_CRNL = auto()
    AFTER_ESCAPED_CRNL = auto()

Statt auto() kann man auch beliebige Werte angeben. auto() nummeriert einfach die Zahlen von 1.. bis durch.
Ah OK, danke dir. Das ist interessant. Da es eigentlich nur ein
Objekt ist, das zurückgegeben wird, dachte ich mir, vielleicht
einfach so :

Code: Alles auswählen

class ParserState() :
    
    def __init__(self) :
        
        Self.START_RECORD = None
        Self.START_FIELD = None
        Self.ESCAPED_CHAR = None
        Self.IN_FIELD = None
        Self.IN_QUOTED_FIELD = None 
        Self.ESCAPE_IN_QUOTED_FIELD = None
        Self.QUOTE_IN_QUOTED_FIELD = None
        Self.EAT_CRNL = None
        Self.AFTER_ESCAPED_CRNL = None 

x = ParserState( )
Im Prinzip macht es doch nichts anderes oder ?

Bei deienm Beispiel erhalte ich :

Code: Alles auswählen

TypeError: __call__() missing 1 required positional argument: 'value'
VG
YAPD
-----
Yet Another Python Developer
nezzcarth
User
Beiträge: 1764
Registriert: Samstag 16. April 2011, 12:47

YAPD hat geschrieben: Samstag 14. August 2021, 14:48 Im Prinzip macht es doch nichts anderes oder ?
So als Faustformel sagt man normalerweise, dass man tendenziell keine Klasse schreibt, die keine Methoden umfasst, die auf den gekapselten Daten operieren. In dem Fall würden man dann eher zu einem namedtuple greifen. Enums in Python haben aber davon abweichende Eigenschaften, wie der Name (Enumeration) schon andeutet; sie sind in der Standardvariante im Grunde eine Sammlung (semantisch zusammenhängender) geordneter Konstanten, bzw. Namen für Magic Values.
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@YAPD,

du erhältst den Fehler, weil du Enum falsch benutzt.

Code: Alles auswählen

ParserState()
So würde man eine Instanz einer Klasse erzeugen. Das geht bei einem Enum nicht.

Bei einem Enum ist das Datenfeld, der Wert selbst (hoffe das mach Sinn?)
Ein (etwas konstruiertes) Beispiel:

Code: Alles auswählen

from enum import Enum


class Switch(Enum):
    ON = 0
    OFF = 1

class Radio():
    def __init__(self):
        self.state = Switch.OFF

    def turn_on(self):
        self.state = Switch.ON

    def turn_off(self):
        self.state = Switch.OFF

    def __repr__(self):
        return f"<Radio state: {self.state}>"

radio = Radio()
radio.turn_on()
print(radio)

"""
Ausgabe:
<Radio state: Switch.ON>
"""
Wie du siehst haben ON und OFF bestimmte Werte (0 und 1). Diese werden vordergründig aber nicht genutzt.

Man kann ein Enum auch durch eine einfache Klasse ersetzen:

Code: Alles auswählen

class Switch():
    ON = 0
    OFF = 1
Dann würde aber der Wert der Attribute ausgegeben:

Code: Alles auswählen

"""
Ausgabe:
<Radio state: 0>
"""
Es gibt Enums in dieser Form noch nicht sehr lange in Python. Man kann auch oft mit anderen Mitteln den gleichen Zweck erreichen. Hier zum Beispiel mit einer einfachen Konstanten:

Code: Alles auswählen

STATE = True

Außerdem:
Die ParserState Klasse, die du gepostet hattest, ist nicht mit einem Enum zu vergleichen, da man mehrere Instanzen davon bilden könnte. Das macht bei einem Enum keinen Sinn. Man möchte es sogar vermeiden.
Benutzeravatar
YAPD
User
Beiträge: 120
Registriert: Dienstag 27. Juli 2021, 23:23
Wohnort: Frankfurt am Main

nezzcarth hat geschrieben: Samstag 14. August 2021, 14:54 So als Faustformel sagt man normalerweise, dass man tendenziell keine Klasse schreibt, die keine Methoden umfasst, die auf den gekapselten Daten operieren. In dem Fall würden man dann eher zu einem namedtuple greifen. Enums in Python haben aber davon abweichende Eigenschaften, wie der Name (Enumeration) schon andeutet; sie sind in der Standardvariante im Grunde eine Sammlung (semantisch zusammenhängender) geordneter Konstanten, bzw. Namen für Magic Values.
Das habe ich mir schon eigentlich so gemerkt, aber manchmal scheint es doch angebracht zu sein,
wie globale Variablen zu benutzen. ;)

@RogerB :

Vielen Dan auch für dein Beispiel bzgl. ENUM. Der Sinn erschließt sich noch nicht so
ganz dahinter, aber das kriege ich och noch raus ;)
Die ParserState Klasse, die du gepostet hattest, ist nicht mit einem Enum zu vergleichen, da man mehrere Instanzen davon bilden könnte. Das macht bei einem Enum keinen Sinn. Man möchte es sogar vermeiden.
Könntest du das noch ein bisschen ausführen ?
-----
Yet Another Python Developer
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@YAPD,

Bei einem Enum geht es ja darum , dass man EINEN Satz von verschiedenen gleichartigen "Eigenschaften" hat. Zum Beispiel Farben:

Color.RED, Color.BLUE, Color.GREEN
oder Zustände
State.ON, State.OFF

Daher sollte es nur EINEN Satz Farben und EINEN Satz Zustände geben. Mehr braucht man nicht und mehr wäre auch verwirrend.
Instanzen von Klassen sind ja eigenständige Objekte nach dem gleichen Bauplan gebaut. Da will man bewusst verschiedene Versionen also Instanzen, der gleichen Art verwalten. Da braucht man mehrere Objekte.

Bei deiner Klassendeklaration waren die Attribute als Instanzattribute angelegt. (Verwendung von "self")
Wären die Attribute als Klassenattribute angelegt, würden sie der Klasse und nicht der Instanz "gehören". Dann gäbe es jedes Attribut nur einmal und das käme dem Enum daher näher.

Hoffe das macht Sinn.
Benutzeravatar
__blackjack__
User
Beiträge: 14056
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Da die `Enum`-Werte Singletons sind, also jeweils nur *einmal* existieren, kann (und sollte) man die in der Regel dann auch mit ``is`` und ``is not`` vergleichen.

Vor dem `enum`-Modul hätte man den C-Code in Python so umgesetzt:

Code: Alles auswählen

(
    START_RECORD,
    START_FIELD,
    ESCAPED_CHAR,
    IN_FIELD,
    IN_QUOTED_FIELD,
    ESCAPE_IN_QUOTED_FIELD,
    QUOTE_IN_QUOTED_FIELD,
    EAT_CRNL,
    AFTER_ESCAPED_CRNL,
) = range(9)
Mit dem `enum`-Modul müsste man eigentlich `IntEnum` statt `Enum` verwenden. Allerdings hat man dann den gleichen Nachteil, den man auch in C hat: das sind eigentlich nur Namen für ganze Zahlen und man kann verschiedene Enums miteinander vergleichen, die man eigentlich nicht vergleichen können sollte:

Code: Alles auswählen

In [89]: class A(IntEnum): 
    ...:     FOO = 0 
    ...:     BAR = 1 
    ...:                                                                        

In [90]: class B(IntEnum): 
    ...:     ONE = 1 
    ...:     TWO = 2 
    ...:                                                                        

In [91]: A.BAR == B.ONE                                                         
Out[91]: True
Das kann zu Fehlern führen weil man Äpfel mit Birnen vergleicht. Bei `Enum` geht das nicht:

Code: Alles auswählen

In [92]: class A(Enum): 
    ...:     FOO = 0 
    ...:     BAR = 1 
    ...:                                                                        

In [93]: class B(Enum): 
    ...:     ONE = 1 
    ...:     TWO = 2 
    ...:                                                                        

In [94]: A.BAR == B.ONE                                                         
Out[94]: False
Das wäre auch `False` wenn Name *und* Wert in beiden `Enum`-Werten gleich wären, weil es ein unterschiedlicher Datentyp ist.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten