Względny import w Pythonie 3

711

Chcę zaimportować funkcję z innego pliku w tym samym katalogu.

Czasami działa dla mnie, from .mymodule import myfunctionale czasami dostaję:

SystemError: Parent module '' not loaded, cannot perform relative import

Czasami to działa from mymodule import myfunction, ale czasami dostaję również:

SystemError: Parent module '' not loaded, cannot perform relative import

Nie rozumiem tutaj logiki i nie mogłem znaleźć żadnego wyjaśnienia. To wygląda zupełnie losowo.

Czy ktoś mógłby mi wyjaśnić, jaka jest logika tego wszystkiego?

John Smith Opcjonalnie
źródło
76
Oznacza to, że uruchamiasz moduł w pakiecie jako skrypt. Uruchamiaj tylko skrypty spoza pakietu.
Martijn Pieters
3
Prawdopodobnie powinieneś zdefiniować warunki, o których wspominasz „czasami”. Rozumiem, że nie oznacza to, że masz przypadkowe błędy.
joaquin
15
@MartijnPieters: cóż, niestety ten moduł musi znajdować się w pakiecie, a czasami musi być również uruchamiany jako skrypt. Masz pomysł, jak to osiągnąć?
John Smith Opcjonalnie
22
@JohnSmithOptional: Mieszanie skryptów w paczkach jest trudne i należy tego unikać, jeśli to w ogóle możliwe. Użyj skryptu opakowania, który importuje pakiet i uruchamia zamiast tego funkcję „scripty”.
Martijn Pieters
3
Wydaje się niefortunne. Zrobiłem moduł podstawowy z klasami / metodami, które potrafią parsować / analizować pewien rodzaj pliku, a także mam (głównie dla siebie) osobne moduły dodatkowe i skrypty, które je importują - mogą one masować / konwertować te pliki. Ale lubię też móc przekazać ten pojedynczy rdzeń pliku (nie cały pakiet) użytkownikowi końcowemu, aby mógł łatwo umieścić go obok swojego pliku i uruchomić. W tym „trybie skryptowym” analizuje i analizuje plik i kodowanie, porównuje różne pola / wartości / znaki specjalne i podaje raport. Ale tak naprawdę nie modyfikuje pliku. Anty-wzór?
Jon Coombs

Odpowiedzi:

526

niestety ten moduł musi znajdować się w pakiecie, a czasami musi być również uruchamiany jako skrypt. Masz pomysł, jak to osiągnąć?

Układ taki jak ten jest dość powszechny ...

main.py
mypackage/
    __init__.py
    mymodule.py
    myothermodule.py

... z mymodule.pypodobnym ...

#!/usr/bin/env python3

# Exported function
def as_int(a):
    return int(a)

# Test function for module  
def _test():
    assert as_int('1') == 1

if __name__ == '__main__':
    _test()

... myothermodule.pyjak ten ...

#!/usr/bin/env python3

from .mymodule import as_int

# Exported function
def add(a, b):
    return as_int(a) + as_int(b)

# Test function for module  
def _test():
    assert add('1', '1') == 2

if __name__ == '__main__':
    _test()

... i main.pytym podobne ...

#!/usr/bin/env python3

from mypackage.myothermodule import add

def main():
    print(add('1', '1'))

if __name__ == '__main__':
    main()

... który działa dobrze po uruchomieniu main.pylub mypackage/mymodule.py, ale nie działa mypackage/myothermodule.py, ze względu na względny import ...

from .mymodule import as_int

Sposób, w jaki powinieneś to uruchomić, to ...

python3 -m mypackage.myothermodule

... ale jest nieco gadatliwy i nie pasuje dobrze do linii shebang #!/usr/bin/env python3.

Najprostszym rozwiązaniem w tym przypadku, zakładając, że nazwa mymodulejest globalnie unikalna, byłoby uniknięcie względnego importu i użycie ...

from mymodule import as_int

... chociaż jeśli nie jest unikalny lub struktura pakietu jest bardziej złożona, musisz dołączyć katalog zawierający katalog pakietu PYTHONPATHi zrobić to w ten sposób ...

from mypackage.mymodule import as_int

... lub jeśli chcesz, aby działał „od razu po wyjęciu z pudełka”, możesz najpierw przeforsować PYTHONPATHkod w tym ...

import sys
import os

PACKAGE_PARENT = '..'
SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))))
sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT)))

from mypackage.mymodule import as_int

To rodzaj bólu, ale istnieje wskazówka, dlaczego w wiadomości e-mail napisanej przez pewnego Guido van Rossuma ...

Mam -1 w tej i innych proponowanych zmianach __main__ maszyny. Wydaje się, że jedynym przypadkiem użycia są uruchomione skrypty, które znajdują się w katalogu modułu, co zawsze uważałem za antipattern. Aby zmienić zdanie, musisz mnie przekonać, że tak nie jest.

Niezależnie od tego, czy uruchamianie skryptów w pakiecie jest antypatriorytetem, czy nie, jest ono subiektywne, ale osobiście uważam, że jest to bardzo przydatne w pakiecie, który mam, który zawiera niestandardowe widżety wxPython, dzięki czemu mogę uruchomić skrypt dla dowolnego pliku źródłowego, aby wyświetlić wx.Frametylko zawierający ten widget do celów testowych.

Aya
źródło
7
Lepszy sposób na uzyskanie SCRIPTDIR podano w komentarzu Importuj moduł ze ścieżki względnej, tak os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))jakbyś był pewny, że Twój moduł ma zawsze odpowiednie właściwości file, których możesz również użyć os.path.realpath(os.path.dirname(__file__)).
marcz
2
Możesz rozszerzyć PYTHONPATH, stosując krótszy i czytelniejszy fragment kodu: sys.path.append( os.path.join( os.path.dirname(__file__), os.path.pardir ) )
Alex-Bogdanov,
12
...which I've always seen as an antipattern.Nie rozumiem, jak to jest anty-wzór ... Wydaje się, że byłoby bardzo wygodne, gdyby importowanie względne działało intuicyjnie. Chcę tylko móc importować rzeczy, które, jak wiem, znajdują się w tym samym katalogu. Zastanawiam się, jakie było jego rozumowanie
YungGun,
9
Guido uderza ponownie: nixing rzeczy, które mogłyby być przydatne. To się więcej nie wydarzy.
javadba
4
To najbardziej smutna rzecz, jaką widziałem w Pythonie.
AtilioA
262

Wyjaśnienie

Od PEP 328

Względne importy używają atrybutu __name__ modułu, aby określić pozycję tego modułu w hierarchii pakietów. Jeśli nazwa modułu nie zawiera żadnych informacji o pakiecie (np. Jest ustawiona na „__main__”), importy względne są rozstrzygane tak, jakby moduł był modułem najwyższego poziomu , niezależnie od tego, gdzie moduł faktycznie znajduje się w systemie plików.

W pewnym momencie PEP 338 był w konflikcie z PEP 328 :

... import względny opiera się na __name__ w celu ustalenia aktualnej pozycji modułu w hierarchii pakietów. W głównym module wartość __name__ to zawsze „__main__” , więc jawne importowanie względne zawsze się nie powiedzie (ponieważ działają tylko dla modułu wewnątrz pakietu)

Aby rozwiązać ten problem, PEP 366 wprowadził zmienną najwyższego poziomu __package__:

Dodając nowy atrybut na poziomie modułu, ten PEP pozwala na automatyczne importowanie względne, jeśli moduł jest wykonywany za pomocą przełącznika -m . Niewielka ilość płyty kotłowej w samym module pozwoli na działanie importu względnego, gdy plik jest wykonywany według nazwy. [...] Gdy [atrybut] jest obecny, import względny będzie oparty na tym atrybucie, a nie na module __name__ . [...] Gdy główny moduł jest określony nazwą pliku, wówczas atrybut __package__ zostanie ustawiony na Brak . [...] Gdy system importu napotka jawny import względny w module bez zestawu __package__ (lub z ustawionym na None), obliczy i zapisze poprawną wartość (__name __. rpartition ('.') [0] dla normalnych modułów i __name__ dla modułów inicjalizujących pakiety)

(moje podkreślenie)

Jeśli __name__jest '__main__', __name__.rpartition('.')[0]zwraca pusty ciąg. Oto dlaczego w opisie błędu występuje literał pusty łańcuch:

SystemError: Parent module '' not loaded, cannot perform relative import

Istotna część tego CPython jest PyImport_ImportModuleLevelObjectfunkcją :

if (PyDict_GetItem(interp->modules, package) == NULL) {
    PyErr_Format(PyExc_SystemError,
            "Parent module %R not loaded, cannot perform relative "
            "import", package);
    goto error;
}

CPython zgłasza ten wyjątek, jeśli nie mógł znaleźć package(nazwa pakietu) w interp->modules(dostępny jako sys.modules). Ponieważ sys.modulesjest to „słownik, który odwzorowuje nazwy modułów na moduły, które zostały już załadowane” , jest teraz jasne, że moduł nadrzędny musi zostać jawnie zaimportowany absolutnie przed wykonaniem importu względnego .

Uwaga: Łatka z wydania 18018 dodała kolejny ifblok , który zostanie wykonany przed powyższym kodem:

if (PyUnicode_CompareWithASCIIString(package, "") == 0) {
    PyErr_SetString(PyExc_ImportError,
            "attempted relative import with no known parent package");
    goto error;
} /* else if (PyDict_GetItem(interp->modules, package) == NULL) {
    ...
*/

Jeśli package(tak jak powyżej) jest pusty ciąg, pojawi się komunikat o błędzie

ImportError: attempted relative import with no known parent package

Jednak zobaczysz to tylko w Pythonie 3.6 lub nowszym.

Rozwiązanie nr 1: Uruchom skrypt za pomocą -m

Rozważmy katalog (który jest pakietem Python ):

.
├── package
│   ├── __init__.py
│   ├── module.py
│   └── standalone.py

Wszystkie pliki w pakiecie zaczynają się od tych samych 2 linii kodu:

from pathlib import Path
print('Running' if __name__ == '__main__' else 'Importing', Path(__file__).resolve())

Uwzględniam te dwie linie tylko po to, aby porządek operacji był oczywisty. Możemy je całkowicie zignorować, ponieważ nie mają one wpływu na wykonanie.

__init__.py i module.py zawierają tylko te dwie linie (tzn. są faktycznie puste).

standalone.py dodatkowo próbuje zaimportować module.py poprzez import względny:

from . import module  # explicit relative import

Wiemy, że /path/to/python/interpreter package/standalone.pyto się nie powiedzie. Możemy jednak uruchomić moduł z -mopcją wiersza poleceń , która „wyszuka sys.pathnazwany moduł i wykona jego zawartość jako __main__moduł” :

vaultah@base:~$ python3 -i -m package.standalone
Importing /home/vaultah/package/__init__.py
Running /home/vaultah/package/standalone.py
Importing /home/vaultah/package/module.py
>>> __file__
'/home/vaultah/package/standalone.py'
>>> __package__
'package'
>>> # The __package__ has been correctly set and module.py has been imported.
... # What's inside sys.modules?
... import sys
>>> sys.modules['__main__']
<module 'package.standalone' from '/home/vaultah/package/standalone.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/package/module.py'>
>>> sys.modules['package']
<module 'package' from '/home/vaultah/package/__init__.py'>

-mwykonuje wszystkie operacje importowania dla Ciebie i automatycznie ustawia __package__, ale możesz to zrobić samodzielnie w

Rozwiązanie nr 2: Ustaw __package__ ręcznie

Potraktuj to jako dowód koncepcji, a nie faktyczne rozwiązanie. Nie nadaje się do użycia w kodzie rzeczywistym.

PEP 366 ma obejście tego problemu, jednak jest niekompletne, ponieważ __package__samo ustawienie nie wystarczy. Będziesz musiał zaimportować co najmniej N poprzednich pakietów w hierarchii modułów, gdzie N jest liczbą katalogów nadrzędnych (w stosunku do katalogu skryptu), które będą wyszukiwane w poszukiwaniu importowanego modułu.

A zatem,

  1. Dodaj katalog nadrzędny n-tego poprzednika bieżącego modułu dosys.path

  2. Usuń katalog bieżącego pliku z sys.path

  3. Zaimportuj moduł nadrzędny bieżącego modułu, używając jego pełnej nazwy

  4. Ustaw __package__na pełną nazwę od 2

  5. Wykonaj import względny

Pożyczę pliki z rozwiązania nr 1 i dodam kilka dodatkowych paczek:

package
├── __init__.py
├── module.py
└── subpackage
    ├── __init__.py
    └── subsubpackage
        ├── __init__.py
        └── standalone.py

Tym razem standalone.py zaimportuje module.py z pakietu pakietu przy użyciu następującego importu względnego

from ... import module  # N = 3

Będziemy musieli poprzedzić ten wiersz kodem bojlera, aby działał.

import sys
from pathlib import Path

if __name__ == '__main__' and __package__ is None:
    file = Path(__file__).resolve()
    parent, top = file.parent, file.parents[3]

    sys.path.append(str(top))
    try:
        sys.path.remove(str(parent))
    except ValueError: # Already removed
        pass

    import package.subpackage.subsubpackage
    __package__ = 'package.subpackage.subsubpackage'

from ... import module # N = 3

Pozwala nam to wykonać standalone.py według nazwy pliku:

vaultah@base:~$ python3 package/subpackage/subsubpackage/standalone.py
Running /home/vaultah/package/subpackage/subsubpackage/standalone.py
Importing /home/vaultah/package/__init__.py
Importing /home/vaultah/package/subpackage/__init__.py
Importing /home/vaultah/package/subpackage/subsubpackage/__init__.py
Importing /home/vaultah/package/module.py

Bardziej ogólne rozwiązanie zawarte w funkcji można znaleźć tutaj . Przykładowe użycie:

if __name__ == '__main__' and __package__ is None:
    import_parents(level=3) # N = 3

from ... import module
from ...module.submodule import thing

Rozwiązanie nr 3: Użyj importu bezwzględnego i setuptools

Kroki to -

  1. Zastąp wyraźny import względny równoważnym importem absolutnym

  2. Zainstaluj, packageaby można go było importować

Na przykład struktura katalogów może wyglądać następująco

.
├── project
│   ├── package
│   │   ├── __init__.py
│   │   ├── module.py
│   │   └── standalone.py
│   └── setup.py

gdzie jest plik setup.py

from setuptools import setup, find_packages
setup(
    name = 'your_package_name',
    packages = find_packages(),
)

Pozostałe pliki zostały zapożyczone z rozwiązania nr 1 .

Instalacja pozwoli ci zaimportować pakiet niezależnie od katalogu roboczego (zakładając, że nie wystąpią problemy z nazewnictwem).

Możemy zmodyfikować standalone.py, aby wykorzystać tę zaletę (krok 1):

from package import module  # absolute import

Zmień katalog roboczy na projecti uruchom /path/to/python/interpreter setup.py install --user( --userinstaluje pakiet w katalogu pakietów witryny ) (krok 2):

vaultah@base:~$ cd project
vaultah@base:~/project$ python3 setup.py install --user

Sprawdźmy, czy można teraz uruchomić standalone.py jako skrypt:

vaultah@base:~/project$ python3 -i package/standalone.py
Running /home/vaultah/project/package/standalone.py
Importing /home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/__init__.py
Importing /home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py
>>> module
<module 'package.module' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py'>
>>> import sys
>>> sys.modules['package']
<module 'package' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/__init__.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py'>

Uwaga : jeśli zdecydujesz się zejść tą drogą, lepiej byłoby używać środowisk wirtualnych do instalowania pakietów w izolacji.

Rozwiązanie nr 4: Użyj importu bezwzględnego i trochę kodu typu „kocioł”

Szczerze mówiąc, instalacja nie jest konieczna - możesz dodać do skryptu trochę kodu, aby importowanie absolutne działało.

Pożyczę pliki z Rozwiązania nr 1 i zmienię standalone.py :

  1. Dodaj katalog nadrzędny pakiet aby sys.path przed przystąpieniem do importu czegokolwiek z pakietu za pomocą importu bezwzględne:

    import sys
    from pathlib import Path # if you haven't already done so
    file = Path(__file__).resolve()
    parent, root = file.parent, file.parents[1]
    sys.path.append(str(root))
    
    # Additionally remove the current file's directory from sys.path
    try:
        sys.path.remove(str(parent))
    except ValueError: # Already removed
        pass
  2. Zastąp import względny importem bezwzględnym:

    from package import module  # absolute import

standalone.py działa bez problemów:

vaultah@base:~$ python3 -i package/standalone.py
Running /home/vaultah/package/standalone.py
Importing /home/vaultah/package/__init__.py
Importing /home/vaultah/package/module.py
>>> module
<module 'package.module' from '/home/vaultah/package/module.py'>
>>> import sys
>>> sys.modules['package']
<module 'package' from '/home/vaultah/package/__init__.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/package/module.py'>

Uważam, że powinienem cię ostrzec: staraj się tego nie robić, zwłaszcza jeśli twój projekt ma złożoną strukturę.


Na marginesie, PEP 8 zaleca stosowanie importu bezwzględnego, ale stwierdza, że ​​w niektórych scenariuszach dopuszczalny jest wyraźny import względny:

Zalecane jest importowanie bezwzględne, ponieważ są one zazwyczaj bardziej czytelne i zwykle lepiej się zachowują (lub przynajmniej dają lepsze komunikaty o błędach). [...] Jednak wyraźny import względny jest akceptowalną alternatywą dla importu absolutnego, szczególnie w przypadku złożonych układów pakietów, w których użycie importu absolutnego byłoby niepotrzebnie gadatliwe.

sklepienie
źródło
3
Czy można ustawić __package__ręcznie, jeśli nazwa ma __main__rozwiązać problem?
Paulo Scardine
Dzięki, miłe odpowiedzi! Byłem w stanie załadować moduł za pomocą impmodułu i __package__odpowiednio go ustawić, ale wynik jest wyraźnie anty-wzorem.
Paulo Scardine
Dostaję błąd AttributeError: 'PosixPath' object has no attribute 'path'.
użytkownik
Dziękuję za szybką odpowiedź. Korzystam z pakietu nltk, pojawia się błąd: `File '/usr/local/lib/python3.5/dist-packages/nltk/__init__.py", wiersz 115, w <module> z nltk.decorators importuj dekorator, zapamiętaj Plik "/usr/local/lib/python3.5/dist-packages/nltk/decorators.py", wiersz 23, w <module> sys.path = [p dla pw sys.path jeśli "nltk „not in p] File” /usr/local/lib/python3.5/dist-packages/nltk/decorators.py ”, wiersz 23, w <listcomp> sys.path = [p dla p w sys.path jeśli” nltk "not in p] TypeError: argument typu 'PosixPath' nie jest iterowalny`
użytkownik
1
Możesz także zaimportować plik według ścieżki pliku (też względnej): docs.python.org/3/library/…
Ctrl-C
84

Umieść to w pliku __init__.py pakietu :

# For relative imports to work in Python 3.6
import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__)))

Zakładając, że Twój pakiet wygląda następująco:

├── project
   ├── package
      ├── __init__.py
      ├── module1.py
      └── module2.py
   └── setup.py

Teraz używaj regularnego importu w swoim pakiecie, na przykład:

# in module2.py
from module1 import class1

Działa to zarówno w Pythonie 2, jak i 3.

am5
źródło
działa to, jeśli
spakujemy
1
Myślę też, że zasługuje na więcej głosów. Umieszczenie tego w każdym __init__.pyrozwiąże w zasadzie wszystkie względne błędy importu.
frankliuao
3
Nie mogę mówić za innymi, ale staram się unikać modyfikacji, sys.pathponieważ obawiam się, że może to wpłynąć na inny kod. (Częściowo dzieje się tak, ponieważ nie znam zawiłości tego, jak to działa.)
pianoJames 10.10.19
@pianoJames Wiem, co masz na myśli, ta (pozornie po wielu głupstwach) magiczna poprawka wydaje się trochę zbyt łatwa. Ale to działa. Byłbym zainteresowany nie wiem od tych, którzy wiedzą, czy to ma negatywne skutki uboczne.
Jon
Używam tego teraz: i jak dotąd tak dobrze.
javadba
37

Natknąłem się na ten problem. Obejście problemu włamania jest importowane przez blok if / else w następujący sposób:

#!/usr/bin/env python3
#myothermodule

if __name__ == '__main__':
    from mymodule import as_int
else:
    from .mymodule import as_int


# Exported function
def add(a, b):
    return as_int(a) + as_int(b)

# Test function for module  
def _test():
    assert add('1', '1') == 2

if __name__ == '__main__':
    _test()
łamaniec
źródło
29
to niezbyt miłe rozwiązanie. także goły except:jest zły. użyj except ImportError:zamiast tego!
ThiefMaster,
6
Jest SystemErrortutaj (Py 3.4)
Avi
8
To nie jest okropny pomysł, ale lepiej byłoby wykryć, którego importu użyć, niż spróbować / z wyjątkiem. Coś jak if __name__ == '__main__': from mymod import as_int; else: from .mymod import as_int.
Perkins
@Perkins Cóż ... w większości przypadków nie . Myślę jednak, że względny import może być wyjątkiem.
wizzwizz4
8

Mam nadzieję, że będzie to dla kogoś wartościowe - przeszedłem przez pół tuzina postów z przepływem stosów, próbując znaleźć względny import podobny do tego, co zamieszczono powyżej. Ustawiłem wszystko zgodnie z sugestią, ale wciąż uderzałemModuleNotFoundError: No module named 'my_module_name'

Ponieważ właśnie rozwijałem się lokalnie i bawiłem, nie utworzyłem / nie uruchomiłem setup.pypliku. Najwyraźniej też tego nie ustawiłem PYTHONPATH.

Zdałem sobie sprawę, że kiedy uruchomiłem mój kod, tak jak wtedy, gdy testy były w tym samym katalogu co moduł, nie mogłem znaleźć mojego modułu:

$ python3 test/my_module/module_test.py                                                                                                               2.4.0
Traceback (most recent call last):
  File "test/my_module/module_test.py", line 6, in <module>
    from my_module.module import *
ModuleNotFoundError: No module named 'my_module'

Kiedy jednak wyraźnie podałem ścieżkę, rzeczy zaczęły działać:

$ PYTHONPATH=. python3 test/my_module/module_test.py                                                                                                  2.4.0
...........
----------------------------------------------------------------------
Ran 11 tests in 0.001s

OK

Jeśli więc ktoś wypróbuje kilka sugestii, uważa, że ​​jego kod jest poprawnie skonstruowany i nadal znajduje się w podobnej sytuacji, jak ja, spróbuj wykonać jedną z poniższych czynności, jeśli nie wyeksportujesz bieżącego katalogu do PYTHONPATH:

  1. Uruchom kod i wyraźnie podaj ścieżkę w następujący sposób: $ PYTHONPATH=. python3 test/my_module/module_test.py
  2. Aby uniknąć wywoływania PYTHONPATH=., utwórz setup.pyplik o treści takiej jak poniżej i uruchom, python setup.py developmentaby dodać pakiety do ścieżki:
# setup.py
from setuptools import setup, find_packages

setup(
    name='sample',
    packages=find_packages()
)
LaCroixed
źródło
6

Musiałem uruchomić Python3 z głównego katalogu projektu, aby działał.

Na przykład, jeśli projekt ma następującą strukturę:

project_demo/
├── main.py
├── some_package/
   ├── __init__.py
   └── project_configs.py
└── test/
    └── test_project_configs.py

Rozwiązanie

Uruchomiłbym python3 w folderze project_demo /, a następnie wykonałem

from some_package import project_configs
Árthur
źródło
4

Aby uniknąć tego problemu, opracowałem rozwiązanie z pakietem przepakowywania , które działało dla mnie od pewnego czasu. Dodaje górny katalog do ścieżki lib:

import repackage
repackage.up()
from mypackage.mymodule import myfunction

Dzięki przepakowaniu można dokonać względnego importu, który działa w szerokim zakresie przypadków, przy użyciu inteligentnej strategii (sprawdzanie stosu wywołań).

fralau
źródło
Zdecydowanie najłatwiejsze rozwiązanie! Dziękuję Ci!
CodingInCircles
1
Dzięki! Zamiast dać najlepszą odpowiedź, próbowałem dać użyteczną odpowiedź :-)
fralau
3

jeśli oba pakiety znajdują się w ścieżce importu (sys.path), a żądany moduł / klasa znajduje się w przykładzie / example.py, to aby uzyskać dostęp do klasy bez importu względnego, spróbuj:

from example.example import fkt
Salt999
źródło
1

Myślę, że najlepszym rozwiązaniem jest utworzenie pakietu dla modułu: Oto więcej informacji o tym, jak to zrobić.

Gdy masz już pakiet, nie musisz się martwić o import względny, możesz po prostu wykonać import bezwzględny.

Taras Kucherenko
źródło
0

Miałem podobny problem: potrzebowałem usługi Linux i wtyczki cgi, które używają wspólnych stałych do współpracy. „Naturalnym” sposobem na to jest umieszczenie ich w pliku init .py pakietu, ale nie mogę uruchomić wtyczki cgi z parametrem -m.

Moje ostateczne rozwiązanie było podobne do rozwiązania nr 2 powyżej:

import sys
import pathlib as p
import importlib

pp = p.Path(sys.argv[0])
pack = pp.resolve().parent

pkg = importlib.import_module('__init__', package=str(pack))

Wadą jest to, że musisz poprzedzić stałe (lub wspólne funkcje) pkg:

print(pkg.Glob)
Thomas Heckmann
źródło