Python != Funktionales Programmieren
--- zumindest scheint es so.
Das hier zum Beispiel:
#!/usr/bin/env python # encoding: utf-8 def counter_generator(): current_value = 0 def counter(): current_value += 1 return current_value return counter import unittest class ClosureTest(unittest.TestCase): def test_counter(self): self.assertEquals(1, counter_generator()()) if __name__ == '__main__': unittest.main()
Wenn man das ausführt dann kriegt man den wenig aussagekräftigen Fehler UnboundLocalError: local variable 'current_value' referenced before assignment. Oder mit anderen Worten: "Wiebidde?"
Kommentiert man current_value += 1 aus, funktioniert das ganze (ok, bis auf den dann fehlschlagenden Testcase).
Immerhin kommt man dem Problem dann auf die Spur (ich und Felix haben das leider erst herausgefunden nachdem wir auf #python nachgefragt haben...) Python kann nämlich seine Scopes nicht vernünftig. Das heißt, man kriegt keinen schreibenden Zugriff auf die Variablen eine Funktion weiter draußen - nur lesend. Nur dass die Fehlermeldung natürlich total mist ist.
Grah.
Interessanterweise geht diese Lösung auch nicht
#!/usr/bin/env python # encoding: utf-8 def counter_generator(): workaround = object() workaround.current_value = 0 def counter(): workaround.current_value += 1 return workaround.current_value return counter import unittest class ClosureTest(unittest.TestCase): def test_counter(self): self.assertEquals(1, counter_generator()()) if __name__ == '__main__': unittest.main()
Für hinweise wieso bin ich dankbar.
Vielleicht kriegt Python dass ja irgendwann mal hin. In der Zwischenzeit hilft dieser Workaround:
#!/usr/bin/env python # encoding: utf-8 class AttributableDict(dict): def __getattr__(self, attribute_name): return self[attribute_name] def __setattr__(self, attribute_name, value): self[attribute_name] = value def counter_generator(): workaround = AttributableDict() workaround.current_value = 0 def counter(): workaround.current_value += 1 return workaround.current_value return counter import unittest class ClosureTest(unittest.TestCase): def test_counter(self): self.assertEquals(1, counter_generator()()) if __name__ == '__main__': unittest.main()