2012-01-02 11 views
15

W poniższej hierarchii znajduje się wygodny i uniwersalny sposób odwoływania się do top_package przy użyciu ogólnego terminu we wszystkich plikach .py poniżej? Chciałbym mieć spójny sposób importowania innych modułów, aby nawet gdy nazwa "top_package" zmieniła się, nic nie pęka.Jak odwołać się do modułu najwyższego poziomu w Pythonie wewnątrz paczki?

Nie jestem zwolennikiem użycia względnego importu takiego jak "..level_one_a", ponieważ ścieżka względna różni się od każdego z plików Pythona poniżej. Szukam sposobu, aby:

  1. Każdy plik python może mieć taką samą instrukcję import dla tego samego modułu w pakiecie.
  2. Oddzielenie odniesienia do "top_package" w dowolnym pliku .py wewnątrz paczki, więc jakakolwiek nazwa "top_package" zmieni się, nic nie zepsuje.

    top_package/ 
        __init__.py 
        level_one_a/ 
        __init__.py 
        my_lib.py 
        level_two/ 
         __init__.py 
         hello_world.py 
        level_one_b/ 
        __init__.py 
        my_lib.py 
        main.py 
    
+1

Dlaczego chcesz odniesienie do modułu najwyższego poziomu? (Czasami ta odpowiedź ujawnia prawdziwy problem/rozwiązanie). –

+1

Chcę, aby pakiet był jak najbardziej możliwy do ponownego użycia, więc kiedy zmieni nazwę górnego modułu, wszystko może nadal działać normalnie, bez konieczności zmiany nazwy importu wewnątrz. – hllau

Odpowiedz

10

To powinno załatwić sprawę:

top_package = __import__(__name__.split('.')[0]) 

Sztuką jest to, że dla każdego modułu zmienna __name__ zawiera pełną ścieżkę do modułu oddzielone kropkami, takich jak, na przykład, top_package.level_one_a.my_lib. Dlatego jeśli chcesz uzyskać nazwę najwyższego pakietu, wystarczy pobrać pierwszy komponent ścieżki i zaimportować go przy użyciu __import__.

Pomimo nazwy zmiennej używanej do uzyskania dostępu do pakietu nadal nazywa się top_package, można zmienić nazwę pakietu i jeśli nadal będzie działać.

+1

-1 Pytanie wymagało rozwiązania, które nie zależy od nazwy "top_package". –

+0

@ Jon-Eric Dzięki za komentarz. Poprawiłem moją odpowiedź na pracę bez względu na nazwę najwyższego pakietu. – jcollado

+1

To nie jest w pełni poprawne; jeśli aktualny moduł nie zostanie zaimportowany z najwyższego poziomu, jego nazwa "__name__" nie będzie zawierała pełnej ścieżki do tego modułu, a jedynie jego część, która rozpocznie się od miejsca, w którym importujesz ten moduł. – thuzhf

0

Uważam, że # 2 jest niemożliwe bez użycia importu względnego lub wymienionego pakietu. Musisz określić, który moduł chcesz zaimportować, jawnie wywołując jego nazwę lub używając importu względnego. inaczej w jaki sposób tłumacz wiedziałby, czego chcesz?

Jeśli dokonać uruchamiania aplikacji o jeden poziom wyżej top_level/ i to import top_leve l można następnie odwołać top_level.* z dowolnego miejsca wewnątrz opakowania top_level.

(mogę pokazać wam przykład z oprogramowania pracuję nad: http://github.com/toddself/beerlog/)

1

Można użyć kombinacji funkcji __import__() i atrybut pakietu __path__.

Załóżmy na przykład, że chcesz zaimportować <whatever>.level_one_a.level_two.hello_world z innego miejsca w pakiecie. Możesz zrobić coś takiego:

import os 
_temp = __import__(__path__[0].split(os.sep)[0] + ".level_one_a.level_two.hello_world") 
my_hello_world = _temp.level_one_a.level_two.hello_world 

Ten kod jest niezależny od nazwy pakietu najwyższego poziomu i może być używany w dowolnym miejscu w pakiecie. Jest też dość brzydka.

5

Połóż opakowanie i skrypt main do katalogu zewnętrznego pojemnika, tak:

container/ 
    main.py 
    top_package/ 
     __init__.py 
     level_one_a/ 
      __init__.py 
      my_lib.py 
      level_two/ 
       __init__.py 
       hello_world.py 
     level_one_b/ 
      __init__.py 
      my_lib.py 

Kiedy main.py uruchomieniu jego katalog nadrzędny (container) zostanie automatycznie dodany do początku sys.path. A ponieważ top_package jest teraz w tym samym katalogu, można go importować z dowolnego miejsca w drzewie pakietów.

Więc hello_world.py mogła importować level_one_b/my_lib.py takiego:

from top_package.level_one_b import my_lib 

Bez względu na nazwę katalogu pojemnika jest lub gdzie się znajduje, import będzie zawsze działać w tym układzie.

Należy jednak zauważyć, że w swoim oryginalnym przykładzie, top_package może on z łatwością funkcjonować jako sam katalog kontenerów. Wszystko, co musisz zrobić, to usunąć top_package/__init__.py, a zostaniesz z efektywnym tym samym ustawieniem.

poprzedniej instrukcji import będzie wtedy zmienić na:

from level_one_b import my_lib 

i może być wolne, aby zmienić nazwę top_package jednak chciałeś.

+0

Jeśli usuniesz pakiet top_package/__ init__.py, to level_one_a i level_one_b nie będą mogły być importowane od siebie nawzajem. – DonGar

+0

@DonGar. Nie, to działa doskonale. Zauważcie jednak, że miałem na myśli strukturę w pytaniu OP, a nie tę przedstawioną w mojej odpowiedzi. Krytyczną różnicą jest lokalizacja skryptu 'main.py'. – ekhumoro

+0

Główna część twojej odpowiedzi Zgadzam się, ale akapit, który zaczyna się od "Ale zauważ, że" jest tym, z czym się nie zgadzam. Chyba że masz na myśli również przeniesienie zarówno level_one_a, jak i level_one_b do nowego submodułu top_container. – DonGar

0

ta działa od wewnątrz modułu Library:

import __main__ as main_package 
TOP_PACKAGE = main_package.__package__.split('.')[0]