Notebooks importieren#
Um modularer entwickeln zu können, ist der Import von Notebooks erforderlich. Da Notebooks jedoch keine Python-Dateien sind, lassen sie sich auch nicht so einfach importieren. Glücklicherweise stellt Python einige Hooks für den Import bereit, sodass IPython-Notebooks schließlich doch importiert werden können.
[1]:
import os, sys, types
[2]:
import nbformat
from IPython import get_ipython
from IPython.core.interactiveshell import InteractiveShell
Import-Hooks haben normalerweise zwei Objekte:
Module Loader, der einen Modulnamen (z. B.
IPython.display
) annimmt und ein Modul zurückgibtModule Finder, der herausfindet, ob ein Modul vorhanden ist, und Python mitteilt, welcher Loader verwendet werden soll
Zunächst jedoch schreiben wir eine Methode, die ein Notebook anhand des vollständig qualifizierten Namen und des optionalen Pfades findet. So wird z.B. aus mypackage.foo
mypackage/foo.ipynb
und ersetzt Foo_Bar
durch Foo Bar
, wenn Foo_Bar
nicht existiert.
[3]:
def find_notebook(fullname, path=None):
name = fullname.rsplit('.', 1)[-1]
if not path:
path = ['']
for d in path:
nb_path = os.path.join(d, name + ".ipynb")
if os.path.isfile(nb_path):
return nb_path
# let import Foo_Bar find "Foo Bar.ipynb"
nb_path = nb_path.replace("_", " ")
if os.path.isfile(nb_path):
return nb_path
Notebook Loader#
Der Notebook Loader führt die folgenden drei Schritte aus:
Laden des Notebook-Dokuments in den Speicher
Erstellen eines leeren Moduls
Ausführen jeder Zelle im Modul-Namensraum
Da IPython-Zellen eine erweiterte Syntax haben können, wird mit
transform_cell
jede Zelle in reinen Python-Code umgewandelt, bevor er ausgeführt wird.
[4]:
class NotebookLoader(object):
"""Module Loader for IPython Notebooks"""
def __init__(self, path=None):
self.shell = InteractiveShell.instance()
self.path = path
def load_module(self, fullname):
"""import a notebook as a module"""
path = find_notebook(fullname, self.path)
print ("importing notebook from %s" % path)
# load the notebook object
nb = nbformat.read(path, as_version=4)
# create the module and add it to sys.modules
# if name in sys.modules:
# return sys.modules[name]
mod = types.ModuleType(fullname)
mod.__file__ = path
mod.__loader__ = self
mod.__dict__['get_ipython'] = get_ipython
sys.modules[fullname] = mod
# extra work to ensure that magics that would affect the user_ns
# magics that would affect the user_ns actually affect the
# notebook module’s ns
save_user_ns = self.shell.user_ns
self.shell.user_ns = mod.__dict__
try:
for cell in nb.cells:
if cell.cell_type == 'code':
# transform the input to executable Python
code = self.shell.input_transformer_manager.transform_cell(cell.source)
# run the code in the module
exec(code, mod.__dict__)
finally:
self.shell.user_ns = save_user_ns
return mod
Notebook Finder#
Der Finder ist ein einfaches Objekt, das angibt, ob ein Notebook anhand seines Dateinamens importiert werden kann, und das den entsprechenden Loader zurückgibt.
[5]:
class NotebookFinder(object):
"""Module Finder finds the transformed IPython Notebook"""
def __init__(self):
self.loaders = {}
def find_module(self, fullname, path=None):
nb_path = find_notebook(fullname, path)
if not nb_path:
return
key = path
if path:
# lists aren’t hashable
key = os.path.sep.join(path)
if key not in self.loaders:
self.loaders[key] = NotebookLoader(path)
return self.loaders[key]
Hook registrieren#
Jetzt registrieren wir NotebookFinder
mit sys.meta_path
:
[6]:
sys.meta_path.append(NotebookFinder())
Überprüfen#
Nun sollte unser Notebook mypackage/foo.ipynb importierbar sein mit
[7]:
from mypackage import foo
importing notebook from /Users/veit/jupyter-tutorial/docs/basics/ipython/mypackage/foo.ipynb
Wird die Python-Methode bar
ausgeführt?
[8]:
foo.bar()
[8]:
'bar'
… und die IPython-Syntax?
[9]:
foo.has_ip_syntax()
[9]:
['debugging.ipynb',
'display.ipynb',
'importing.ipynb',
'index.rst',
'magics.ipynb',
'mprun_demo.py',
'mypackage',
'myscript.py',
'profiling.ipynb',
'shell.ipynb',
'show.ipynb',
'start.rst']
Wiederverwendbarer Import-Hook#
Der Import-Hook kann auch einfach in anderen Notebooks ausgeführt werden mit
[10]:
%run importing.ipynb
importing notebook from /Users/veit/jupyter-tutorial/docs/basics/ipython/mypackage/foo.ipynb