Reset von Defaultwerten bei Funktionen möglich?

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
Mauli
User
Beiträge: 10
Registriert: Donnerstag 5. April 2007, 09:03

Hallo,
ich habe einen Shift porgrammiert der mit Daten verschiebt und zwar so, dass die ersten 23 Werte "vergessen" werden.
Quasi so:
y[0] = x[23]

Theoretisch geht das mit scipy.roll(data, x) mit x = Zahl um die ein Array geshiftet werden soll, viel einfacher. Allerdings habe ich immer nur Datenpakete die jeweils 5 Datenpunkte enthalten und schiebe die nacheinander in meinen Shift, der wie folgt aufgebaut ist:

Code: Alles auswählen

def shift(x,n=23,i=[0]):
    y = []
    for k in range(len(x)):
        i[0] += 1
        if i[0] > n:
            y.append(x[k])       
    return y

Das klappt soweit auch ganz gut. Ich möchte ungerne mit globalen Werten arbeiten oder den Zählindex immer wieder über return zurückgeben und dann wieder mit aufrufen. Die Lösung einfach ein mutable Defaultvalue als Zähler zu nutzen (hier: i) finde ich persönlich einfacher. Problematisch wirds, allerdings wenn man das Programm zwei mal hintereinander ausführt (ohne Programmneustart) oder aber den Shift von zwei verschiedenen Stellen im Programm aufruft, dann nämlich wird immer mit dem geänderten i als Defaultvalue weiter gearbeitet. Mit anderen Worten: Der Shift klappt quasi nur beim ersten Aufruf und dann nie wieder, weil anschließend die Bedingung i[0] > n immer erfüllt ist. Dann kann man auch nix mehr "verschieben" :-(

Hat jemand eine Idee wie man das besser machen kann?
Bzw. kann man die Defaultwerte irgendwie wieder "resetten"? Das wäre ja genau das was ich bräuchte.

Das Problem ist, dass ich beim Aufruf der Funktion ja auch nicht

Code: Alles auswählen

values = [1,2,3,4,5]
# for-Schleife simuliert mehrfachen Aufruf:
for j in range(10):
    print shift(values, i=[0])
...das machen kann. Denn dann ist bei jedem Aufruf der Funktion, der Zaehler ja erneut 0, so dass mein i jedesmal nur bis 4 zählen würde und 23 nie erreicht.

Viele Grüße,

Mauli
BlackJack

Funktional programmiert könnte man die Pakete erst einmal "flachklopfen" so dass ein Strom von Elementen entsteht und davon kann man ganz einfach die ersten n Elemente wegwerfen:

Code: Alles auswählen

from itertools import islice


def data_packets(count, length):
    return (range(1, length + 1) for dummy in xrange(count))


def flatten(packets):
    for packet in packets:
        for element in packet:
            yield element


def drop_n(count, iterable):
    iterator = iter(iterable)
    for item in islice(iterator, count):
        pass
    return iterator


def main():
    print list(drop_n(23, flatten(data_packets(10, 5))))
Ansonsten bleibt Dir immer noch der Weg über eine Klasse:

Code: Alles auswählen

class Forgetful(object):
    def __init__(self, count):
        self.count = count
    
    def __call__(self, iterable):
        result = list()
        for item in iterable:
            if self.count:
                self.count -= 1
            else:
                result.append(item)
        return result


def main():
    forget = Forgetful(23)
    values = [1, 2, 3, 4, 5]
    for dummy in xrange(10):
        print forget(values)
Mauli
User
Beiträge: 10
Registriert: Donnerstag 5. April 2007, 09:03

Das erste wird wohl nicht gehen, weil der flachgeklopfte Datenstrom nicht mit der "Echtzeitbedingung" meiner gesamten Applikation vereinbar ist.

Aber die Klasse finde ich klasse ;-). Das sollte klappen, werde ich auf jeden Fall mal ausprobieren.

Vielen Dank.

Gruß,
Mauli
BlackJack

Dir ist klar, dass die funktionale Lösung "lazy" ist, also nicht erst alle Pakete "flachgeklopft" werden und *dann* die ersten Elemente verworfen werden, sondern dass beides "parallel" passiert sowie jeweils genug Daten vorhanden sind!?

Alle drei Funktionen arbeiten mit Generatoren bzw. Iteratoren.
Mauli
User
Beiträge: 10
Registriert: Donnerstag 5. April 2007, 09:03

Um ehrlich zu sein, ist mir das nicht ganz klar :? Du nimmst doch am Anfang mehrere Datenpackete oder nicht? Ich habe zu einem Zeitpunkt nur ein Packet, was durch den shift gehen soll und dann weiterverarbeitet wird.
Aber die Klasse ist meiner Ansicht nachsowie so uebersichtlicher und leichter zu verstehen, deshalb besser fuer mich geeignet. Sofern Du es mir dennoch versuchen moechtest zu erklären, höre ich aber gerne zu.

Ich habs auch mit der Klasse bereits ausprobiert und es funktioniert. Da die Klasse sowie die Instanzierung Teil eines Moduls ist, welches importiert wird, "leben" die Objekte der Klasse aber anschließend noch weiter, habe ich den Eindruck.
Damit bleibt das Problem dass beim mehrfachen Ausführen der Datei ohne Programmneustart, der Shift nur einmal richtig funktioniert. Liegt vermutlich daran, dass das Objekt nicht wirklich zerstört wird, wenn das Programm einmal durchgelaufen ist.

Gruß,
Mauli
BlackJack

Die Datenpakete am Anfang werden erzeugt, aber nicht sofort, sondern erst wenn sie gebraucht werden. Ich habe die Eingabe der Pakete mal auf den Benutzer "geschoben" und ein paar ``print``-Anweisungen eingefügt:

Code: Alles auswählen

from itertools import islice


def data_packets():
    try:
        while True:
            yield input('Input packet: ')
    except KeyboardInterrupt:
        pass


def flatten(packets):
    for packet in packets:
        print 'Flatten new packet...'
        for element in packet:
            yield element


def drop_n(count, iterable):
    iterator = iter(iterable)
    print 'Start dropping...'
    for item in islice(iterator, count):
        print 'Dropped element...'
    print 'Stopped dropping...'
    return iterator


def main():
    print 'Start printing items...'
    for item in drop_n(23, flatten(data_packets())):
        print 'Item:', item
Programmablauf:

Code: Alles auswählen

$ python test.py
Start printing items...
Start dropping...
Input packet: range(1, 6)
Flatten new packet...
Dropped element...
Dropped element...
Dropped element...
Dropped element...
Dropped element...
Input packet: range(1, 6)
Flatten new packet...
Dropped element...
Dropped element...
Dropped element...
Dropped element...
Dropped element...
Input packet: range(1, 6)
Flatten new packet...
Dropped element...
Dropped element...
Dropped element...
Dropped element...
Dropped element...
Input packet: range(1, 6)
Flatten new packet...
Dropped element...
Dropped element...
Dropped element...
Dropped element...
Dropped element...
Input packet: range(1, 6)
Flatten new packet...
Dropped element...
Dropped element...
Dropped element...
Stopped dropping...
Item: 4
Item: 5
Input packet: range(1, 6)
Flatten new packet...
Item: 1
Item: 2
Item: 3
Item: 4
Item: 5
Input packet:
Wie man sieht wird jedes eingegebene Paket *sofort* verarbeitet und sowie 23 Elemente verworfen wurden werden die folgenden Elemente auch sofort ausgegeben. Es wird nie mehr als ein Paket im Speicher gehalten.

Wo sich die Lösung von Deinem Ansatz unterscheidet ist die API für die Pakete. Ich habe eine "pull"-API, dass heisst ich brauche eine Quelle die auf Verlangen das nächste Paket liefert. Diese Quelle kann auch blockieren, weil zum Beispiel auf die Eingabe des Benutzers gewartet wird. Und Du hast eine "push"-API bei der Du die Pakete aktiv in Deine Funktion "hinein schieben" musst.

Der Quelltext auf Modulebene läuft pro Programmablauf immer nur *einmal* beim ersten Importieren ab. Jeder folgende Import bindet einfach nur noch einmal das bereits importierte Modul-Objekt an einen Namen. Du müsstest also immer wenn Du eine neue Instanz brauchst, diese explizit erzeugen.
Mauli
User
Beiträge: 10
Registriert: Donnerstag 5. April 2007, 09:03

Danke fuer die Mühe, der Test macht die recht kompakte Funktionsweise deiner Funktionen schon deutlich verständlicher. Genauso solltes es praktisch funktionieren. Dennoch müsste ich mich etwas mehr mit Generatoren, Iteratoren und itertools beschäftigen um es 100% zu verstehen.

Gruß,
Mauli
Antworten