Seite 1 von 1

Scope von localen Funktionen

Verfasst: Freitag 17. Februar 2012, 00:42
von fonzy
Weiß jemand von Euch wie sich der Scope von lokal definierten Funktionen aufbaut?
Folgender Code wirft eine Exception:

Code: Alles auswählen

def outer():
    a=5
    def inner():
        a+=1
    inner()
>>> outer()
UnboundLocalError: local variable 'a' referenced before assignment
Wenn ich jedoch den Integer in einer Liste verstecke gehts?!?

Code: Alles auswählen

def outer():
    a=[5]
    def inner():
        a[0]+=1
    inner()
Warum kennt die lokale Funktion 'a' nicht wenn es ein Integer ist sondern nur wenn es ein Objekt ist?

Könnt ihr mir das erklären? Was mach ich im ersten Beispiel falsch? Ist CodeSchnipsel 2 tatsächlich die Lösung?

Danke

Re: Scope von localen Funktionen

Verfasst: Freitag 17. Februar 2012, 00:44
von deets
Das liegt daran, dass du im ersten Beispiel einen Namen an ein Objekt bindest - und das ist das einzige, was Python als Hinweis darauf hat dass du eine Variable deklarierst.

Im zweiten Beispiel modifiezierst du ein mutable Objekt durch einen existierenden Namen. Darum beschwert Python sich da nicht.

In Python 3 gibt es fuer diesen Fall das nonlocal-Keyword.

Re: Scope von localen Funktionen

Verfasst: Freitag 17. Februar 2012, 11:12
von lunar
@fonzy: Siehe Naming and Binding in der Sprachreferenz.

Tatsächlich ist das zweite Beispiel die Lösung, wenn man den übergeordneten lokalen Namensraum verändern möchte. Um die Lesbarkeit zu verbessern, würde ich allerdings eher ein Wörterbuch oder ein Dummy-Objekt nutzen:

Code: Alles auswählen

def outer():
    ns = {'a': 5}
    def inner():
        ns['a'] += 1
    inner()

def outer():
    ns = object()
    ns.a = 5
    def inner():
        ns.a += 1
    inner()

Re: Scope von localen Funktionen

Verfasst: Freitag 17. Februar 2012, 12:16
von deets
@lunar

Das hast du aber nicht wirklich ausprobiert, oder ;)

Code: Alles auswählen

for nice experiences hit <tab> multiple times
>>> f = object()
>>> f.foo = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'foo'
>>> 
Ich wuenschte auch es gaebe da eine so simple loesung, um sich das gefummele mit dict/anfuehrungsstrichen und so zu sparen.

Re: Scope von localen Funktionen

Verfasst: Freitag 17. Februar 2012, 12:56
von nomnom
Na ja, zwölf Zeilen Code für so eine Möglichkeit …

Code: Alles auswählen

class Mutable(object):

    def __init__(self):
        self.variables = {}

    def __setattr__(self, name, value):
        self.__dict__[name] = value

    def __getattr__(self, name):
        if name not in self.__dict__:
            raise AttributeError
        return self.__dict__[name]
Wenn man es unbedingt haben will, ist das ja nicht unbedingt zu viel verlangt.

Re: Scope von localen Funktionen

Verfasst: Freitag 17. Februar 2012, 13:07
von lunar
@deets: Stimmt, das habe ich nicht. Ich bin überrascht, dass sich die Semantik von "object()" so sehr unterscheidet von Klassen, die man von "object" ableitet.

@nomnom: Es geht wesentlich kürzer:

Code: Alles auswählen

In [5]: ns = type('ns', (object,), {})()

In [6]: ns.foo = 10

Re: Scope von localen Funktionen

Verfasst: Freitag 17. Februar 2012, 13:10
von deets
@lunar

das liegt wohl daran, dass object auch superklasse von C-typen sein soll ala int, die ja auch kein instance-dict kennen.

Wir haben hier ein util-Pakte, in dem uA ein Bunch vorhanden ist - der eignet sich natuerlich hervorragend fuer sowas. Aber auch das ist schon eine (kleine) Abhaengigkeit.

Re: Scope von localen Funktionen

Verfasst: Freitag 17. Februar 2012, 13:17
von lunar
@deets: Stimmt, daran hatte ich nicht gedacht.

Btw, nette Signatur ;)

Re: Scope von localen Funktionen

Verfasst: Freitag 17. Februar 2012, 15:25
von fonzy
Vielen Dank für Eure Hilfe :)

Aber bevor ich mir eine Iterable-Klasse für sowas mache scheint mir doch ein Integer-Objekt mit entsprechend überladenen Operatoren fast sauberer zu sein. Aber dann haben wir ja das gleiche Problem wie bei Java, wo ich ein Integer in ein IntegerObjekt umwandeln muss, um es per Referenz an eine Methode zu übergeben...

Re: Scope von localen Funktionen

Verfasst: Freitag 17. Februar 2012, 15:29
von problembär
fonzy hat geschrieben:Aber bevor ich mir eine Iterable-Klasse für sowas mache scheint mir doch ein Integer-Objekt mit entsprechend überladenen Operatoren fast sauberer zu sein. Aber dann haben wir ja das gleiche Problem wie bei Java, wo ich ein Integer in ein IntegerObjekt umwandeln muss, um es per Referenz an eine Methode zu übergeben...
Ich glaube, sowas wird man nie machen müssen, nur um ein paar Daten zu verarbeiten. Alles viel zu kompliziert. Wahrscheinlich ist es besser, einfacher zu denken.

Re: Scope von localen Funktionen

Verfasst: Freitag 17. Februar 2012, 15:39
von deets
@fonzy

Wieso eine iterable Klasse? Das hat doch damit nix zu tun. Alles was du brauchst ist ein mutierbares Objekt. Klar, das ist ein Workaround, aber bis Py3 ist's halt notwendig.

Re: Scope von localen Funktionen

Verfasst: Freitag 17. Februar 2012, 15:49
von lunar
@fonzy: Worauf willst Du mit "iterable Klasse" hinaus? Um dieses Problem zu lösen, brauchst Du keine eigene Klasse, und erst recht keine, die einen iterierbaren Datentyp definiert.

Deine Idee, einfach von "int" abzuleiten, und die Operatoren zu überladen, funktioniert ohnehin nicht, da +=, -= und Konsorten den Namen auf der linken Seite immer im aktuellen Namensraum neu binden, unabhängig davon, ob der Typ des rechten Operanden den Operator überlädt oder nicht:

Code: Alles auswählen

In [1]: class Foo(object):
   ...:     def __iadd__(self, value):
   ...:         print(value)
   ...:         return self
   ...: 

In [2]: f = Foo()

In [3]: f += 10
10

In [4]: def foo():
   ...:     f += 10
   ...: 

In [5]: foo()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
/home/swiesner/<ipython-input-5-624891b0d01a> in <module>()
----> 1 foo()

/home/swiesner/<ipython-input-4-5ab5d0e3f155> in foo()
      1 def foo():
----> 2     f += 10
      3 

UnboundLocalError: local variable 'f' referenced before assignment
Anders darf sich Python an dieser Stelle gar nicht verhalten, da eine "__iadd__()"-Implementierung prinzipiell beliebige Objekte zurückgeben kann, so dass der Name zwangsläufig neu gebunden werden muss, um eine korrekte Semantik zu erhalten.

Re: Scope von localen Funktionen

Verfasst: Freitag 17. Februar 2012, 17:11
von fonzy
Sorry, mein fehler. Ich hab mir eingebildet, nomnom hätte in seinem Vorschlag

__getitem__ und __setitem__

implementiert. Ne iterables brauchen wir wahrlich nicht...

Re: Scope von localen Funktionen

Verfasst: Freitag 17. Februar 2012, 21:02
von nomnom
nomnom hat geschrieben:Noch kürzer:

Code: Alles auswählen

ns = lambda: Null
Wie komm ich denn auf Null? Ich meine natürlich None …

Re: Scope von localen Funktionen

Verfasst: Sonntag 19. Februar 2012, 09:50
von mutetella
Guten morgen...

Ich hoffe mal, dass das jetzt nicht völlig am Thema vorbeigeht... ;-) Aber ist es nicht grundsätzlich sinnvoller, die getrennten Namensräume zu berücksichtigen, anstatt zu versuchen sie zu überbrücken?

Code: Alles auswählen

def outer(a):
    def inner(a):
        a += 1
        return a
    return inner(a)
Welches Szenario könnte es denn geben, dass ich über ein Dummy-Objekt auf den oberen Namensraum zugreifen muss?

mutetella

Re: Scope von localen Funktionen

Verfasst: Sonntag 19. Februar 2012, 10:11
von BlackJack
@mutetella: Ganz allgemein ist das in Closures üblich.