Probleme mit anonymen Funktionen

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
Daishy
User
Beiträge: 21
Registriert: Samstag 4. April 2009, 12:40
Kontaktdaten:

Hi zusammen,

Ich verbringe mittlerweile einen Grossteil meines Tages damit einen Fehler zu verstehen, ich werd aber irgendwie nicht schlauer, deswegen wollte ich hier mal Fragen. Eins vorweg: Ich bin mir selber nicht ganz sicher wie der Fehler auftritt oder warum, deswegen bitte Nachsicht, wenn es etwas konfus klingt.

Ich habe eine Funktion rev die entweder einen String oder eine Funktion zurueckgeben soll, je nachdem welche Parameter gesetzt sind (Ist zum aufloesen von URLs. Sind dynamische Parameter vorhanden, so muss erst ein weiteres Objekt/Dict geliefert werden)

Code: Alles auswählen

def rev(url=None, stat=dict(), dyn=dict(), obj=None):
    if obj == None and len(dyn) > 0:
        def tmp(obj):
            return rev(url, stat, dyn, obj)
        return tmp
        
    else:
        # Static-array in dict umwandeln
        if isinstance(dyn, list):
            dynamic = dict([(x,x) for x in dyn])
        else:
            dynamic = dyn
        
        # dyn aufloesen und mit static mergen
        for field_name, param_name in dynamic.items():
            stat[param_name] = get_field_value(obj, field_name)
        
        # URL aufloesen
        return reverse(url, kwargs=stat) 
Diese Funktion wird im Kontext einer Webanwendung (Django) benutzt um urls aufzuloesen und die reverse etwas zu ergaenzen.
WEnn ich diesen Webserver jetzt frisch starte und die Startseite aufrufe, dann werden unter anderem ein paar Klassendefinitionen gelesen und in diesem Rahmen ein rev('edit', dyn=['id']) aufgerufen, es wird also eine Funktion (1) zurueckgegeben. Danach wird rev('create') aufgerufen und es wird korrekte url zurueckgegeben. Anschliessend wird durch eine Ajax-Funktion beim Server ein Ladevorgang ausgeloest, der unter anderem die Funktion (1) mit einem Objekt gefuettert (also die Anonyme Funktion tmp aufgerufen).
Soweit auch alles gut. Lade ich die Startseite jetzt jedoch neu, dann wird wieder rev('create') aufgerufen und jetzt passiert das, was ich mir nicht erklaeren kann. Innerhalb von rev('create') hat stat schon zu funktionsbeginn den Wert {'id':Blub}, also das, was in (1) dynamisch zu dem stat-dict hinzugefuegt wurde.
Also vielleicht nochmal in kurz:

Code: Alles auswählen

rev('edit', dyn=['id']) 
# wird beim Import des Moduls aufgerufen
# zurueck kommt eine Referenz auf tmp

rev('create')
# wird beim aufrufen der Startseite geladen
# dyn, stat und obj sind alle leer, es wird der korrekte String geliefert

tmp(dict(...)) -> rev('edit', dyn=['id'], dict(...)
# gelieferte Funktion aus Schritt eins wird aufgerufen und ruft rev mit den Parametern auf.
# in Rev wird z.B. stat['id'] = 4 hinzugefuegt

rev('create')
# Seite wird neu geladen, rev wieder aufgerufen.
# SChon beim FUnktionseintritt hat stat jetzt den Inhalt {'id':4}

Vielleicht habe ich heute einfach schon zu lange da draufgeguckt, aber ich werd nicht draus schlau. Deswegen hoffe ich jetzt, dass einer von euch damit was anfangen kann und ich nur wieder zu dumm war und was uebersehen hab. Vielen lieben Dank schonmal jetzt :D
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Mal zur Info, ohne deinen Post genau gelesen zu haben:

Code: Alles auswählen

>>> from random import random
>>> def f(a=dict()):
...     a[random()] = None
...     return a
...
>>> f()
{0.75671095172248226: None}
>>> f()
{0.9880592343772292: None, 0.75671095172248226: None}
>>> f()
{0.9880592343772292: None, 0.75671095172248226: None, 0.30537352939112716: None}
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
Daishy
User
Beiträge: 21
Registriert: Samstag 4. April 2009, 12:40
Kontaktdaten:

Das erklaert einiges (Unter anderem das Verhalten).

Analog zu dem Post (Habe aus letzter Verzweiflung gerade auch mal was in die Richtung probiert) habe ich es mit einem deepcopy probiert und nicht mehr direkt auf stat gearbeitet:

Code: Alles auswählen

from copy import deepcopy
mix = deepcopy(stat)
Und siehe da, alles wie es soll. Interessant faende ich trotzdem noch, warum a nicht bei jedem Funktionsaufruf neu belegt wird sondern scheinbar immer das alte benutzt wird. Kann das zufaellig jemand erklaeren? (Oder hat einen Link fuer mich, wuesste nicht so ganz wonach ich da googlen soll).

Danke :)
BlackJack

@Daishy: Da gibt's nicht viel zu erklären: Der Ausdruck bei Default-Argumenten wird *einmal* ausgewertet, wenn das ``def`` der Funktion ausgeführt wird. Und nicht jedesmal beim Aufruf der Funktion. Das steht in der Dokumentation bei Funktionen.
Daishy
User
Beiträge: 21
Registriert: Samstag 4. April 2009, 12:40
Kontaktdaten:

Oi, in der Tat, danke :D
Da muss ich aber ehrlich sagen, da waere ich so nicht drauf gekommen, also an der Stelle zu gucken, zumindest nicht bevor ich die Loesung zu dem Problem kannte. Die Dokumentation zu den Basissachen guckt man sich ja doch eher selten an, gerade wenn man anhand von Tutorials oder aehlichem sich die Sprache aneignet :D

Nunja, das naechste mal denk ich vorher an sowas ^^

Gruesse,
Daishy
Antworten