2009-09-22 12 views
7

W dużej aplikacji pracuję, kilka osób importuje różne moduły w inny sposób, np. importu x lub z y import x skutków ubocznych to x jest importowany dwukrotnie i mogą wprowadzać błędy bardzo subtelne, czy dana osoba jest poleganie na globalnych parametrówmoduł został ponownie zaimportowany, jeśli został zaimportowany z innej ścieżki.

np Załóżmy, że mam mypakcage pakiet z trzech plików mymodule.py, main.py i init, .py

mymodule.py zawartość

l = [] 
class A(object): pass 

zawartość main.py

def add(x): 
    from mypackage import mymodule 
    mymodule.l.append(x) 
    print "updated list",mymodule.l 

def get(): 
    import mymodule 
    return mymodule.l 

add(1) 
print "lets check",get() 

add(1) 
print "lets check again",get() 

drukuje

updated list [1] 
lets check [] 
updated list [1, 1] 
lets check again [] 

ponieważ teraz są dwie listy w dwóch różnych modułach, podobnie klasa A jest inna Dla mnie wygląda wystarczająco poważnie, ponieważ same klasy będą traktowane inaczej np. poniżej wydruków kodów Fałszywe

def create(): 
    from mypackage import mymodule 
    return mymodule.A() 

def check(a): 
    import mymodule 
    return isinstance(a, mymodule.A) 

print check(create()) 

Pytanie:

Czy istnieje jakiś sposób, aby tego uniknąć? z wyjątkiem egzekwowania tego modułu powinien być importowany w jedną stronę onyl. Nie można tego obsłużyć przez mechanizm importowania Pythona, widziałem kilka błędów związanych z tym w kodzie django i gdzie indziej.

Odpowiedz

3

Mogę replikować tylko wtedy, gdy plik main.py jest faktycznie uruchomionym plikiem. W takim przypadku otrzymasz bieżący katalog main.py na ścieżce sys. Ale najwyraźniej masz również ustawioną ścieżkę systemową, aby można zaimportować mypackage.

Python w tej sytuacji nie zda sobie sprawy, że mymodule i mypackage.mymodule są tym samym modułem, a otrzymasz ten efekt. Zmiana ta ilustruje to:

def add(x): 
    from mypackage import mymodule 
    print "mypackage.mymodule path", mymodule 
    mymodule.l.append(x) 
    print "updated list",mymodule.l 

def get(): 
    import mymodule 
    print "mymodule path", mymodule 
    return mymodule.l 

add(1) 
print "lets check",get() 

add(1) 
print "lets check again",get() 


$ export PYTHONPATH=. 
$ python mypackage/main.py 

mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'> 
mymodule path <module 'mymodule' from '/tmp/mypackage/mymodule.pyc'> 

Ale dodać kolejny mainfile w katalogu currect:

realmain.py: 
from mypackage import main 

a wynik jest inny:

mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'> 
mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'> 

Więc podejrzewam, że masz swoją główną plik python w pakiecie. W takim wypadku rozwiązaniem jest nie robić tego. :-)

+0

hmmm faktycznie, że nie jest to przypadek, to widzę, jak powtórzyć go main.py opakowaniu zewnętrznym –

+0

no masz rację :) –

3

Każda przestrzeń nazw modułu jest importowana tylko raz. Problem polega na tym, że importujesz je inaczej. Na pierwszym importujesz z pakietu globalnego, a na drugim robisz lokalny, niepakowany import. Python widzi moduły jako różne. Pierwszy import jest buforowany wewnętrznie jako mypackage.mymodule, a drugi jako mymodule.

Sposób rozwiązania tego problemu polega na tym, że zawsze używają bezwzględnego importu.Oznacza to, że zawsze daje moduł ścieżki bezwzględne importu z opakowania najwyższego poziomu roku:

def add(x): 
    from mypackage import mymodule 
    mymodule.l.append(x) 
    print "updated list",mymodule.l 

def get(): 
    from mypackage import mymodule 
    return mymodule.l 

Pamiętaj, że punkt wejścia (plik uruchomić, main.py) również powinny być poza pakiet. Jeśli chcesz, aby kod punktu wejścia znajdował się w pakiecie, zwykle używaj zamiast niego małego skryptu. Przykład:

runme.py poza pakietem:

from mypackage.main import main 
main() 

I main.py dodać:

def main(): 
    # your code 

znajdę this document JP Calderone'a być wielki wskazówka, jak (nie) struktury Twój projekt Pythona. Po nim nie będziesz mieć problemów. Zwróć uwagę na folder bin - znajduje się poza pakietem. Będę odtworzyć cały tekst tutaj:

Plików strukturę projektu Pythona

Czy:

  • nazwą katalogu coś związane z projektem. Na przykład: , jeśli projekt nosi nazwę "Skręcona", nazwa katalogu najwyższego poziomu dla jego plików źródłowych Twisted. Po wykonaniu wersji należy dołączyć sufiks numeru wersji : Twisted-2.5.
  • utworzyć katalog Twisted/bin i umieścić pliki wykonywalne tam, jeśli masz żadnych. Nie podawaj im rozszerzenia, nawet jeśli są to pliki źródłowe Python . Nie umieszczaj w nich żadnych kodów, z wyjątkiem importowania i wywoływania funkcji głównej zdefiniowanej gdzie indziej w swoich projektach jako .
  • Jeśli Twój projekt można wyrazić jako pojedynczy plik źródłowy Python , umieść go w katalogu i nadaj mu nazwę związaną z projektem. Na przykład: Twisted/twisted.py. Jeśli potrzebujesz wielu plików źródłowych, utwórz pakiet (Twisted/twisted/, z pustym Twisted/twisted/__init__.py) i umieść w nim swoje pliki źródłowe. Dla przykładu , Twisted/twisted/internet.py.
  • umieścić swoje testy jednostkowe w sub-pakietu pakietu (uwaga - to znaczy, że opcja plik pojedyncze źródło Python powyżej był podstęp - zawsze trzeba w najmniej jednego innego pliku dla jednostki testów). Na przykład: Twisted/twisted/test/. Oczywiście, sprawiają, że jest to pakiet z Twisted/twisted/test/__init__.py. Umieść testy w plikach takich jak Twisted/twisted/test/test_internet.py.
  • dodaj Twisted/README i T wisted/setup.py, aby wyjaśnić i zainstalować oprogramowanie, jeśli czujesz się dobrze.

Nie:

  • umieścić swoje źródło w katalogu zwanego src lub lib. To sprawia, że ​​trudno jest go uruchomić bez instalacji.
  • Połóż swoje testy poza pakietem Python . To sprawia, że ​​testy są trudne w stosunku do zainstalowanej wersji.
  • utwórz pakiet, który ma tylko __init__.py, a następnie umieść cały kod w __init__.py. Po prostu zbuduj moduł zamiast paczki, to jest łatwiejsze, ponieważ jest to .
  • spróbować wymyślić magicznych sztuczek, aby Python stanie zaimportować moduł lub pakiet bez konieczności użytkownik dodać katalog zawierający go swojej drodze importu (albo poprzez PYTHONPATH lub innego mechanizm). Nie będziesz prawidłowo obsługiwać wszystkich przypadków, a użytkownicy otrzymają od Ciebie zła, gdy Twoje oprogramowanie nie działa w ich środowisku.
Powiązane problemy