Forum: PC-Programmierung Python: enum wie in C


von Mehmet K. (mkmk)


Lesenswert?

Servus allerseits

Benutze Python (v2.7.5) dazu, um meine MCU-Projekte waehrend der 
Entwicklung vom PC aus zu testen.
Python Programm sendet also einen Befehl, und die MCU macht irgendwas.
Solange die Anzahl der Befehle nicht gross ist bzw. deren Reihenfolge 
nicht staendig sich aendert, ist die Welt in Ordnung.
Nun habe ich aber leider auf der MCU-Seite eine sich staendig aendernde 
Situation.
Waehend mein C-Programm ein schlichtes
1
enum Befehle
2
{
3
    CMD_READY,
4
    CMD_SYSTEM_RESET,
5
    ...
6
    CMD_END_OF_LIST,
7
};
hat, habe ich beim Python ein Modul, das so aussieht

CMD_READY               =   0
CMD_SYSTEM_RESET        =   1
...

Jede kleinste Aenderung im enum führt dazu, dass ich im Phyton die 
Aermel hochkrempeln muss.
Weiss jemand eine etwas elegantere Lösung?

von Klaus (Gast)


Lesenswert?

Da du vermutlich auf dem PC crosscompilierst: den Headerfile, in dem das 
enum steht, in Python einlesen und auswerten. Dann kann sich das 
Pythonprogramm selbst anpassen

MfG Klaus

von Mehmet K. (mkmk)


Lesenswert?

Klaus, meine Pyhton-Kenntnisse sind eher bescheidener Natur.
Sicher könnte ich mit (für mich) viel Aufwand dies irgendwie hinkriegen; 
aber leider bin ich mit meiner Zeit etwas knapp und deshalb würde ich es 
vorziehen, wie bis anhin auf die Zaehne zu beissen und das Python-Modul 
umzustrukturieren.

von Yalu X. (yalu) (Moderator)


Lesenswert?

In Python 2 habe ich das meist so gemacht:
1
CMD_READY, CMD_SYSTEM_RESET, CMD_DRINK_BEER, CMD_EAT_SNAKES = range(4)
2
3
c = CMD_DRINK_BEER
4
print c
5
6
# Ausgabe: 2

In Python 3.4 gibt es eine Enum-Klasse:

  https://docs.python.org/3/library/enum.html

Damit sieht das obige Beispiel so aus:
1
from enum import Enum
2
3
Cmd = Enum('Cmd', 'READY SYSTEM_RESET DRINK_BEER EAT_SNAKES')
4
5
c = Cmd.DRINK_BEER
6
print(c)
7
8
# Ausgabe: Cmd.DRINK_BEER

Es gibt auch noch andere Möglichkeiten, die Enum-Klasse zu
initialisieren, aber die gezigte ist die kürzeste. Schön an dieser
Klasse ist, dass

- die Enum-Werte als Symbol und nicht nur als Zahl ausgegeben werden und

- sie nicht wie in C zu int kompatibel sind, man also keine
  unbeabsichtigen Typkonvertierungen vornehmen kann.

Für diejenigen, die die Enums trotzdem wie Integers behandeln möchten,
gibt es die Klasse IntEnum.

von neuer PIC Freund (Gast)


Lesenswert?

Hack aus alten tagen:

enum.h:
1
/* XXXPYTHONSNIPXXX */
2
enum Befehle
3
{
4
  CMD1,
5
  CMD2,
6
  CMD3
7
}
8
/* XXXPYTHONSNIPXXX */


enum.py:
1
#!/usr/bin/env python
2
3
enum_snippet = open('enum.h').read().split('XXXPYTHONSNIPXXX')[1]
4
enum_cmds = enum_snippet[enum_snippet.index('{')+1:enum_snippet.index('}')]
5
cmd_seq = enum_cmds.split(',')
6
7
cmd_ctr = 0
8
for cmd in cmd_seq:
9
  cmd = cmd.strip()
10
  if cmd.upper().startswith('CMD'):
11
    new_assign = '{0} = {1}'.format(cmd, cmd_ctr)
12
    exec(new_assign)
13
    cmd_ctr += 1
14
15
print(CMD1)    # test, should be 0
16
print(CMD2)    # 1

Alternativ verwende ich heutzutage bei größeren MCU protobuf von google.

von Mehmet K. (mkmk)


Lesenswert?

@neuer PIC Freund
Zwar verstehe ich die Zeilen so in etwa; aber bis zum Aha-Erlebnis 
müsste ich mich doch sehr intensiv und lange am Kopf kratzen.

@Yalu
Python 3.4 spuckt mir ein paar Fehler mit der seriellen Kommunikation 
aus, weshalb ich z.Zt. bei 2.7 verbleiben möchte.
1
CMD_READY, CMD_SYSTEM_RESET, CMD_DRINK_BEER, CMD_EAT_SNAKES = range(4)
2
3
c = CMD_DRINK_BEER
4
print c
5
6
# Ausgabe: 2
Das war genau das, wonach ich gesucht hatte. Herzlichen Dank!

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ich sehe gerade, dass es gibt einen Backport von Enum auf ältere Pythons
ab 2.4 gibt:

  https://pypi.python.org/pypi/enum34/

Falls du den nicht installieren möchtest, ist hier noch eine sehr
einfache Enum-Klasse, die immerhin die Elemente automatisch nummeriert,
ohne dass man – wie in meinem allerersten Vorsachlag – die Gesamtzahl
der Elemente angeben muss.
1
class MyEnum:
2
  def __init__(self, names):
3
    for value, name in enumerate(names.split()):
4
      # self.__setattr__(name, value) # Korrigiert
5
      setattr(self, name, value)
6
7
Cmd = MyEnum('READY SYSTEM_RESET DRINK_BEER EAT_SNAKES')
8
9
c = Cmd.DRINK_BEER
10
11
print(c)
12
13
# Ausgabe: 2

: Bearbeitet durch Moderator
von Mehmet K. (mkmk)


Lesenswert?

Yalu, bei diesem Bespiel kriege ich die Meldung:
MyEnum instance has no attribute '__setattr__'

Aber wie schon gesagt: Dein erstes Bespiel reicht mir vollkommen.

von Mehmet K. (mkmk)


Lesenswert?

Off topic betreffend CMD_DRINK_BEER.
Seit die AKP in der Türkei an der Macht ist, hat sie die Steuern für 
alkoholische Getraenke um 700% erhöht.
Als letzthin ein paar Freunde aus Deutschland bei mir waren, gab ich 
ihnen mit zitternder Hand je eine Flasche Bier. Und mit zitternder 
Stimme: "Sehr teuer, also langsam trinken"
So erfuhr ich, dass man in Deutschland für den Preis einer Flasche Bier 
made in turkey (2 EUR) ein 6-er Pack bei Aldi bekommt.

von Kaj (Gast)


Lesenswert?

Mehmet K. schrieb:
> Python 3.4 spuckt mir ein paar Fehler mit der seriellen Kommunikation
> aus, weshalb ich z.Zt. bei 2.7 verbleiben möchte.
Und welche fehler wären das?
Ins blaue geraten:
Beim Aufrufen der serial.write() funktion bekommst du einen Fehler 
"TypeError: an integer is required" oder ähnliches?
Ja, da hat sich was geändert zwischen Python 2.x und Python 3.x. Wenn es 
der Fehler ist, das lässt sich sehr leicht beheben, siehe hier-> 
Beitrag "Re: Serielle Kommunikation zwischen Python und µC"

von Yalu X. (yalu) (Moderator)


Lesenswert?

Mehmet K. schrieb:
> Yalu, bei diesem Bespiel kriege ich die Meldung:
> MyEnum instance has no attribute '__setattr__'

Mist, ich habe das aus Versehen nur mit Python 3 getestet. Richtig
muss die Zeile so lauten:
1
      setattr(self, name, value)

Da funktioniert in beiden Pythons. Ich hab's mal oben korrigiert.

> Aber wie schon gesagt: Dein erstes Bespiel reicht mir vollkommen.

Bis auf den kleinen Schönheitsfehler, dass man die Anzahl der Elemente
explizit als Range-Argument hinschreibe muss, gefällt mir die Methode
eigentlich auch ganz gut, weil man dazu weder eine Klassendefinition
nich einen Import braucht.

von Daniel A. (daniel-a)


Lesenswert?

neuer PIC Freund schrieb im Beitrag #4232879:
> /* XXXPYTHONSNIPXXX */
> enum Befehle
> {
>   CMD1,
>   CMD2,
>   CMD3
> }
> /* XXXPYTHONSNIPXXX */

Da fehlt ein Semicolon

von Dennis S. (eltio)


Lesenswert?

Yalu X. schrieb:
> CMD_READY, CMD_SYSTEM_RESET, CMD_DRINK_BEER, CMD_EAT_SNAKES = range(4)

Sehr cool.. :-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

Dennis S. schrieb:
> Yalu X. schrieb:
>> CMD_READY, CMD_SYSTEM_RESET, CMD_DRINK_BEER, CMD_EAT_SNAKES = range(4)
>
> Sehr cool.. :-)

Da hier angewandte coole Feature heißt Pattern-Matching. Schade, dass es
so etwas nicht in allen Programmiersprachen gibt. Das würde sehr viele
inhaltsarme Codezeilen einsparen.

von Dennis S. (eltio)


Lesenswert?

Yepp.. das Feature kenne ich, kam nur nicht darauf es in diesem 
Zusammenhang zu nutzen. Dafür denke ich zu sehr in C. :-D

Gruß
Dennis

von Eric B. (beric)


Lesenswert?

Yalu X. schrieb:
> Da hier angewandte coole Feature heißt Pattern-Matching.

Nö, eher "tupling".
Mit pattern matching (regexp und seine Freunde) hat es nix am Hut.

EDiT: Aber ein cooles Feature ist es trotzdem :-)

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Das ist Pattern-Matching für Strings.
Eric B. schrieb:
> Yalu X. schrieb:
>> Da hier angewandte coole Feature heißt Pattern-Matching.
>
> Mit pattern matching (regexp und seine Freunde) hat es nix am Hut.

Regexes machen Pattern-Matching auf Strings. Es gibt aber auch Pattern-
Matching auf Datenstrukturen, was vor allem in Funktionalsprachen weit
verbreitet ist. Python deckt mit der Zuweisung zu Target-Lists einen,
allerdings auch nur einen Aspekt dieser Form des Pattern-Matching ab.
Insofern ist der Begriff sicherlich etwas zu hoch gegriffen, und ich
nehme ihn hiermit zurück.

> Nö, eher "tupling".

Tupling kenne ich als Programmiertechnik zur Effizienzsteigerung von
rekursiv definierten Funktionen. Auch die meisten Google-Treffer zeigen
in diese Richtung. Und eigentlich ist das, was Python bei Zuweisungen
tut, eher ein "Detupling".

Da auch die Python-Referenz das Kind nicht wirklich beim Namen nennt,
habe ich etwas recherchiert und bin auf den Begriff "Destructuring" bzw.
"Destructuring Assignment" gestoßen, der IMHO den Sachverhalt IMHO ganz
gut trifft, auch wenn in Python das Destructuring auf (möglicherweise
verschachtelte) Iterables (Tuples, Listen usw.) beschränkt ist.

> EDiT: Aber ein cooles Feature ist es trotzdem :-)

Deswegen hätte ich es eben auch gerne in C und C++, egal wie die
dortigen Sprachentwickler es nennen mögen :)

von Daniel A. (daniel-a)


Angehängte Dateien:

Lesenswert?

Yalu X. schrieb:
> Deswegen hätte ich es eben auch gerne in C und C++, egal wie die
> dortigen Sprachentwickler es nennen mögen

Es ist nicht notwendig die Sprachen damit zu Überladen, da dies bereits 
möglich ist.

In C++ könnte das dann so aussehen:
1
  int a, b;
2
  std::string c, d;
3
4
  tupleReferences( a, b, c, d ) = tupleValues( 1, 2, "test", "abc" );
5
  std::cout << a << " " << b << " " << c << " " << d << std::endl;
6
7
  std::vector<int> liste = { 8, 7, 'O', 'K' };
8
  tupleList( a, b, c, d ) = liste;
9
  std::cout << a << " " << b << " " << c << " " << d << std::endl;
10
11
  tupleList( a, b, c, d ) = "test";
12
  std::cout << a << " " << b << " " << c << " " << d << std::endl;

Der vollständige Code ist im Anhang. Getestet mit gcc 4.9.2, compiliert 
mit "g++ -std=c++11 -Wall -Wextra -Werror -pedantic set.cpp -o set"

In C wären mit Macros noch viel extremere Dinge möglich.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Daniel A. schrieb:
> In C++ könnte das dann so aussehen:

Respekt! Das sieht wirklich gut aus.

Ich habe gerade gesehen, dass es in der Standardbibliothek das
function template tie gibt, das in etwa deinem tupleReference
entspricht:

  http://www.cplusplus.com/reference/tuple/tie/

Allerdings lässt sich tie im Gegensatz zu deinem tupleReference
nicht verschachteln, da die Argumente normale non-const references sind,
weswegen kein tie-Tupel übergeben werden kann.

Du hast stattdessen rvalue references genommen, so dass auch
Verschachtelung möglich ist, was natürlich näher an das Python-Vorbild
herankommt. Ebenfalls mit rvalue references arbeitet das function
template forward_as_tuple der Standardbibliothek:

  http://www.cplusplus.com/reference/tuple/forward_as_tuple/

Damit sind Verschachtelungen möglich, allerdings steht da geschrieben:
1
This function is designed to forward arguments, not to store its result
2
in a named variable, since the returned object may contain references to
3
temporary variables.

Da ich mit den neuen C++11/14-Features (noch) nicht so vertraut bin,
frage ich mich, ob das ein Problem ist und wenn ja, ob dieses Problem
auch bei deinen Funktionen auftreten kann. Aus dem Gefühl heraus hätte
ich gesagt, dass kein Problem entsteht, solange als Argumente keine
rvalues übergeben werden, was ja für das unpacking sowieso keinen Sinn
ergäbe. Übergibt man allerdings versehentlich doch einen rvalue, erhält
man keine Fehlermeldung des Compilers, sondern evtl. einen Crash bei der
Ausführung, da Zuweisungen an nicht mehr existente Objekte erfolgen. Man
erkauft sich also mehr Funktionalität (Verschachtelung) durch eine etwas
reduzierte Typsicherheit.

Da du sehr fit in C++ zu sein scheinst, würde mich interessieren, wie du
das siehst.


@Mehmet:

Da dir meine erste Antwort offensichtlich weitergeholfen hat, hoffe ich,
es stört dich nicht allzu sehr, dass dein Thread gerade etwas abdriftet.
Wenn doch, sag Bescheid, dann fange ich einen neuen Thread an. Ansonsten
hat sich das C++-Thema sicher auch schnell wieder erledigt.

von Mehmet K. (mkmk)


Lesenswert?

Yalu X. schrieb:
> Da dir meine erste Antwort offensichtlich weitergeholfen hat, hoffe ich,
> es stört dich nicht allzu sehr, dass dein Thread gerade etwas abdriftet.
> Wenn doch, sag Bescheid, dann fange ich einen neuen Thread an. Ansonsten
> hat sich das C++-Thema sicher auch schnell wieder erledigt.

Um alles in der Welt, natürlich stört mich das nicht. Im Gegenteil!

von Daniel A. (daniel-a)


Angehängte Dateien:

Lesenswert?

Yalu X. schrieb:
> frage ich mich, ob das ein Problem ist und wenn ja, ob dieses Problem
> auch bei deinen Funktionen auftreten kann.

Ja, es ist ein Designproblem, welches bei meiner Variante sogar noch 
vergrößert wird, da ich aus rvalue Referenzen lvalue Referenzen mache 
und diese zurückgebe, ohne std::forward zu verwenden um die 
Typsicherheit sicherzustellen.

> Aus dem Gefühl heraus hätte
> ich gesagt, dass kein Problem entsteht, solange als Argumente keine
> rvalues übergeben werden, was ja für das unpacking sowieso keinen Sinn
> ergäbe. Übergibt man allerdings versehentlich doch einen rvalue, erhält
> man keine Fehlermeldung des Compilers, sondern evtl. einen Crash bei der
> Ausführung, da Zuweisungen an nicht mehr existente Objekte erfolgen.

Das ist soweit alles korrekt.

> Man erkauft sich also mehr Funktionalität (Verschachtelung) durch eine etwas
> reduzierte Typsicherheit.

Ja, schon, aber man kann auch Verschachtelung mit etwas stärkerer 
Typprüfung realisieren, indem man für std::tuple eine Fallunterscheidung 
mittels SFINAE durchführt.

> Da du sehr fit in C++ zu sein scheinst, würde mich interessieren, wie du
> das siehst.

So fit bin ich momentan eigentlich nichtmehr in C++, mein letzter C++ 
Code war vor vielleicht vor einem halben Jahr. Und rvalue referenzen 
korrekt zu nutzen finde ich immer noch schwierig. Mein vorheriges 
beispiel enthielt zwei weitere Designfehler:
 * Bei "constexpr const tupleSetter& operator=( const U&& values )" kann 
dem = opperator keine lvalue zugewiesen werden
 * Die Definitionen von tupleValues können miteinander in Konflikt 
geraten.

Ich habe eine Verbesserte und etwas Erweiterte Version erstellt, aber 
sie ist sicher noch nicht Perfekt.

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

Daniel A. schrieb:
> Bei "constexpr const tupleSetter& operator=( const U&& values )" kann
> dem = opperator keine lvalue zugewiesen werden

Ich meinte natürlich rvalue, sorry.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.