Voglio ereditare da una classe in un file che si trova in una directory sopra quella corrente.
È possibile importare relativamente quel file?
Voglio ereditare da una classe in un file che si trova in una directory sopra quella corrente.
È possibile importare relativamente quel file?
Risposte:
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
from
specificato 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 eseguifrom . import mod
da un modulo nelpkg
pacchetto, finirai per importarepkg.mod
. Se eseguifrom ..subpkg2 import mod
da dentropkg.subpkg1
, importeraipkg.subpkg2.mod
. Le specifiche per le importazioni relative sono contenute nel PEP 328 .
Il PEP 328 riguarda le importazioni assolute / relative.
import sys
sys.path.append("..") # Adds higher directory to python modules path.
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.dirname
chiamate 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
sys.path
rendere 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.
sys.path.remove(pathYouJustAdded)
dopo l'importazione che ti serviva per non conservare questo nuovo percorso.
Importa il modulo da una directory che si trova esattamente a un livello sopra la directory corrente:
from .. import module
from .. import module
ho ottenuto l'errore ValueError: Tentativo di importazione relativa in non pacchetto seguendo la raccomandazione
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.
Questo è il motivo per cui scriviamo import foo
per 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
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 .foo
e..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
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 foo
per altri computer (indipendentemente dall'interprete, sistema operativo host, ecc.)
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.py
come 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.py
in 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 -e
reindirizzerà 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 foo
un "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 foo
eseguirà foo/__main__.py
come __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:
foo
, anche se la sostituzione dei trattini bassi con trattini è popolarepython ./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 --target
opzione, o virtualenv aka venv per i cavi.
torna all'esempio
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 baz
implementazione relativa , potremmo usare un'importazione relativa come:
import ..baz
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.py
efoo/__init__.py
, il mio breve esempio:
#!/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_project
nella 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
#!/usr/bin/env python3
import bar
import foo
print(dir(bar))
print(dir(foo))
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.
editable
i 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 :)