Sunday, September 25, 2011

Python: Automatische Werte fuer ganzzahlige Konstanten

Manchmal braucht man Konstante, die fortlaufend nummeriert sind. Diese werden dann zur Fallunterscheidung und / oder als Listenindex verwendet oder um oder-verknuepfte Flags zu uebertragen.


BEFORE_READ = 1
AFTER_READ = 2
BEFORE_WRITE = 3
AFTER_WRITE = 4
BEFORE_CREATE = 5
AFTER_CREATE = 6
BEFORE_UNLINK = 7
AFTER_UNLINK = 8
BEFORE_SEARCH = 9
AFTER_SEARCH = 10
BEFORE_WRITE_CREATE = 11
AFTER_WRITE_CREATE = 12

EVENT_FIRST = 1
_NUM_TRIGGER_EVENTS = 10
EVENT_LAST = 12

_FLAG_EXEC_ON_EACH_RECORD = 1
_FLAG_TRANSFORM_MANY2X_VALUES = 2
_FLAG_EXTEND_FIELD_LIST = 4

_trigger_funcs = [False] + [[] for x in range(_NUM_TRIGGER_EVENTS)]


Besonders waehrend der Entwicklung, aendert sich die Reihenfolge oder es werden Elemente entfernt oder hinzugefuegt. Die "Wartung" der Konstanten ist dabei recht laestig. Python hat keinen Datentyp fuer Enumerationen (Java) und auch keine Schluesselworter, um solche zu definieren (C / C++). Jedoch bietet Python mit Generatoren und Coroutienen Werkzeuge, mit denen dieser "Mangel" (nicht boese sein, mir fiel kein anderes Wort ein :-)) behoben werden kann.

Im folgenden Codeausschnitt wird eine Funktion definiert, die im Anschluss sofort als Generator und Coroutine initialisiert wird.


def _SequenceGenerator():
i = 0
while True:
y = (yield i if i else 1)
if y == None:
if not i:
i = 1
else:
i += 1
elif y == 'reset':
i = 0
sequence = _SequenceGenerator()
sequence.send(None)


Sollen nun Konstanten definiert werden, sendet man zunaecht die Zeichenfolge 'reset' an den Generator (Coroutine) und weist dann den Konstante von 1 an aufstegende Werte durch Aufruf der 'next()'-Methode zu. Um einen Wert mehrfach zu verwenden sendet man einen von None (und 'reset') unterschiedlichen Wert.


import tools
tools.sequence.send('reset')

EVENT_FIRST = tools.sequence.send(True)

BEFORE_READ = tools.sequence.next()
AFTER_READ = tools.sequence.next()
BEFORE_WRITE = tools.sequence.next()
AFTER_WRITE = tools.sequence.next()
BEFORE_CREATE = tools.sequence.next()
AFTER_CREATE = tools.sequence.next()
BEFORE_UNLINK = tools.sequence.next()
AFTER_UNLINK = tools.sequence.next()
BEFORE_SEARCH = tools.sequence.next()
AFTER_SEARCH = tools.sequence.next()

_NUM_TRIGGER_EVENTS = tools.sequence.send(True)

BEFORE_WRITE_CREATE = tools.sequence.next()
AFTER_WRITE_CREATE = tools.sequence.next()

EVENT_LAST = tools.sequence.send(True)

tools.sequence.send('reset')

_FLAG_EXEC_ON_EACH_RECORD = 1 << (tools.sequence.next() - 1)
_FLAG_TRANSFORM_MANY2X_VALUES = 1 << (tools.sequence.next() - 1)
_FLAG_EXTEND_FIELD_LIST = 1 << (tools.sequence.next() - 1)


Der Generator-Code kann in ein globales 'tools'-Modul ausgelagert und bei Bedarf importiert und so oft aufgerufen werden wie noetig.

Diese Methode ist nicht Thread-sicher.

0 comments:

Post a Comment