Come importare una classe Python che si trova in una directory sopra?


213

Voglio ereditare da una classe in un file che si trova in una directory sopra quella corrente.

È possibile importare relativamente quel file?

Risposte:


177

from ..subpkg2 import mod

Secondo i documenti Python: quando sei all'interno di una gerarchia di pacchetti, usa due punti, come dice il documento dell'istruzione import :

Quando si specifica quale modulo importare non è necessario specificare il nome assoluto del modulo. Quando un modulo o un pacchetto è contenuto in un altro pacchetto, è possibile effettuare un'importazione relativa all'interno dello stesso pacchetto principale senza dover menzionare il nome del pacchetto. Utilizzando i punti iniziali nel modulo o pacchetto fromspecificato dopo è possibile specificare quanto in alto attraversare la gerarchia del pacchetto corrente senza specificare i nomi esatti. Un punto iniziale indica il pacchetto corrente in cui esiste il modulo che effettua l'importazione. Due punti indicano un livello di pacchetto superiore . Tre punti sono su due livelli, ecc. Quindi, se esegui from . import modda un modulo nel pkgpacchetto, finirai per importare pkg.mod. Se esegui from ..subpkg2 import modda dentro pkg.subpkg1, importeraipkg.subpkg2.mod. Le specifiche per le importazioni relative sono contenute nel PEP 328 .

Il PEP 328 riguarda le importazioni assolute / relative.


4
up1 = os.path.abspath ('..') sys.path.insert (0, up1)
rp.

Pep 328 mostra solo la versione Python: 2.4, 2,5, 2.6. La versione 3 è lasciata ad anime più esperte.
Gimel

22
Questo innesca l'errore ValueError: tentativo di importazione relativa oltre il pacchetto di livello superiore
Carlo

4
Risultati in ImportError: tentativo di importazione relativa senza pacchetto parent noto
Rotkiv

116
import sys
sys.path.append("..") # Adds higher directory to python modules path.

1
questo funziona per me. Dopo aver aggiunto questo, posso importare direttamente i moduli principali, senza bisogno di usare "..".
Evan Hu,

8
funziona solo in termini approssimativi se il valore PWD dell'applicazione, ovvero il suo attuale directeroy, è figlio del genitore. Quindi, anche se funziona, molti tipi di cambiamenti sistemici possono rimuoverlo.
Phlip

Questo ha funzionato anche per me per l'importazione di moduli di livello superiore. Lo uso con os.chdir ("..") per caricare altri file di livello superiore.
Surendra Shrestha,

Questo sembra innescare lo stesso errore della risposta precedente di gimel.
Carlo,

81

La risposta di @ gimel è corretta se puoi garantire la gerarchia del pacchetto che menziona. Se non puoi - se il tuo reale bisogno è come lo hai espresso, legato esclusivamente alle directory e senza alcuna relazione necessaria con il packaging - allora devi lavorare su __file__per scoprire la directory principale (un paio di os.path.dirnamechiamate lo faranno; -), quindi (se tale directory non è già acceso sys.path) prepend temporaneamente inserto detto dir proprio all'inizio del sys.path, __import__, allontanarli dir di nuovo - il lavoro disordinato in effetti, ma, "quando è necessario, è necessario" (e si impegna a Pyhon non fermare mai il programmatore dal fare ciò che deve essere fatto - proprio come dice lo standard ISO C nella sezione "Spirito di C" nella sua prefazione! -).

Ecco un esempio che potrebbe funzionare per te:

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

2
questo potrebbe aggiungere una directory all'interno di un pacchetto Python per sys.pathrendere così disponibile lo stesso modulo con nomi diversi e tutti i bug corrispondenti. autopath.py in pypy o _preamble.py in twisted risolvilo usando un criterio di ricerca che identifichi il pacchetto di livello superiore attraversando le directory verso l'alto.
jfs,

4
Potresti voler fare qualcosa di simile sys.path.remove(pathYouJustAdded)dopo l'importazione che ti serviva per non conservare questo nuovo percorso.
Matthias,

35

Importa il modulo da una directory che si trova esattamente a un livello sopra la directory corrente:

from .. import module

38
Ho ottenuto: tentativo di importazione relativa oltre il pacchetto di livello superiore :(
RicardoE

25
utilizzando from .. import module ho ottenuto l'errore ValueError: Tentativo di importazione relativa in non pacchetto seguendo la raccomandazione
user3428154

4

Come caricare un modulo che è una directory

prefazione: ho fatto una sostanziale riscrittura di una precedente risposta con la speranza di aiutare le persone a entrare nell'ecosistema di Python e, si spera, dare a tutti il ​​miglior cambiamento di successo con il sistema di importazione di Python.

Questo riguarderà le importazioni relative all'interno di un pacchetto , che ritengo sia il caso più probabile alla domanda di OP.

Python è un sistema modulare

Questo è il motivo per cui scriviamo import fooper caricare un modulo "pippo" dallo spazio dei nomi di root, invece di scrivere:

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 non è accoppiato a un file system

Questo è il motivo per cui possiamo incorporare Python in un ambiente in cui non esiste un filesystem defacto senza fornirne uno virtuale, come Jython.

Essendo disaccoppiato da un filesystem consente l'importazione è flessibile, questo design consente di importare da file di archivio / zip, import singleton, caching bytecode, estensioni cffi, persino il caricamento della definizione del codice remoto.

Quindi se le importazioni non sono accoppiate a un filesystem cosa significa "una directory in su"? Dobbiamo scegliere alcune euristiche, ma possiamo farlo, ad esempio quando si lavora all'interno di un pacchetto , sono già state definite alcune euristiche che rendono simili le importazioni relative .fooe..foo funzionano all'interno dello stesso pacchetto. Freddo!

Se desideri sinceramente associare i tuoi schemi di caricamento del codice sorgente a un filesystem, puoi farlo. Dovrai scegliere la tua euristica e utilizzare una sorta di macchinari di importazione, mi raccomando importlib

L'esempio importlib di Python è simile al seguente:

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

Confezione

C'è un grande esempio di progetto disponibile ufficialmente qui: https://github.com/pypa/sampleproject

Un pacchetto python è una raccolta di informazioni sul codice sorgente, che può informare altri strumenti su come copiare il codice sorgente su altri computer e su come integrare il codice sorgente nel percorso di quel sistema in modo che funzioni import fooper altri computer (indipendentemente dall'interprete, sistema operativo host, ecc.)

Struttura delle directory

Consente di avere un nome di pacchetto foo, in alcune directory (preferibilmente una directory vuota).

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

La mia preferenza è creare setup.pycome fratello foo.py, perché semplifica la scrittura del file setup.py, tuttavia è possibile scrivere la configurazione per modificare / reindirizzare tutto ciò che fa setuptools, se lo si desidera; per esempio mettere foo.pyin una directory "src /" è piuttosto popolare, non trattato qui.

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/

"editable" aka -ereindirizzerà ancora una volta i macchinari di importazione per caricare i file di origine in questa directory, copiando invece i file esatti correnti nella libreria dell'ambiente di installazione. Ciò può anche causare differenze comportamentali sulla macchina di uno sviluppatore, assicurati di testare il tuo codice! Ci sono strumenti diversi da pip, tuttavia consiglierei pip di essere quello introduttivo :)

Mi piace anche creare fooun "pacchetto" (una directory contenente __init__.py) anziché un modulo (un singolo file ".py"), sia i "pacchetti" che i "moduli" possono essere caricati nello spazio dei nomi radice, i moduli consentono spazi dei nomi nidificati, che è utile se vogliamo avere un'importazione "relativa a una directory in su".

some_directory/
    foo/
        __init__.py
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
)

Mi piace anche fare un foo/__main__.py, questo permette a Python di eseguire il pacchetto come modulo, ad esempio python3 -m fooeseguirà foo/__main__.pycome __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',
        ]
    },
)

Diamo un'occhiata a questo con altri moduli: In pratica, puoi avere una struttura di directory in questo modo:

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 contiene convenzionalmente informazioni sui metadati sul codice sorgente all'interno, come:

  • quali dipendenze sono necessarie per l'installazione denominata "install_requires"
  • quale nome dovrebbe essere usato per la gestione dei pacchetti (installa / disinstalla "nome"), suggerisco che corrisponda al nome del pacchetto python principale nel nostro caso foo , anche se la sostituzione dei trattini bassi con trattini è popolare
  • informazioni sulla licenza
  • tag di maturità (alfa / beta / ecc.),
  • tag del pubblico (per sviluppatori, per l'apprendimento automatico, ecc.),
  • contenuto della documentazione a pagina singola (come un file README),
  • nomi di shell (nomi digitati nella shell dell'utente come bash o nomi che trovi in ​​una shell utente grafica come un menu di avvio),
  • un elenco di moduli Python che questo pacchetto installerà (e disinstallerà)
  • un punto di entrata "esegui test" defacto python ./setup.py test

È molto espansivo, può anche compilare estensioni c al volo se un modulo sorgente viene installato su una macchina di sviluppo. Per un esempio quotidiano, consiglio setup.py di PYPA Sample Repository

Se stai rilasciando un artefatto di compilazione, ad esempio una copia del codice che è destinato a eseguire computer quasi identici, un file requisito.txt è un modo popolare per acquisire istantaneamente informazioni esatte sulla dipendenza, dove "install_requires" è un buon modo per acquisire il minimo e versioni massime compatibili. Tuttavia, dato che le macchine target sono quasi identiche, consiglio vivamente di creare un tarball di un intero prefisso python. Questo può essere complicato, troppo dettagliato per entrare qui. Scopri pip install's --targetopzione, o virtualenv aka venv per i cavi.

torna all'esempio

come importare un file una directory su:

Da foo / spam / eggs.py, se volessimo il codice da foo / baz, potremmo richiederlo dal suo spazio dei nomi assoluto:

import foo.baz

Se volessimo riservare la capacità di spostare eggs.py in qualche altra directory in futuro con qualche altra bazimplementazione relativa , potremmo usare un'importazione relativa come:

import ..baz

-5

Python è un sistema modulare

Python non si basa su un file system

Per caricare il codice python in modo affidabile, disporre di quel codice in un modulo e quel modulo installato nella libreria di python.

I moduli installati possono sempre essere caricati dallo spazio dei nomi di livello superiore con import <name>


C'è un grande progetto di esempio disponibile ufficialmente qui: https://github.com/pypa/sampleproject

Fondamentalmente, puoi avere una struttura di directory in questo modo:

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.

.

Assicurati di dichiarare il tuo setuptools.setup()in setup.py,

esempio ufficiale: https://github.com/pypa/sampleproject/blob/master/setup.py

Nel nostro caso probabilmente vogliamo esportare bar.pyefoo/__init__.py , il mio breve esempio:

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
)

.

Ora possiamo installare il nostro modulo nella libreria python; con pip, puoi installarlo the_foo_projectnella tua libreria python in modalità modifica, così possiamo lavorarci su in tempo reale

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

.

Ora da qualsiasi contesto di Python, possiamo caricare i nostri py_modules e pacchetti condivisi

foo_script.py

#!/usr/bin/env python3

import bar
import foo

print(dir(bar))
print(dir(foo))

2
dovrei menzionare che installo sempre il mio modulo mentre ci lavoro pip install --edit foo, quasi sempre all'interno di un virtualenv. Non scrivo quasi mai un modulo che non è destinato all'installazione. se fraintendessi qualcosa che vorrei sapere.
ThorSummoner,

Vorrei anche menzionare che l'uso di una suite di test di creazione automatica di venv, come tox, è molto utile in quanto editablei collegamenti uovo e i moduli python installati non sono esattamente gli stessi in tutti i modi; ad esempio, l'aggiunta di un nuovo spazio dei nomi a un modulo modificabile si troverà nella ricerca del percorso, ma se non viene esportato nel file setup.py non verrà impacchettato / installato! Prova il tuo caso d'uso :)
ThorSummoner l'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.