Korzystając już z płaskich pakietów, nie spodziewałem się problemu z pakietami zagnieżdżonymi. Tutaj jest…
Układ katalogu
dir
|
+-- test.py
|
+-- package
|
+-- __init__.py
|
+-- subpackage
|
+-- __init__.py
|
+-- module.py
Zawartość init .py
Obie package/__init__.py
i package/subpackage/__init__.py
są puste.
Zadowolony z module.py
# file `package/subpackage/module.py`
attribute1 = "value 1"
attribute2 = "value 2"
attribute3 = "value 3"
# and as many more as you want...
Zawartość test.py
(3 wersje)
Wersja 1
# file test.py
from package.subpackage.module import *
print attribute1 # OK
To zły i niebezpieczny sposób importowania rzeczy (importuj wszystko zbiorczo), ale działa.
Wersja 2
# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
from module import attribute1
Bezpieczniejszy sposób importowania, pozycja po pozycji, ale zawodzi, Python nie chce tego: kończy się niepowodzeniem z komunikatem: „Brak modułu o nazwie moduł”. Jednak …
# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
print module # Surprise here
… Mówi <module 'package.subpackage.module' from '...'>
. Więc to jest moduł, ale to nie jest moduł / -P 8-O ... uh
Wersja 3
# file test.py v3
from package.subpackage.module import attribute1
print attribute1 # OK
Ten działa. Więc albo jesteś zmuszony do używania przedrostka overkill przez cały czas, albo używasz niebezpiecznego sposobu, jak w wersji # 1 i zabroniony przez Python do korzystania z bezpiecznego, wygodnego sposobu? Lepszy sposób, który jest bezpieczny i pozwala uniknąć zbędnych długich przedrostków, jest jedynym, który Python odrzuca? Czy to dlatego, że kocha, import *
czy dlatego, że kocha zbyt długie przedrostki (co nie pomaga w egzekwowaniu tej praktyki)?
Przepraszam za trudne słowa, ale już dwa dni próbuję obejść to głupie zachowanie. O ile gdzieś się nie pomyliłem, będę miał wrażenie, że coś jest naprawdę zepsute w modelu pakietu i pakietów podrzędnych Pythona.
Uwagi
- Nie chcę polegać na
sys.path
globalnych efektach ubocznych ani na*.pth
plikach, które są po prostu kolejnym sposobem na zabawęsys.path
z tymi samymi efektami globalnymi. Aby roztwór był czysty, musi być tylko lokalny. Albo Python jest w stanie obsłużyć podpakiet, albo nie, ale nie powinien wymagać zabawy z konfiguracją globalną, aby móc obsługiwać lokalne rzeczy. - Próbowałem również użyć importu w
package/subpackage/__init__.py
, ale nic nie rozwiązało, robi to samo, a narzekasubpackage
nie jest znanym modułem, podczas gdyprint subpackage
mówi, że to moduł (znowu dziwne zachowanie).
Być może całkowicie się mylę - trudna opcja (wolałbym), ale to sprawia, że czuję się bardzo rozczarowany Pythonem.
Jakikolwiek inny znany sposób poza trzema, których próbowałem? Coś, o czym nie wiem?
(westchnienie)
-----% <----- edytuj ----->% -----
Dotychczasowe wnioski (po komentarzach ludzi)
W Pythonie nie ma nic podobnego do prawdziwego pakietu podrzędnego, ponieważ wszystkie odwołania do pakietów trafiają tylko do globalnego słownika, co oznacza, że nie ma lokalnego słownika, co oznacza, że nie ma możliwości zarządzania odwołaniami do lokalnego pakietu.
Musisz użyć pełnego prefiksu lub krótkiego prefiksu lub aliasu. Jak w:
Pełna wersja prefiksu
from package.subpackage.module import attribute1
# An repeat it again an again
# But after that, you can simply:
use_of (attribute1)
Wersja z krótkim prefiksem (ale z powtarzającym się prefiksem)
from package.subpackage import module
# Short but then you have to do:
use_of (module.attribute1)
# and repeat the prefix at every use place
Albo odmiana powyższego.
from package.subpackage import module as m
use_of (m.attribute1)
# `m` is a shorter prefix, but you could as well
# define a more meaningful name after the context
Wersja faktoryzowana
Jeśli nie masz nic przeciwko zaimportowaniu wielu encji naraz w partii, możesz:
from package.subpackage.module import attribute1, attribute2
# and etc.
Nie w moim pierwszym ulubionym guście (wolę mieć jedno oświadczenie dotyczące importu na importowany podmiot), ale może być tym, które osobiście faworyzuję.
Aktualizacja (2012-09-14):
Wreszcie wydaje się, że w praktyce jest OK, z wyjątkiem komentarza dotyczącego układu. Zamiast powyższego użyłem:
from package.subpackage.module import (
attribute1,
attribute2,
attribute3,
...) # and etc.
__all__
zmienną zawierającą listę nazw, które powinny być eksportowane podczas importu z gwiazdą. edycja: OK, czytając odpowiedź BrenBarna, zrozumiałem, co masz na myśli.Odpowiedzi:
Wydaje się, że nie rozumiesz, w jaki sposób
import
wyszukuje się moduły. Kiedy używasz instrukcji importu, zawsze przeszukuje ona aktualną ścieżkę modułu (i / lubsys.modules
); nie korzysta z obiektów modułów w lokalnej przestrzeni nazw, które istnieją z powodu poprzednich importów. Kiedy robisz:import package.subpackage.module from package.subpackage import module from module import attribute1
Druga linia szuka pakietu wywołanego
package.subpackage
i importuje gomodule
z tego pakietu. Ta linia nie ma wpływu na trzecią linię. Trzecia linia po prostu szuka modułu o nazwiemodule
i go nie znajduje. Nie "ponownie używa" obiektu wywołanegomodule
, który otrzymałeś z powyższej linii.Innymi słowy
from someModule import ...
, nie oznacza „z modułu o nazwie jakiśModuł, który wcześniej zaimportowałem…”, to znaczy „z modułu o nazwie jakiśModuł, który znajdziesz na sys.path…”. Nie ma możliwości „przyrostowego” budowania ścieżki modułu poprzez importowanie pakietów, które do niego prowadzą. Podczas importu zawsze musisz odwołać się do całej nazwy modułu.Nie jest jasne, co próbujesz osiągnąć. Jeśli chcesz zaimportować tylko określony atrybut obiektu1, po prostu zrób to
from package.subpackage.module import attribute1
i skończ z tym. Nigdy nie musisz się martwić o długi czaspackage.subpackage.module
po zaimportowaniu z niego żądanej nazwy.Jeśli nie chcesz mieć dostęp do modułu dostępu do innych nazw później, to można zrobić
from package.subpackage import module
i jak widzieliście można wtedy zrobićmodule.attribute1
i tak dalej jak lubisz.Jeśli chcesz mieć oba - to znaczy, jeśli chcesz mieć
attribute1
bezpośredni dostęp i chcesz, aby byłymodule
dostępne, wykonaj obie powyższe czynności:from package.subpackage import module from package.subpackage.module import attribute1 attribute1 # works module.someOtherAttribute # also works
Jeśli nie lubisz pisać
package.subpackage
nawet dwa razy, możesz po prostu ręcznie utworzyć lokalne odniesienie do atrybutu1:from package.subpackage import module attribute1 = module.attribute1 attribute1 # works module.someOtherAttribute #also works
źródło
module.attribute1
Myślałem o dodaniu na końcu, o używaniu , ale pomyślałem, że byłby sposób na uniknięcie konieczności stosowania prefiksu wszędzie. Więc muszę albo użyć prefiksu w każdym miejscu, albo utworzyć lokalny alias, powtarzając nazwę. Nie taki styl, jakiego się spodziewałem, ale jeśli nie ma sposobu (w końcu jestem przyzwyczajony do Ady, która wymaga czegoś podobnego z deklaracjami zmiany nazwy).Przyczyną niepowodzenia # 2 jest to, że
sys.modules['module']
nie istnieje (procedura importu ma swój własny zakres i nie widzimodule
nazwy lokalnej), amodule
na dysku nie ma modułu ani pakietu. Pamiętaj, że możesz oddzielić wiele importowanych nazw przecinkami.from package.subpackage.module import attribute1, attribute2, attribute3
Również:
from package.subpackage import module print module.attribute1
źródło
sys.modules['name']
którym nie wiedziałem do tej pory, sprawiło, że pomyślałem, że właśnie tego się obawiałem (a BrenBarn potwierdza): w Pythonie nie ma nic lepszego niż prawdziwe pakiety podrzędne.sys.modules
, jak sugeruje jego nazwa, jest globalny i jeśli wszystkie odniesienia do modułów na nim polegają, to nie ma nic lepszego niż lokalne odniesienie do modułu (może pochodzić z Python 3.x?).import
w # 2 generuje odwołanie lokalne dopackage.subpackage.module
powiązaniamodule
.Jeśli wszystko, co próbujesz zrobić, to uzyskać atrybut1 w globalnej przestrzeni nazw, wersja 3 wydaje się w porządku. Dlaczego jest to przedrostek przesady?
W wersji 2 zamiast
from module import attribute1
możesz to zrobić
źródło
attribute1 = module.attribute1
jest po prostu powtarzaniem nazwy bez wartości dodanej. Wiem, że to działa, ale nie podoba mi się ten styl (co nie oznacza, że nie podoba mi się twoja odpowiedź).