Chcę dziedziczyć po klasie w pliku znajdującym się w katalogu powyżej bieżącego.
Czy można względnie zaimportować ten plik?
Chcę dziedziczyć po klasie w pliku znajdującym się w katalogu powyżej bieżącego.
Czy można względnie zaimportować ten plik?
Odpowiedzi:
from ..subpkg2 import mod
Zgodnie z dokumentacją w języku Python: w hierarchii pakietów użyj dwóch kropek, jak mówi dokument w instrukcji importu :
Podczas określania modułu do zaimportowania nie trzeba podawać bezwzględnej nazwy modułu. Gdy moduł lub pakiet jest zawarty w innym pakiecie, możliwe jest dokonanie względnego importu w tym samym górnym pakiecie bez konieczności podawania nazwy pakietu. Używając wiodących kropek w określonym module lub pakiecie
from
, możesz określić, jak wysoko przejść w górę bieżącej hierarchii pakietów bez podawania dokładnych nazw. Jedna wiodąca kropka oznacza bieżącą paczkę, w której istnieje moduł dokonujący importu. Dwie kropki oznaczają jeden poziom pakietu . Trzy kropki w górę o dwa poziomy itd. Więc jeśli wykonaszfrom . import mod
z modułu wpkg
pakiecie, zakończysz importowaniepkg.mod
. Jeśli wykonaszfrom ..subpkg2 import mod
od wewnątrzpkg.subpkg1
, zaimportujeszpkg.subpkg2.mod
. Specyfikacja importu względnego zawarta jest w PEP 328 .
PEP 328 dotyczy importu bezwzględnego / względnego.
import sys
sys.path.append("..") # Adds higher directory to python modules path.
Odpowiedź @ gimel jest poprawna, jeśli możesz zagwarantować, że wspomniana hierarchia pakietów. Jeśli nie możesz - jeśli twoja prawdziwa potrzeba jest taka, jak ją wyraziłeś, związana wyłącznie z katalogami i bez żadnego koniecznego związku z pakowaniem - musisz dalej __file__
szukać katalogu nadrzędnego (wystarczy kilka os.path.dirname
połączeń; -), a następnie (jeśli ten katalog nie jest jeszcze włączony sys.path
) tymczasowo wstaw ten katalog na samym początkusys.path
, __import__
wyjmij powiedział reż ponownie - niechlujny pracy rzeczywiście, ale „kiedy musisz, musisz” (i Pyhon dąży do nigdy nie powstrzymuj programisty przed zrobieniem tego, co należy zrobić - tak jak mówi norma ISO C w części „Spirit of C” we wstępie! -).
Oto przykład, który może Ci pomóc:
import sys
import os.path
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
import module_in_parent_dir
sys.path
dzięki czemu ten sam moduł będzie dostępny pod różnymi nazwami i wszystkimi odpowiadającymi im błędami. autopath.py w pypy lub _preamble.py w twisted rozwiązuj go za pomocą kryteriów wyszukiwania, które identyfikują pakiet najwyższego poziomu, podczas gdy katalogi przechodzą w górę.
sys.path.remove(pathYouJustAdded)
po importowaniu, którego potrzebujesz, aby nie zachować tej nowej ścieżki.
Zaimportuj moduł z katalogu znajdującego się dokładnie jeden poziom powyżej bieżącego katalogu:
from .. import module
from .. import module
I dostał błąd ValueError: Podjęto próbę importu względnego w pakiecie , postępując zgodnie z zaleceniem
przedmowa: Dokonałem znacznej przeróbki poprzedniej odpowiedzi z nadzieją, że pomogę wprowadzić ludzi w ekosystem Pythona i mam nadzieję dać wszystkim najlepszą zmianę sukcesu dzięki systemowi importu Pythona.
Obejmuje to względny import w ramach pakietu , co moim zdaniem jest najbardziej prawdopodobnym przypadkiem pytania OP.
Dlatego piszemy, import foo
aby załadować moduł „foo” z głównej przestrzeni nazw, zamiast pisać:
foo = dict(); # please avoid doing this
with open(os.path.join(os.path.dirname(__file__), '../foo.py') as foo_fh: # please avoid doing this
exec(compile(foo_fh.read(), 'foo.py', 'exec'), foo) # please avoid doing this
Dlatego możemy osadzić Pythona w środowisku, w którym nie ma systemu plików defacto, bez zapewnienia wirtualnego, takiego jak Jython.
Oddzielony od systemu plików pozwala na elastyczny import, ten projekt pozwala na takie rzeczy jak import z plików archiwalnych / zip, import singletonów, buforowanie kodów bajtowych, rozszerzenia cffi, a nawet zdalne ładowanie definicji kodu.
Więc jeśli importy nie są połączone z systemem plików, co oznacza „jeden katalog w górę”? Musimy wybrać heurystykę, ale możemy to zrobić, na przykład podczas pracy w pakiecie , niektóre heurystyki zostały już zdefiniowane, dzięki czemu import względny jest podobny .foo
i..foo
działa w ramach tego samego pakietu. Chłodny!
Jeśli naprawdę chcesz połączyć wzorce ładowania kodu źródłowego z systemem plików, możesz to zrobić. Będziesz musiał wybrać własną heurystykę i użyć jakiegoś sprzętu importującego, polecam importlib
Przykład importlib Pythona wygląda mniej więcej tak:
import importlib.util
import sys
# For illustrative purposes.
file_path = os.path.join(os.path.dirname(__file__), '../foo.py')
module_name = 'foo'
foo_spec = importlib.util.spec_from_file_location(module_name, file_path)
# foo_spec is a ModuleSpec specifying a SourceFileLoader
foo_module = importlib.util.module_from_spec(foo_spec)
sys.modules[module_name] = foo_module
foo_spec.loader.exec_module(foo_module)
foo = sys.modules[module_name]
# foo is the sys.modules['foo'] singleton
Świetny przykładowy projekt dostępny oficjalnie tutaj: https://github.com/pypa/sampleproject
Pakiet Pythona to zbiór informacji o kodzie źródłowym, który może informować inne narzędzia, jak skopiować kod źródłowy na inne komputery i jak zintegrować kod źródłowy ze ścieżką tego systemu, aby import foo
działał na innych komputerach (niezależnie od interpretera, system operacyjny hosta itp.)
Pozwala mieć nazwę pakietu foo
w jakimś katalogu (najlepiej w pustym katalogu).
some_directory/
foo.py # `if __name__ == "__main__":` lives here
Preferuję tworzenie setup.py
jako rodzeństwo foo.py
, ponieważ upraszcza to pisanie pliku setup.py, jednak możesz napisać konfigurację, aby zmienić / przekierować wszystko, co domyślnie robi setuptools; na przykład umieszczenie foo.py
w katalogu „src /” jest dość popularne, nie zostało tutaj omówione.
some_directory/
foo.py
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
py_modules=['foo'],
)
.
python3 -m pip install --editable ./ # or path/to/some_directory/
„edytowalne” aka -e
po raz kolejny przekieruje maszynę importującą, aby załadować pliki źródłowe do tego katalogu, zamiast kopiować aktualne dokładne pliki do biblioteki środowiska instalacyjnego. Może to również powodować różnice w zachowaniu na komputerze dewelopera, koniecznie przetestuj swój kod! Są inne narzędzia niż pip, jednak polecam pip być wprowadzający :)
Lubię też tworzyć foo
„pakiet” (katalog zawierający __init__.py
) zamiast modułu (pojedynczy plik „.py”), zarówno „pakiety”, jak i „moduły” mogą być ładowane do głównej przestrzeni nazw, moduły pozwalają na zagnieżdżone przestrzenie nazw, co jest pomocne, jeśli chcemy mieć import „względny jeden katalog do góry”.
some_directory/
foo/
__init__.py
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
packages=['foo'],
)
Lubię też zrobić a foo/__main__.py
, to pozwala pythonowi wykonać pakiet jako moduł, np. python3 -m foo
Wykona się foo/__main__.py
jako __main__
.
some_directory/
foo/
__init__.py
__main__.py # `if __name__ == "__main__":` lives here, `def main():` too!
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
packages=['foo'],
...
entry_points={
'console_scripts': [
# "foo" will be added to the installing-environment's text mode shell, eg `bash -c foo`
'foo=foo.__main__:main',
]
},
)
Uzupełnijmy to o kilka modułów: Zasadniczo możesz mieć strukturę katalogów taką jak:
some_directory/
bar.py # `import bar`
foo/
__init__.py # `import foo`
__main__.py
baz.py # `import foo.baz
spam/
__init__.py # `import foo.spam`
eggs.py # `import foo.spam.eggs`
setup.py
setup.py
konwencjonalnie przechowuje informacje o metadanych o kodzie źródłowym, takie jak:
foo
, chociaż popularne jest zastępowanie podkreślników łącznikamipython ./setup.py test
Jest bardzo rozbudowany, może nawet kompilować rozszerzenia c w locie, jeśli moduł źródłowy jest instalowany na maszynie programistycznej. Dla codziennego przykładu polecam setup.py PYPA Sample Repository
Jeśli zwalniasz artefakt kompilacji, np. Kopię kodu, który ma uruchamiać prawie identyczne komputery, plik wymagania.txt jest popularnym sposobem na wykonanie migawki dokładnych informacji o zależnościach, gdzie „install_requires” jest dobrym sposobem na uchwycenie minimum i maksymalnie kompatybilne wersje. Jednak biorąc pod uwagę fakt, że docelowe maszyny i tak są prawie identyczne, bardzo polecam utworzenie tarballa całego prefiksu Pythona. Może to być trudne, zbyt szczegółowe, aby się tutaj dostać. Sprawdź pip install
„s--target
opcję lub virtualenv aka venv dla potencjalnych klientów.
powrót do przykładu
Z foo / spam / eggs.py, gdybyśmy chcieli kodu z foo / baz, moglibyśmy go poprosić o jego absolutną przestrzeń nazw:
import foo.baz
Jeśli chcielibyśmy zarezerwować możliwość przeniesienia eggs.py do innego katalogu w przyszłości za pomocą innej względnej baz
implementacji, moglibyśmy użyć importu względnego, takiego jak:
import ..baz
Aby niezawodnie ładować kod Pythona, umieść ten kod w module, a moduł ten zainstaluj w bibliotece Pythona.
Zainstalowane moduły zawsze można załadować z przestrzeni nazw najwyższego poziomu za pomocą import <name>
Świetny przykładowy projekt dostępny jest oficjalnie tutaj: https://github.com/pypa/sampleproject
Zasadniczo możesz mieć taką strukturę katalogów:
the_foo_project/
setup.py
bar.py # `import bar`
foo/
__init__.py # `import foo`
baz.py # `import foo.baz`
faz/ # `import foo.faz`
__init__.py
daz.py # `import foo.faz.daz` ... etc.
.
Pamiętaj, aby zadeklarować setuptools.setup()
insetup.py
,
oficjalny przykład: https://github.com/pypa/sampleproject/blob/master/setup.py
W naszym przypadku prawdopodobnie chcemy wyeksportować, bar.py
a foo/__init__.py
mój krótki przykład:
#!/usr/bin/env python3
import setuptools
setuptools.setup(
...
py_modules=['bar'],
packages=['foo'],
...
entry_points={},
# Note, any changes to your setup.py, like adding to `packages`, or
# changing `entry_points` will require the module to be reinstalled;
# `python3 -m pip install --upgrade --editable ./the_foo_project
)
.
Teraz możemy zainstalować nasz moduł w bibliotece Pythona; dzięki pip możesz zainstalować the_foo_project
w bibliotece Pythona w trybie edycji, abyśmy mogli pracować nad nim w czasie rzeczywistym
python3 -m pip install --editable=./the_foo_project
# if you get a permission error, you can always use
# `pip ... --user` to install in your user python library
.
Teraz z dowolnego kontekstu Pythona możemy załadować nasze współdzielone py_modules i pakiety
#!/usr/bin/env python3
import bar
import foo
print(dir(bar))
print(dir(foo))
pip install --edit foo
, prawie zawsze wewnątrz virtualenv. Prawie nigdy nie piszę modułu, który nie jest przeznaczony do zainstalowania. jeśli źle zrozumiem coś, co chciałbym wiedzieć.
editable
linki do jaj i zainstalowane moduły python nie są dokładnie takie same pod każdym względem; na przykład dodanie nowej przestrzeni nazw do modułu edytowalnego zostanie znalezione podczas wyszukiwania ścieżki, ale jeśli nie zostanie to wyeksportowane do pliku setup.py, nie zostanie spakowane / zainstalowane! Przetestuj swój przypadek użycia :)