2012-09-28 17 views
9

Stos przepełnienia stosu ma wiele pytań dotyczących zmiennych globalnych w pythonie i wydaje się powodować pewne zamieszanie dla osób pochodzących z innych języków. Zasady ustalania zakresu nie działają dokładnie tak, jak tego oczekuje wiele osób z innych środowisk.Częstotliwość zmiennych globalnych w python?

Jednocześnie kod ma być zorganizowany nie tyle na poziomie klasy, ale na poziomie modułu. Zatem, gdy wszystko nie musi być zawarte w klasach, stan, który w przeciwnym razie znalazłby się w zmiennych członkowskich może przejść w zmiennych na poziomie modułu.

Więc moje pytanie jest 2 część:

1) Czy mogę być unikanie stosowania globalnych (konkretnie ustawiając je od wewnątrz funkcji i korzystania z globalnego słowa kluczowego)?

2) Jeśli nr 1 jest twierdzący, czy istnieją typowe wzorce, w których należy je stosować?

Pracuję w miejscu, gdzie obfituje wiele różnych języków i chcę złagodzić zamieszanie i upewnić się, że pythonistas nie znienawidzi mnie później.

Dziękuję za wszelkie konstruktywne dane wejściowe.

+0

I nie ma 'nonlocal' w Pythonie 3.x :) –

+0

@Jon Clements: co z tego? – martineau

Odpowiedz

7

Gorąco polecam przeczytać ten wpis na blogu pod tytułem Singletons and their Problems in Python. Zmusiło mnie to do ponownego przemyślenia mojego wykorzystania zmiennych globalnych. Jakiś wybór cytuje:

Ale uwaga. Tylko dlatego, że nie implementujesz wzoru projektu singleton, nie oznacza to, że unikasz podstawowego problemu singletonu. Podstawowym problemem singletonu jest globalny, wspólny stan. Singleton to nic innego jak gloryfikowana zmienna globalna, aw językach takich jak Java istnieje wiele powodów, dla których warto użyć czegoś w stylu singleton. W Pythonie mamy coś innego dla singletonów, i ma bardzo niewinną nazwę, która ukrywa krwawe szczegóły: moduł.

To prawda ludzie: moduł Pythona to singleton. I ma te same problemy z singletonowym wzorem, że jest trochę gorzej.

A oto jeden z przykładów problemów mających taki wspólny stan może powodować:

Aby nie mówić o nieistotnych rzeczach, rzućmy okiem na jeden z modułów ze standardowej biblioteki, moduł MIME.

spojrzeć:

inited = False 

def init(files=None): 
    global inited 
    db = MimeTypes() 
    ... 

To jest rzeczywisty kod z modułu MIMETYPES że statki z Pythonem, tylko z bardziej drastyczne szczegóły usunięte. Chodzi o to, że istnieje wspólny stan. A stanem współużytkowania jest flaga logiczna, która ma wartość True, jeśli moduł został zainicjowany lub False, jeśli nie był. Teraz ten konkretny przypadek prawdopodobnie nie jest aż tak problematyczny (zaufaj mi, to jest), ponieważ inicjują same rodzaje MIME, ale widać, że istnieje parametr plików do funkcji init. Jeśli prześlesz listę plików do tej funkcji, to zostanie ona ponownie zainicjowana w bazie danych mime w pamięci za pomocą informacji mime z tych plików. Teraz wyobraź sobie, co by się stało, gdybyś miał dwie biblioteki inicjujące typy Mimet z dwoma różnymi źródłami ...

To dość powszechny wzór, a sam to zrobiłem ... ale na przykład lepszym sposobem byłoby zrobienie tego : init zwraca instancję klasy, która implementuje wszystkie metody, i inne części kodu mogą init uzyskać inną instancję z różnymi parametrami, które nie zakłócają tego pierwszego. "Minusem" jest to, że musisz przekazać tę instancję każdemu kodowi, który nie chce zainicjować nowego, ale wadą tego "downside" jest to, że pokazuje, jakie są twoje zależności.

Krótko mówiąc, starałbym się go unikać tak bardzo, jak to możliwe, ale jeśli dobrze się czujesz z kodem mającym niejawny singleton, to idź do niego.

+1

Kolejnym poważnym ograniczeniem używania modułów jako instancji pojedynczego że w przeciwieństwie do klasy z metodą '__init __()', która będzie wywoływana i ewentualnie przekazywana argumenty, trudno jest wpływać/kontrolować konstrukcję (pojedynczej) instancji utworzonej na jej początkowym 'imporcie'. – martineau

+0

@ Dziękuję, ta odpowiedź dała mi więcej, niż się spodziewałem :). – MrFox

2

Globale nie są tak naprawdę tabu, po prostu musisz pamiętać, aby zadeklarować je jako globalne w swojej funkcji przed ich użyciem. Moim zdaniem sprawia to, że są bardziej jasne, ponieważ użytkownik widzi, że wyraźnie używasz globalnego.

Byłbym bardziej boją się nie-pythonistów, nie rozumiejących globów Pythona, modyfikujących twój kod i nie dodających właściwych globalnych deklaracji.

3

Krótko mówiąc, powinieneś unikać słowa kluczowego global. Może być w języku z jakiegoś powodu, ale ogólnie uważam, że jest to zapach kodu - jeśli masz jakiś stan, który chcesz nadążyć, zamknij go w klasie. To znacznie mniej delikatne niż używanie global.

+1

Rozumiem, że nigdy nie używam 'global'. Utrzymywanie całego stanu w instancji klasy jest łatwe i jeśli kiedykolwiek będziesz potrzebować stworzyć więcej niż jedną instancję klasy, powinieneś podziękować swoim szczęśliwym gwiazdom, że nie chcesz, aby na siebie nadepnęli. Jeśli stan naprawdę musi być wspólny, a następnie przenieść go na wyższy poziom w hierarchii klas. – Dave

+0

Czasami robię 'foo = [globalvar]', a następnie uzyskuję i ustawiaję 'foo [0]' w ramach funkcji, więc nie muszę używać słowa kluczowego 'global'. to sprawia, że ​​jest bardziej oczywiste, że używam globalnego varibalu, ponieważ muszę go traktować "specjalnie", ale nadal jest dość brzydki, podobnie jak przy użyciu zmiennych globalnych jest – Claudiu

3

Zrobiłem tę uwagę na inne pytanie, więc tutaj są moje 2 centy:

Python jest obiektowe, ale nie trzeba z niego korzystać. Ale jeśli zdecydujesz się, można wykorzystać ten mechanizm klasy posiadać kilka właściwości:

class Config(): 
""" 
hold some vars for example. 
""" 
def __init__(self): 
    self.CONFIG_LOG_FILE_DIR='/tmp2/ozn/venus_mon_log/' 
    self.DATE_FORMAT="%Y%m%d" 
    #self.FILE_NAME_BASE_TEMPLATE=eval('datetime.datetime.now().strftime(self.DATE_FORMAT)')+'-venus_utilization.log' 
    self.FILE_NAME_BASE_TEMPLATE='venus_utilization.log' 
    self.FILE_NAME=self.CONFIG_LOG_FILE_DIR+self.FILE_NAME_BASE_TEMPLATE 
    self.MAX_FILE_SIZE=1024*1024*50 # in Byte, 50 in MB. 

Można by utworzyć instancję, która daje dostęp do właściwości:

cfg = Config() 

A potem do nich dostęp za pomocą :

cfg.MAX_FILE_SIZE 

lub nawet je zmienić:

cfg.MAX_FILE_SIZE=50000 
    cfg.MAX_FILE_SIZE=calculateNewSize() 

i tak dalej ... Można również zrobić rzeczy jak:

# this will print all items that an instance has  
if options.debug: 
    print "DEBUG INFO:" 
    for k,v in vars(cfg).iteritems(): 
     print k,v 
Powiązane problemy