2012-11-01 21 views
10

Wstępnie, myślę, że mogłem wymyślić, jak uzyskać ten kod działa (na podstawie Changing module variables after import), ale moje pytanie jest naprawdę o tym, dlaczego następujące zachowanie występuje, więc mogę zrozumieć, co do nie robić w przyszłości.Importowanie modułów: __main__ vs import jako moduł

Mam trzy pliki. Pierwszym z nich jest mod1.py:

# mod1.py 

import mod2 

var1A = None 

def func1A(): 
    global var1 
    var1 = 'A' 
    mod2.func2() 

def func1B(): 
    global var1 
    print var1 

if __name__ == '__main__': 
    func1A() 

Następny mam mod2.py:

# mod2.py 

import mod1 

def func2(): 
    mod1.func1B() 

Wreszcie mam driver.py:

# driver.py 

import mod1 

if __name__ == '__main__': 
    mod1.func1A() 

Gdybym wykonać polecenie python mod1.py wówczas wyjście to None. W oparciu o link, który wymieniłem powyżej, wydaje się, że istnieje pewne rozróżnienie między importowaniem mod1.py jako __main__ i mod1.py importowanych z mod2.py. Dlatego stworzyłem driver.py. Jeśli wykonam polecenie python driver.py, otrzymam oczekiwane wyjście: A. W pewnym sensie widzę różnicę, ale tak naprawdę nie widzę mechanizmu ani przyczyny. Jak i dlaczego tak się dzieje? Wydaje się sprzeczne z intuicją, że ten sam moduł istniałby dwa razy. Jeśli wykonam python mod1.py, czy będzie możliwe uzyskanie dostępu do zmiennych w wersji __main__ zamiast zmiennych w wersji zaimportowanej przez mod2.py?

+1

Będziesz robił sobie przysługę, jeśli chcesz zrezygnować z okrężnego importu. – eryksun

Odpowiedz

19

Zmienna __name__ zawsze zawiera nazwę modułu, wyjątkiem kiedy plik został załadowany do tłumacza jako skrypt zamiast. Następnie zmienna jest ustawiona na ciąg '__main__' zamiast tego.

W końcu skrypt jest uruchamiany jako główny plik całego programu, wszystko inne to moduły importowane bezpośrednio lub pośrednio przez ten plik główny. Testując zmienną __name__, można w ten sposób wykryć, czy plik został zaimportowany jako moduł, czy został uruchomiony bezpośrednio.

Wewnętrznie moduły otrzymują słownika przestrzeni nazw, który jest przechowywany jako część metadanych dla każdego modułu, w sys.modules. Główny plik, wykonany skrypt, jest przechowywany w tej samej strukturze co '__main__'.

Ale kiedy importujesz plik jako moduł, python najpierw sprawdza w sys.modules, czy ten moduł był już wcześniej zaimportowany. Zatem import mod1 oznacza, że ​​najpierw zaglądamy do modułu sys.modules dla modułu mod1. Utworzy on nową strukturę modułów z przestrzenią nazw, jeśli jeszcze nie ma tam mod1.

Tak więc, jeśli zarówno uruchomić mod1.py jako głównego pliku, i później zaimportować go jako moduł Pythona, będzie to dostać dwa wpisy przestrzeni nazw w sys.modules. Jeden jako '__main__', a następnie jako 'mod1'. Te dwie przestrzenie nazw są całkowicie oddzielne. Twój globalny var1 jest przechowywany w sys.modules['__main__'], ale func1B szuka w sys.modules['mod1'] dla var1, gdzie jest None.

Ale podczas korzystania python driver.py, driver.py staje główny plik programu '__main__' i mod1 zostaną zaimportowane tylko raz w strukturę sys.modules['mod1']. Tym razem func1A przechowuje var1 w strukturze sys.modules['mod1'], a to znajdzie func1B.

+0

Czy to będzie OK? 'jeśli __name__ == '__main__': sys.modules ['mod1'] = sys.modules ['__ main__']; func1A() ' – Kos

+1

@Kos: Nie zrobiłbym tego, myślę, że przekonasz się, że złamie się wiele założeń. Unikaj używania modułów jako skryptów. –

+0

Używam 'if __name__ == '__main __':' do napisania procedury testowej dla każdego z moich modułów w module. Czy istnieje dobry sposób na kontynuowanie tej praktyki, czy też powinienem napisać procedury testowe jako funkcje, a następnie mieć sterownik w osobnym pliku, który nie robi nic oprócz importowania modułu do testowania i wywoływania procedury testowania? – Brendan

1

Odnośnie praktycznego rozwiązania z zastosowaniem jako główny moduł ewentualnie skryptu - Wspieranie Jednorodna-Import:

roztwór 1

Patrz np w module pdb Pythona, w jaki sposób jest prowadzony jako skrypt importując się podczas wykonywania jako __main__ (na końcu):

#! /usr/bin/env python 
"""A Python debugger.""" 
# (See pdb.doc for documentation.) 
import sys 
import linecache 

... 

# When invoked as main program, invoke the debugger on a script 
if __name__ == '__main__': 
    import pdb 
    pdb.main() 

Wystarczy Polecam reorganizacji uruchomienie __main__ na początku skryptu jak poniżej:

#! /usr/bin/env python 
"""A Python debugger.""" 
# When invoked as main program, invoke the debugger on a script 
import sys 
if __name__ == '__main__':   
    ##assert os.path.splitext(os.path.basename(__file__))[0] == 'pdb' 
    import pdb 
    pdb.main() 
    sys.exit(0) 

import linecache 
... 

W ten sposób korpus modułu nie jest wykonywany dwukrotnie - co jest "kosztowne", niepożądane, a czasem krytyczne.

Roztwór 2:

W rzadkich przypadkach, pożądane jest, aby odsłonić rzeczywistą modułu skryptu __main__ nawet bezpośrednio jako moduł rzeczywistego alias (mod1)

# mod1.py  
import mod2 

... 

if __name__ == '__main__': 
    # use main script directly as cross-importable module 
    _mod = sys.modules['mod1'] = sys.modules[__name__] 
    ##_modname = os.path.splitext(os.path.basename(os.path.realpath(__file__)))[0] 
    ##_mod = sys.modules[_modname] = sys.modules[__name__] 
    func1A() 

Znane wady:

  • reload(_mod) kończy się niepowodzeniem
  • Lekcje na pikle wymagają dodatkowej mappi ngs do rozpakowywania (find_global ..)
Powiązane problemy