Jak zaimportować klasę Python, która znajduje się w katalogu powyżej?

211

Chcę dziedziczyć po klasie w pliku znajdującym się w katalogu powyżej bieżącego.

Czy można względnie zaimportować ten plik?

użytkownik129975
źródło

Odpowiedzi:

177

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 wykonasz from . import modz modułu w pkgpakiecie, zakończysz importowanie pkg.mod. Jeśli wykonasz from ..subpkg2 import modod wewnątrz pkg.subpkg1, zaimportujeszpkg.subpkg2.mod. Specyfikacja importu względnego zawarta jest w PEP 328 .

PEP 328 dotyczy importu bezwzględnego / względnego.

gimel
źródło
4
up1 = os.path.abspath ('..') sys.path.insert (0, up1)
rp.
Pep 328 pokazuje tylko wersję Python: 2.4, 2,5, 2.6. Wersja 3 jest pozostawiona duszom o większej wiedzy.
gimel
22
Powoduje to błąd Błąd wartości: próba importu względnego poza pakietem najwyższego poziomu
Carlo
4
Wyniki w ImportError: próba importu względnego bez znanego pakietu nadrzędnego
Rotkiv
115
import sys
sys.path.append("..") # Adds higher directory to python modules path.
Sepero
źródło
1
to działa dla mnie. Po dodaniu tego mogę bezpośrednio importować moduły nadrzędne, bez potrzeby używania „..”.
Evan Hu
8
z grubsza działa to tylko wtedy, gdy wartość PWD aplikacji - jej obecny directeroy - jest dzieckiem rodzica. Tak więc, nawet jeśli zadziała, wiele zmian systemowych może to wyprzeć.
Phlip,
Działa to również dla mnie w przypadku importowania modułów wyższego poziomu. Używam tego z os.chdir („..”) do ładowania innych plików wyższego poziomu.
Surendra Shrestha
Wydaje się, że powoduje to ten sam błąd, co powyższa odpowiedź z gimel.
Carlo,
81

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.dirnamepołą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
Alex Martelli
źródło
2
może to dodać katalog znajdujący się w pakiecie Pythona, sys.pathdzię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ę.
jfs
4
Możesz chcieć zrobić coś takiego sys.path.remove(pathYouJustAdded)po importowaniu, którego potrzebujesz, aby nie zachować tej nowej ścieżki.
Matthias,
35

Zaimportuj moduł z katalogu znajdującego się dokładnie jeden poziom powyżej bieżącego katalogu:

from .. import module
Sepero
źródło
38
Dostałem: próba importu względnego poza pakietem najwyższego poziomu :(
RicardoE
25
using from .. import module I dostał błąd ValueError: Podjęto próbę importu względnego w pakiecie , postępując zgodnie z zaleceniem
3428154
4

Jak załadować moduł będący katalogiem do góry

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.

Python jest systemem modułowym

Dlatego piszemy, import fooaby 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

Python nie jest sprzężony z systemem plików

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 .fooi..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

Opakowanie

Ś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 foodziałał na innych komputerach (niezależnie od interpretera, system operacyjny hosta itp.)

Struktura katalogów

Pozwala mieć nazwę pakietu foow jakimś katalogu (najlepiej w pustym katalogu).

some_directory/
    foo.py  # `if __name__ == "__main__":`  lives here

Preferuję tworzenie setup.pyjako 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.pyw 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 -epo 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 fooWykona się foo/__main__.pyjako __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:

  • jakie zależności są potrzebne do instalacji o nazwie „install_requires”
  • jaka nazwa powinna być używana do zarządzania pakietami (zainstaluj / odinstaluj „nazwa”), sugeruję, aby pasowała do twojej podstawowej nazwy pakietu python w naszym przypadku foo , chociaż popularne jest zastępowanie podkreślników łącznikami
  • informacje licencyjne
  • tagi dojrzałości (alfa / beta / itp.),
  • tagi odbiorców (dla programistów, uczenia maszynowego itp.),
  • jednostronicowa treść dokumentacji (np. README),
  • nazwy powłoki (nazwy, które wpisujesz w powłoce użytkownika, takie jak bash, lub nazwy, które znajdziesz w graficznej powłoce użytkownika, takiej jak menu startowe),
  • lista modułów Pythona, które ten pakiet zainstaluje (i odinstaluje)
  • punkt wejścia „uruchom testy” defacto python ./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

jak zaimportować plik o jeden katalog w górę:

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 bazimplementacji, moglibyśmy użyć importu względnego, takiego jak:

import ..baz
ThorSummoner
źródło
-5

Python jest systemem modułowym

Python nie polega na systemie plików

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.pya foo/__init__.pymój krótki przykład:

setup.py

#!/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_projectw 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

foo_script.py

#!/usr/bin/env python3

import bar
import foo

print(dir(bar))
print(dir(foo))
ThorSummoner
źródło
2
powinienem wspomnieć, że zawsze instaluję moduł podczas pracy z nim 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ć.
ThorSummoner
Powinienem również wspomnieć, że przy użyciu pakietu testowego automatycznego tworzenia Venv, takiego jak tox, jest bardzo pomocny, ponieważ editablelinki 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 :)
ThorSummoner