it-swarm-eu.dev

Jak provádět relativní dovozy v Pythonu?

Představte si tuto strukturu adresářů:

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

Jsem kódování mod1, a musím něco importovat z mod2. Jak to mám udělat?

Vyzkoušel jsem from ..sub2 import mod2, ale dostávám „Pokus o relativní import v nebalení“.

Rozhlédl jsem se, ale našel jsem jen hackery „sys.path“. Neexistuje čistý způsob?


Úpravy: všechny mé __init__.py jsou aktuálně prázdné

Edit2: Snažím se o to, protože sub2 obsahuje třídy, které jsou sdíleny mezi sub balíčky (sub1, subX atd.).

Edit3: Chování, které hledám, je stejné jako v PEP 366 (díky Johnu B)

487
Joril

Zdá se, že vám každý chce říct, co byste měli dělat, spíše než jen odpovědět na otázku.

Problém je v tom, že modul používáte jako „__main__“ předáním mod1.py jako argumentu tlumočníkovi.

Od PEP 328 :

Relativní importy používají atribut __modulu k určení pozice tohoto modulu v hierarchii balíčku. Pokud název modulu neobsahuje žádné informace o balíčku (např. Je nastaven na „__main__“), budou relativní dovozy vyřešeny, jako by se jednalo o modul nejvyšší úrovně, bez ohledu na to, kde je modul skutečně umístěn v systému souborů.

V Python 2.6 přidávají možnost odkazovat na moduly vzhledem k hlavnímu modulu. PEP 366 popisuje změnu.

Aktualizace : Podle Nicka Coghlana je doporučenou alternativou spustit modul uvnitř balíčku pomocí přepínače -m.

306
John B
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. Spustíte python main.py.
  2. main.py dělá: import app.package_a.module_a
  3. module_a.py dělá import app.package_b.module_b

Alternativně lze použít 2 nebo 3: from app.package_a import module_a

To bude fungovat, pokud máte ve svém PYTHONPATH app. main.py by pak mohl být kdekoli.

Takže napíšete setup.py ke zkopírování (instalaci) celého balíčku aplikace a dílčích balíčků do složek python cílového systému a main.py do cílových složek skriptu systému.

116
nosklo

Zde je řešení, které pro mě funguje:

Relativní import provádím jako from ..sub2 import mod2 a pak, pokud chci spustit mod1.py, pak jdu do nadřazeného adresáře app a spusťte modul pomocí přepínače python -m jako python -m app.sub1.mod1.

Skutečným důvodem, proč k tomuto problému dochází při relativním importu, je to, že relativní import funguje tak, že vezme vlastnost __name__ modulu. Pokud je modul spuštěn přímo, je __name__ nastaveno na __main__ a neobsahuje žádné informace o struktuře balíčků. A proto si python stěžuje na chybu relative import in non-package.

Takže pomocí přepínače -m poskytnete informace o struktuře balíčku pythonu, díky kterému může úspěšně vyřešit relativní import.

S tímto problémem jsem se setkal mnohokrát při relativním importu. A po přečtení všech předchozích odpovědí jsem stále nebyl schopen přijít na to, jak to vyřešit, čistým způsobem, aniž bych musel vkládat kód kotlové desky do všech souborů. (Ačkoli některé komentáře byly opravdu užitečné, díky @ncoghlan a @XiongChiamiov)

Doufám, že to pomůže někomu, kdo bojuje s problémem relativního importu, protože procházení PEP není opravdu zábavné.

114
Pankaj

"Guido prohlíží spuštěné skripty v balíčku jako anti-pattern" (odmítnuto PEP-3122 )

Strávil jsem tolik času hledáním řešení, četl jsem související příspěvky zde na Stack Overflow a říkal jsem si „musí existovat lepší způsob!“. Vypadá to, že není.

46
lesnik

To je vyřešeno 100%:

  • aplikace/
    • main.py
  • nastavení /
    • local_setings.py

Import nastavení/local_setting.py do aplikace/main.py:

main.py:

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


try:
    from local_settings import *
except ImportError:
    print('No Import')
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

Používám tento fragment k importu modulů z cest, doufám, že to pomůže

24
iElectric

vysvětlení odpovědi nosklo's s příklady

poznámka: všechny soubory __init__.py jsou prázdné.

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()

pokud spustíte $ python main.py, vrátí se:

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 dělá: from app.package_b import fun_b
  • fun_b.py dělá from app.package_a.fun_a import print_a

takže soubor ve složce package_b používá soubor ve složce package_a, což je to, co chcete. Že jo??

21
suhailvs

Toto je bohužel hacker sys.path, ale funguje to docela dobře.

S tímto problémem jsem se setkal v jiné vrstvě: už jsem měl modul zadaného názvu, ale byl to špatný modul.

co jsem chtěl udělat, bylo následující (modul, ze kterého jsem pracoval, byl modul3):

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


import mymodule.mymodule1.mymodule1_1  

Všimněte si, že jsem již nainstaloval mymodule, ale v mé instalaci nemám "mymodule1"

a dostal bych ImportError, protože se pokoušel importovat z mých nainstalovaných modulů.

Snažil jsem se udělat sys.path.append, a to nefungovalo. Co fungovalo, bylo sys.path.insert

if __== '__main__':
    sys.path.insert(0, '../..')

Tak trochu hack, ale všechno to fungovalo! Takže mějte na paměti, že pokud chcete, aby vaše rozhodnutí přepsat jiné cesty, musíte použít sys.path.insert (0, cesta), aby to fungovalo! Bylo to pro mě velmi frustrující naléhavé místo, přidělit lidi, kteří říkají, že používají funkci „append“ k sys.path, ale to nefunguje, pokud již máte definovaný modul (považuji to za velmi podivné chování)

11
Garrett Berg

Dovolte mi to jen zde pro vlastní potřebu. Vím, že to není dobrý Python kód, ale potřeboval jsem skript pro projekt, na kterém jsem pracoval, a chtěl jsem skript umístit do adresáře scripts.

import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
10
milkypostman

Jak říká @EvgeniSergeev v komentářích k OP, můžete importovat kód ze souboru .py na libovolném místě pomocí:

import imp

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

Toto je převzato z tato SO odpověď .

8
LondonRob

Podívejte se na http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports . Můžeš to udělat

from .mod1 import stuff
4
mossplix

Od Python doc ,

V Python 2.5 můžete pomocí direktivy from __future__ import absolute_import přepnout chování importu na absolutní import. Toto chování při absolutním importu se stane výchozí v budoucí verzi (pravděpodobně Python 2.7). Jakmile jsou výchozí importy absolutní, import string vždy najde verzi standardní knihovny. Doporučuje se, aby uživatelé začali používat absolutní import v co největší míře, takže je lepší začít psát from pkg import string do vašeho kódu

2
jung rhew

Kromě toho, co řekl John B, se zdá, že by mělo pomoci nastavení proměnné __package__, namísto změny __main__, která by mohla zničit další věci. Ale pokud jsem mohl vyzkoušet, nefunguje to úplně tak, jak má.

Mám stejný problém a ani PEP 328 nebo 366 problém nevyřeší úplně, protože oba do konce dne potřebují, aby byla hlava balíčku zahrnuta do sys.path, pokud jsem to pochopil.

Také bych měl zmínit, že jsem nenašel, jak formátovat řetězec, který by měl jít do těchto proměnných. Je to "package_head.subfolder.module_name" nebo co?

1
Gabriel

Zjistil jsem, že je snadnější nastavit proměnnou prostředí „PYTHONPATH“ do horní složky:

bash$ export PYTHONPATH=/PATH/TO/APP

pak:

import sub1.func1
#...more import

pYTHONPATH je samozřejmě „globální“, ale pro mě to ještě nezpůsobilo potíže.

1
Andrew_1510