2015-12-27 18 views
36

Ho una struttura di directory simile al seguenteimportazione funzione locale da un modulo ospitato in un'altra directory con le importazioni relative a notebook jupyter utilizzando python3

meta_project 
    project1 
     __init__.py 
     lib 
      module.py 
      __init__.py 
    notebook_folder 
     notebook.jpynb 

Quando si lavora in notebook.jpynb se cerco di usare una di importazione rispetto a accedere ad una funzione function() in module.py con:

from ..project1.lib.module import function 

ottengo il seguente errore

SystemError        Traceback (most recent call last) 
<ipython-input-7-6393744d93ab> in <module>() 
----> 1 from ..project1.lib.module import function 

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

C'è un modo per farlo funzionare utilizzando le importazioni relative?

Nota: il server del notebook viene istanziato al livello della directory meta_project, quindi dovrebbe avere accesso alle informazioni in tali file.

Nota, inoltre, che almeno come originariamente previsto project1 non è stato pensato come un modulo e quindi non ha un file __init__.py, era solo inteso come una directory del file system. Se la soluzione al problema richiede di trattarlo come un modulo e includere un file __init__.py (anche uno vuoto) va bene, ma farlo non è sufficiente per risolvere il problema.

Condivido questa directory tra le macchine e le importazioni relative mi consentono di utilizzare lo stesso codice ovunque, & Spesso utilizzo i taccuini per la prototipazione rapida, quindi è improbabile che suggerimenti che coinvolgono l'hacking di percorsi assoluti siano utili.


Edit: Questo è diverso da Relative imports in Python 3, che parla di importazioni relative a Python 3 in generale e - in particolare - esecuzione di uno script all'interno di una directory del pacchetto. Questo ha a che fare con il lavoro all'interno di un taccuino jupyter che tenta di chiamare una funzione in un modulo locale in un'altra directory che ha sia diversi aspetti generali che particolari.

+1

c'è qualche 'file __init__' nella directory pacchetto? –

+0

Sì, nella directory 'lib'. – mpacer

+0

Per favore, menzionalo nella struttura della tua directory nella tua domanda –

risposta

52

Avevo quasi lo stesso esempio di this notebook in cui volevo illustrare l'utilizzo della funzione di un modulo adiacente in modo ASCIUTTO.

La mia soluzione era quella di dire a Python di quella ulteriore percorso modulo di importazione con l'aggiunta di un frammento di come questo per il notebook:

import os 
import sys 
module_path = os.path.abspath(os.path.join('..')) 
if module_path not in sys.path: 
    sys.path.append(module_path) 

Questo consente di importare la funzione desiderata dalla gerarchia modulo:

from project1.lib.module import function 
# use the function normally 
function(...) 

si noti che è necessario aggiungere vuoti __init__.py file project1/ e lib/ cartelle se non si dispone di loro alrea dy.

+4

Questo risolve il problema di poter importare un pacchetto usando ciò che è più o meno una posizione relativa, ma solo indirettamente . Mi capita di conoscere Matthias Bussonier (@matt su SE) e Yuvi Panda (@yuvi su SE) stanno sviluppando https://github.com/ipython/ipynb che affronteranno questo più direttamente (ad esempio, consentendo le importazioni relative utilizzando lo standard sintassi una volta che il loro pacchetto è stato importato). Accetterò la tua risposta per ora, e quando la loro soluzione sarà completamente pronta per altri utenti probabilmente scriverò una risposta su come usarla, o chiederò a una di loro di farlo. – mpacer

+0

grazie per aver segnalato il vuoto __init__.py Sono un novizio pitone e stavo avendo problemi a importare le mie classi. Stavo ricevendo errori nella nota del modulo, l'aggiunta di __init__.py vuoto ha risolto il problema! –

3

È venuto qui alla ricerca di best practice per l'astrazione del codice per i sottomoduli quando si lavora nei notebook. Non sono sicuro che ci sia una buona pratica. Ho proposto questo.

Una gerarchia progetto in quanto tale:

├── ipynb 
│   ├── 20170609-Examine_Database_Requirements.ipynb 
│   └── 20170609-Initial_Database_Connection.ipynb 
└── lib 
    ├── __init__.py 
    └── postgres.py 

E da 20170609-Initial_Database_Connection.ipynb:

In [1]: cd .. 

    In [2]: from lib.postgres import database_connection 

Questo funziona perché per impostazione predefinita il Notebook Jupyter in grado di analizzare il comando cd. Nota che questo non fa uso della magia di Notebook Python. Funziona semplicemente senza prepagare %bash.

Considerando che 99 volte su 100 Sto lavorando in Docker utilizzando uno dei Project Jupyter Docker images, la seguente modifica è idempotent

In [1]: cd /home/jovyan 

    In [2]: from lib.postgres import database_connection 
+0

Grazie. Davvero orribili le restrizioni di queste importazioni relative. – Michael

+0

Anche io uso 'chdir' piuttosto che aggiungere al percorso, dal momento che sono entrambi interessato all'importazione dal repository principale e all'interfacciamento con alcuni file. – TheGrimmScientist

+0

Purtroppo, la cosa più compromessa che faccio in Python. Tuttavia, non riesco a trovare una soluzione migliore. – TheGrimmScientist