2012-06-22 9 views
9

Spodziewałem się, że ten mały fragment wydrukuje "Dlaczego to nie działa?" Czy ktoś może mi pomóc zrozumieć, dlaczego to nie działa tak jak oczekuję? Używam Pythona 2.6, jeśli to ma znaczenie.Zamknięcie w pythonie + globalna dziwność

class WhyDoesntThisWork(object): 
    def outer(self): 
    acc = '' 
    def inner(msg): 
     global acc 
     acc = acc + msg 
    inner("Why doesn't") 
    inner(" this work?") 
    print acc 
WhyDoesntThisWork().outer() 
  • Gdybym zawierać oświadczenie global dostaję NameError: global name 'acc' is not defined .
  • Jeśli nie dołączę oświadczenia global, otrzymam UnboundLocalError: local variable 'acc' referenced before assignment.
+4

'global' nie działa z zamknięciami. Zamiast tego potrzebujesz 'nonlocal' z Pythona 3. –

+1

Zamiast tego użyj zmiennego (brak globalnego słowa kluczowego); 'acc = []', 'acc.append', itp. –

+0

W porządku, więc używa się zmiennego (' acc = [] '). Czy możesz wyjaśnić, dlaczego to ma znaczenie ... po prostu dla ciekawostek? – sholsapp

Odpowiedz

8

Nie wiem, dlaczego tak wiele komentarzy powyżej zawiera poprawną odpowiedź i nikt nie odważył się napisać odpowiedzi, więc zrobię to tutaj.

class ThisWorksNow(object): 
    def outer(self): 
    acc = [] 
    def inner(msg): 
     acc.append(msg) 
    inner("Why doesn't") 
    inner(" this work?") 
    print "".join(acc) 
ThisWorksNow().outer() 

Jaka jest różnica?

Przypisywanie nazwy do obiektu znajdującego się w zamknięciu nie działa w języku Python 2.x, ponieważ brakuje słowa kluczowego Py3 nonlocal, dlatego musimy znaleźć obejście problemu.

Jeśli musimy zachować stałą wiązania nazwa-obiekt, musimy zmienić coś innego. W tym przypadku jest to obiekt, do którego dodawana jest zawartość do dodania.

Linia print nie jest bardzo elegancka; może bardziej odpowiedni będzie obiekt, który drukuje jego zawartość połączoną.

class StringBuilder(list): # class name stolen from Java 
    def __str__(self): 
     """this makes the object printable in a way which represents the concatenated string""" 
     return "".join(self) 
    @property 
    def string(self): 
     """this gives us a property which represents the concatenated string""" 
     return "".join(self) 
# use whatever suits you better, one or both 

Z tego, co możemy zrobić, aby:

class ThisWorksNow(object): 
    def outer(self): 
    acc = StringBuilder() 
    def inner(msg): 
     acc.append(msg) 
    inner("Why doesn't") 
    inner(" this work?") 
    print acc 
    print acc.string # depending on what you take above 
ThisWorksNow().outer() 

Edit (dołącz): Dlaczego global nie działa?

Możemy to osiągnąć również z global, z 2 wadami.

  1. acc musiałby być globalny na obu miejscach używamy go

    class WhyDoesntThisWork(object): 
        def outer(self): 
        global acc 
        acc = '' 
        def inner(msg): 
         global acc 
         acc = acc + msg 
    

    Niniejszym "lift" oba acc zdarzenia do poziomu "global".

  2. acc może być modyfikowany z zewnątrz.

    Jeśli wykonamy global acc gdzie indziej, lub używamy acc na poziomie modułu, nasz proces może zostać zmodyfikowany. Należy tego unikać.

+0

Proszę wyjaśnić. Zamiast postrzegać, że słowo kluczowe "nielokalne" pozwoli na to, wyjaśnij, co się dzieje, gdy używasz słowa kluczowego "global" w tym miejscu. Czy słowo kluczowe "global" jest ignorowane przez tłumacza, itp. – wberry

+0

Myślę, że widzę. Oznacza to, że słowo kluczowe "global" ma zastosowanie tylko do zmiennych zdefiniowanych na poziomie modułu; i że używanie "global" w zamknięciu nie jest samo w sobie problemem, jak twierdzili niektórzy komentatorzy. – wberry

+0

To nie jest duży problem, ale jest nieczysty, ponieważ zanieczyszcza przestrzeń nazwy poziomu modułu i może prowadzić do kolizji. – glglgl

0

Można utworzyć klasę zamykania pomocnika, jak poniżej, aby wyraźnie oznaczyć zmienną jako zamknięcie. Nie jestem pewien, czy takie narzędzie już istnieje.

class closure: 
    def __init__(self, val = None): 
     self.val = val 
    def __call__(self, val = None): 
     if val: 
     self.val = val 
     return self.val 
class WhyDoesntThisWork(object): 
    def outer(self): 
    acc = closure('') 
    def inner(msg): 
     acc(acc() + msg) 
    inner("Why doesn't") 
    inner(" this work?") 
    print acc 
WhyDoesntThisWork().outer()