Jak zrobić import względny w Pythonie?

Wyobraź sobie strukturę katalogów:

app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
   sub2/
      __init__.py
      mod2.py

Koduję mod1 i muszę coś zaimportować z mod2. Jak mam to zrobić?

Próbowałem from ..sub2 import mod2, ale otrzymuję "Attempted relative import in non-package".

Wygooglowałem, ale znalazłem tylko hacki "sys.path manipulacja". Nie ma czystego sposobu?


Edit: wszystkie moje __init__.py są obecnie puste

Edit2: próbuję to zrobić, ponieważ sub2 zawiera klasy, które są współdzielone między pod pakietami (sub1, subX, itd.).

Edit3: zachowanie którego szukam jest takie samo jak opisane w PEP 366 (Dzięki John B)

Author: ferkulat, 2008-09-16

16 answers

Wydaje się, że każdy chce ci powiedzieć, co powinieneś robić, a nie tylko odpowiadać na pytanie.

Problem polega na tym, że uruchamiasz moduł jako '_ _ main__ ' przekazując mod1.py jako argument dla tłumacza.

From PEP 328:

Import względny używa atrybutu __Nazwa__ modułu do określenia pozycji tego modułu w hierarchii pakietów. Jeśli nazwa modułu nie zawiera żadnych informacji o pakiecie (np. jest ustawiona na ' _ _ main__') następnie względne importy są rozwiązywane tak, jakby moduł był modułem najwyższego poziomu, niezależnie od tego, gdzie moduł znajduje się w systemie plików.

W Pythonie 2.6 dodają możliwość odwoływania się do modułów względem głównego modułu. PEP 366 opisuje zmianę.

Update: według Nicka Coghlana, zalecaną alternatywą jest uruchomienie modułu wewnątrz pakietu za pomocą przełącznika-M.

 276
Author: John B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-07 15:25:46
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. biegniesz python main.py.
  2. main.py robi: import app.package_a.module_a
  3. module_a.py robi import app.package_b.module_b

Alternatywnie 2 lub 3 można użyć: from app.package_a import module_a

To będzie działać tak długo, jak masz app w swojej ścieżce PYTHONPATH. Więc może być wszędzie.

Więc piszesz setup.py aby skopiować (zainstalować) cały pakiet aplikacji i podpakiety do folderów Pythona systemu docelowego i main.py do folderów skryptów systemu docelowego.

 105
Author: nosklo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-21 12:42:11

Oto rozwiązanie, które działa dla mnie:

Robię import względny jako from ..sub2 import mod2 następnie, jeśli chcę uruchomić mod1.py, idę do katalogu nadrzędnego app i uruchamiam moduł używając przełącznika python-m jako python -m app.sub1.mod1.

Prawdziwym powodem, dla którego ten problem występuje przy relatywnym imporcie, jest to, że relatywny import działa przyjmując właściwość __name__ modułu. Jeśli moduł jest uruchamiany bezpośrednio, to __name__ jest ustawione na __main__ i nie zawiera żadnych informacji o pakiecie struktura. I dlatego python narzeka na błąd relative import in non-package.

Tak więc, używając przełącznika-m, przekazujesz pythonowi informacje o strukturze pakietów, za pomocą których może on pomyślnie rozwiązać względny import.

Napotkałem ten problem wiele razy podczas względnego importu. I, po przeczytaniu wszystkich poprzednich odpowiedzi, nadal nie byłem w stanie dowiedzieć się, jak go rozwiązać, w czysty sposób, bez konieczności umieszczania kodu boilerplate we wszystkich plikach. (Choć niektóre z komentarze były naprawdę pomocne, dzięki @ ncoghlan i @XiongChiamiov)

Mam nadzieję, że pomoże to komuś, kto walczy z problemem względnego importu, ponieważ przechodzenie przez PEP nie jest naprawdę zabawne.

 101
Author: Pankaj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-17 07:43:34

"Guido postrzega uruchamianie skryptów w pakiecie jako anty-wzorzec" (odrzucony PEP-3122)

Spędziłem tyle czasu próbując znaleźć rozwiązanie, czytając powiązane posty tutaj na Stack Overflow i mówiąc sobie "musi być lepszy sposób!". Wygląda na to, że nie ma.

 42
Author: lesnik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-26 20:14:16
def import_path(fullpath):
    """ 
    Import a file with full path specification. Allows one to
    import from anywhere, something __import__ does not do. 
    """
    path, filename = os.path.split(fullpath)
    filename, ext = os.path.splitext(filename)
    sys.path.append(path)
    module = __import__(filename)
    reload(module) # Might be out of date
    del sys.path[-1]
    return module

Używam tego fragmentu do importowania modułów ze ścieżek, mam nadzieję, że to pomoże

 24
Author: iElectric,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-22 19:18:28

To jest rozwiązane w 100%:

  • app/
    • main.py
  • Ustawienia/
    • local_setings.py

Import settings/local_setting.py w app/main.py:

Main.py:

import sys
sys.path.insert(0, "../settings")


try:
    from local_settings import *
except ImportError:
    print('No Import')
 24
Author: Роман Арсеньев,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-05 00:28:07

Wyjaśnienie odpowiedzi nosklo's z przykładami

uwaga: wszystkie pliki __init__.py są puste.

main.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       fun_a.py
    package_b/ ->
       __init__.py
       fun_b.py

App/package_a/fun_a.py

def print_a():
    print 'This is a function in dir package_a'

App/package_b/fun_b.py

from app.package_a.fun_a import print_a
def print_b():
    print 'This is a function in dir package_b'
    print 'going to call a function in dir package_a'
    print '-'*30
    print_a()

Main.py

from app.package_b import fun_b
fun_b.print_b()

Jeśli uruchomisz $ python main.py zwraca:

This is a function in dir package_b
going to call a function in dir package_a
------------------------------
This is a function in dir package_a
  • main.py czy: from app.package_b import fun_b
  • fun_b.py czy from app.package_a.fun_a import print_a

Więc plik w folderze package_b używany plik w folderze package_a, czyli to, co chcesz. Prawda??

 19
Author: simple_human,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-08 03:19:42

To jest niestety sys.Path hack, ale działa całkiem dobrze.

Napotkałem ten problem z inną warstwą: miałem już moduł o podanej nazwie, ale był to zły moduł.

To, co chciałem zrobić ,było następujące (moduł, z którego pracowałem, to module3):

mymodule\
   __init__.py
   mymodule1\
      __init__.py
      mymodule1_1
   mymodule2\
      __init__.py
      mymodule2_1


import mymodule.mymodule1.mymodule1_1  

Zauważ, że mam już zainstalowany mymodule, ale w mojej instalacji nie mam "mymodule1"

I dostałbym ImportError, ponieważ próbował zaimportować z mojego zainstalowane moduły.

Próbowałem zrobić sys./ align = "left" / / align = "left" / To, co działało, to sys./ align = "left" / insert
if __name__ == '__main__':
    sys.path.insert(0, '../..')

To rodzaj włamania, ale wszystko działa! Pamiętaj więc, że jeśli chcesz, aby twoja decyzja nadpisała inne ścieżki , musisz użyć sys./ align = "left" / insert (0, pathname) to get it to work! To był bardzo frustrujący punkt dla mnie, allot ludzi mówią, aby użyć funkcji "dołącz" do sys.ścieżka, ale to nie działa, jeśli już masz moduł zdefiniowany (uważam to za bardzo dziwne zachowanie)

 11
Author: Garrett Berg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-05 17:10:18

Pozwól, że umieszczę to tutaj dla własnego odniesienia. Wiem, że nie jest to dobry Kod Pythona, ale potrzebowałem skryptu do projektu, nad którym pracowałem i chciałem umieścić skrypt w katalogu scripts.

import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
 9
Author: milkypostman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-07 04:26:52

Jak pisze @EvgeniSergeev w komentarzach do OP, możesz zaimportować kod z pliku .py W dowolnej lokalizacji za pomocą:

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

To jest wzięte z to więc odpowiedz .

 8
Author: LondonRob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:11
 5
Author: mossplix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-25 09:39:50

From Python doc ,

W Pythonie 2.5 można zmienić zachowanie importu na import bezwzględny za pomocą dyrektywy from __future__ import absolute_import. To zachowanie importu bezwzględnego stanie się domyślne w przyszłej wersji (prawdopodobnie Python 2.7). Gdy import bezwzględny jest domyślny, import string zawsze znajdzie wersję biblioteki standardowej. Sugeruje się, że użytkownicy powinni zacząć używać importu bezwzględnego jak najwięcej, więc lepiej jest zacząć pisać from pkg import string w kodzie

 2
Author: jung rhew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-26 19:44:28

Znalazłem, że łatwiej jest ustawić zmienną środowiska "PYTHONPATH"w górnym folderze:

bash$ export PYTHONPATH=/PATH/TO/APP

Wtedy:

import sub1.func1
#...more import

Oczywiście, PYTHONPATH jest "globalny", ale to jeszcze nie przysporzyło mi kłopotów.

 1
Author: Andrew_1510,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-17 09:20:09

Poza tym, co powiedział John B, wydaje się, że ustawienie zmiennej __package__ powinno pomóc, zamiast zmieniać __main__, co może zepsuć inne rzeczy. Ale o ile mogę przetestować, to nie działa całkowicie tak, jak powinno.

Mam ten sam problem i ani PEP 328 ani 366 nie rozwiązują problemu całkowicie, ponieważ oba, pod koniec dnia, muszą być włączone do pakietu sys.path, o ile mogę zrozumieć.

Powinienem również wspomnieć, że nie znalazłem, jak sformatować string, który powinien przejść do tych zmiennych. To jest "package_head.subfolder.module_name" czy co?

 1
Author: Gabriel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-22 04:35:16

Załóżmy, że biegniesz na najwyższym poziomie, a następnie w mod1 Użyj:

import sub2.mod2 

Zamiast

from ..sub2 import mod2
 -5
Author: lukmac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-29 08:33:02

Myślę, że to, co musisz sobie zadać, to:

    Dlaczego muszę to zrobić?
  • czy separacja pakietów jest dobra?
Nie znam kontekstu, dlaczego chcesz to zrobić w ten sposób. Ale dla mnie czystszym projektem byłoby mieć następującą strukturę pakietów:
app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
      sub12/
           __init__.py
           mod2.py

Wtedy wystarczy:

from sub12 import mod2
 -8
Author: Lucas S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-26 20:47:11