W przerwie między zastanawianiem się czemu jeszcze nie śpię, a zajadaniem spaghetti z mikrofalówki, trafiłem na najbrzydszego do tej pory quirka w Pythonie. Znalezienie rozwiązania zajęło mi przygnębiająco dużo czasu. Jak również samo rozwiązanie jest przygnębiające - moduł musi importować samego siebie.

Python, jak wszystkim dobrze wiadomo, nie ma czegoś takiego jak zmienna globalna. Słowo kluczowe global co prawda istnieje, ale dla niepoznaki oznacza ono zmienną modułową (prosty odpowiednik our z Perla). Zmienną taką zdefiniowaną w pliku abc.py z innego pliku możemy dorwać jako abc.zmienna.

Problem pojawia się, gdy plik abc.py jest bezpośrednio tym, który uruchamiamy. Jak się okazuje, wewnętrznie Python naszą zmienną zachowuje w --name–.zmienna. Zaś dla pliku uruchomionego --name– ma wartość --main–. “Dzięki” temu gdy importujemy ten plik z innego pliku otrzymujemy dwie oddzielne kopie naszej zmiennej: --main–.zmienna oraz abc.zmienna.

To samo tyczy się klas. Jeśli więc korzystamy z obiektów klasy, lub w jakikolwiek inny sposób modyfikujemy klasę w locie, możemy potknąć się o różnicę między --main–.klasa i abc.klasa.

Rozwiązaniem tego problemu jest taka konstrukcja naszego pliku:

#!/usr/bin/env python
# abc.py

if __name__ = "__main__":
    import abc

abc.zmienna = 42

Niestety triku tego nie da się zastosować do klas.

Komentarze

  • Remigiusz ‘lRem’ Modrzejewski (2010-12-19 01:51:08):

    Przy okazji: nadal czekam na sugestię, jak przekonać Joggera, by nie traktował dwóch podkreśleń jako tagu markdowna. Markdown dla wpisów mam w panelu wyłączony.

  • Szymon (2010-12-19 09:16:48):

    w pythonie są same brzydkie rzeczy ;>

  • 2M1R (2010-12-19 10:26:50):

    @Szymon: Płoń heretyku ;) Programowanie w py to bajka, szczególnie jak się przesiadło z C… Co do zagadnienia zasięgu zmiennych polecam przeglądnąć oficjalny tutorial: http://docs.python.org/py3k/tutorial/classes.html#python-scopes-and-namespaces (Fakt ze akurat podałem linka do py3k nie powinien mieć tu znaczenia ale niech mnie jakiś bardziej doświadczony pythonowiec poprawi)

  • Remigiusz ‘lRem’ Modrzejewski (2010-12-19 10:47:05):

    Rzeczywiście jest o tym wspomniane, ale żeby nie przeoczyć tego jednego zdania trzeba chyba specjalnie tego szukać…

  • Satanowski (2010-12-19 11:40:37):

    #!/usr/bin/env perl

    Zastanawia mnie jak ten perl sobie z tym kodem poradzi ;-)

  • Remigiusz ‘lRem’ Modrzejewski (2010-12-19 13:04:25):

    Jak zawsze, tylko zapomniałem w Inline::Python owinąć ;)

  • Remigiusz ‘lRem’ Modrzejewski (2010-12-19 14:46:01):

    Ech, ludzie mi bocznymi kanałami ciągle powtarzają, że powyższe rozwiązanie jest brzydkie… Kilka innych działających:
    1. Przenieść zmienne globalne do osobnego global.py.
    2. Dodać do konstruktora każdej innej klasy w projekcie referencję do obiektu który chciałem uczynić globalnym (bo “to potrzeba posiadania globalnych zmiennych jest dziwna”).
    3. Oddzielić logikę głównej klasy od programu wykonywalnego. Inaczej mówiąc, stworzyć osobny plik który będzie tym wykonywanym, którego całą zawartością będzie import i uruchomienie głównej klasy.
    W moim programie wybrałem rozwiązanie pierwsze.

    Kilka w sposób oczywisty nie działających:
    1. Odnosić się do zmiennej przez –main–.zmienna.
    2. Zrobić z niej singleton dowolną powszechnie znaną metodą.
    3. Użyć słowa kluczowego global.

  • sprae (2010-12-19 14:53:06):

    Czekam na kolejny art w którym stwierdzisz, że brakuje goto ;-)

  • Remigiusz ‘lRem’ Modrzejewski (2010-12-19 14:59:02):

    Gdyby goto było najlepszym rozwiązaniem czegokolwiek, to bym narzekał też i na jego brak. Mogę tymczasem narzekać, że wychodzenie z zagnieżdżonych pętli w Pythonie rzeczywiście jest nieprzemyślane. Brakuje nazwanych pętli.

  • sprae (2010-12-19 15:05:24):

    Tak na serio chciałem przez to powiedzieć, że tradycyjna idea globalnych zmiennych nieco kłóci się z ideą dynamicznego języka i namespace-ów.
    Wizja “najlepszego” rozwiązania to tylko twoja wizja.

  • Remigiusz ‘lRem’ Modrzejewski (2010-12-19 15:12:44):

    Ale alternatywą do zmiennej globalnej jest dorobienie tony dodatkowego kodu, który na dobrą sprawę nic nie robi, a przy okazji marnuje pamięć i stwarza możliwość błędu. To mi się nieco kłóci z ideą wygodnego programowania. Ale tak, to tylko moje zdanie.

  • sprae (2010-12-19 15:39:19):

    Alternatywą jest dostosować się do platformy, np. sprawdzając jak podobne rozwiązania stworzyli inni, albo zmienić platformę na taką, która pasuje do twoich wymagań. Przecież jest tyle konkurencji.

  • sprae (2010-12-19 15:46:07):

    Zapomniałem o najważniejszym. Dla mnie najlepszym rozwiązaniem byłoby stworzenie osobnego modułu/ów ze zbiorem takich zmiennych i importowanie tam, gdzie są potrzebne.
    To chyba jest najładniejsze z możliwych. Chyba, że jesteś od tych, których krzywdzi zastosowanie każdej kolejnej zmiennej, modułu, zależności.

  • Ravicious (2010-12-19 16:32:07):

    Myślałem, że nie utrudniasz sobie życia i piszesz obiektowy kod, wtedy właściwym rozwiązaniem byłoby rozwiązanie numer

    1. AFAIK na jeden plik powinna przypadać jedna klasa (oczywiście są odstępstwa od tej reguły).

      No chyba, że to jest jakiś bardzo prosty skrypt.

  • Remigiusz ‘lRem’ Modrzejewski (2010-12-19 16:41:09):

    @sprae… Przecież wyraźnie napisałem, że takie właśnie rozwiązanie zastosowałem ostatecznie w swoim programie. A nie zmienię platformy na lepszą z tego trywialnego powodu, że nie wiadomo mi nic o lepszej do tego zastosowania.

    @Ravicious: Piszę obiektowo, jeden plik na klasę. I w tym właśnie układzie wydaje mi się logiczne, że uruchamiany powinien być plik, w którym zaimplementowana jest klasa trzymająca stan głównej pętli programu. Nie powiesz mi, że wymogiem obiektowości jest umieszczanie wywołania głównej pętli w osobnym pliku niż samej pętli.