重要
本文档涵盖 IPython 6.0 及更高版本。从 6.0 版本开始,IPython 不再支持与低于 3.3 的 Python 版本(包括所有版本的 Python 2.7)的兼容性。
如果您正在寻找与 Python 2.7 兼容的 IPython 版本,请使用 IPython 5.x LTS 版本并参考其文档(LTS 是长期支持版本)。
定义自定义魔术¶
定义您自己的魔术函数主要有两种方法:从独立函数和通过继承 IPython 提供的基类:IPython.core.magic.Magics
。下面我们展示了可以放置在从配置加载的文件中的代码,例如默认 IPython 配置文件的 startup
子目录中的任何文件。
首先,让我们看看最简单的案例。以下内容展示了如何仅使用普通函数创建行魔术、单元格魔术和同时适用于两种模式的魔术
from IPython.core.magic import (register_line_magic, register_cell_magic,
register_line_cell_magic)
@register_line_magic
def lmagic(line):
"my line magic"
return line
@register_cell_magic
def cmagic(line, cell):
"my cell magic"
return line, cell
@register_line_cell_magic
def lcmagic(line, cell=None):
"Magic that works both as %lcmagic and as %%lcmagic"
if cell is None:
print("Called as line magic")
return line
else:
print("Called as cell magic")
return line, cell
# In an interactive session, we need to delete these to avoid
# name conflicts for automagic to work on line magics.
del lmagic, lcmagic
您还可以通过继承 IPython.core.magic.Magics
类来创建所有三种类型的魔术。这使您可以创建可能在调用之间保持状态并完全访问主 IPython 对象的魔术
# This code can be put in any Python module, it does not require IPython
# itself to be running already. It only creates the magics subclass but
# doesn't instantiate it yet.
from __future__ import print_function
from IPython.core.magic import (Magics, magics_class, line_magic,
cell_magic, line_cell_magic)
# The class MUST call this class decorator at creation time
@magics_class
class MyMagics(Magics):
@line_magic
def lmagic(self, line):
"my line magic"
print("Full access to the main IPython object:", self.shell)
print("Variables in the user namespace:", list(self.shell.user_ns.keys()))
return line
@cell_magic
def cmagic(self, line, cell):
"my cell magic"
return line, cell
@line_cell_magic
def lcmagic(self, line, cell=None):
"Magic that works both as %lcmagic and as %%lcmagic"
if cell is None:
print("Called as line magic")
return line
else:
print("Called as cell magic")
return line, cell
# In order to actually use these magics, you must register them with a
# running IPython.
def load_ipython_extension(ipython):
"""
Any module file that define a function named `load_ipython_extension`
can be loaded via `%load_ext module.path` or be configured to be
autoloaded by IPython at startup time.
"""
# You can register the class itself without instantiating it. IPython will
# call the default constructor on it.
ipython.register_magics(MyMagics)
如果您想创建一个具有不同构造函数并保存其他状态的类,则应始终调用父构造函数并在注册之前自行实例化类
@magics_class
class StatefulMagics(Magics):
"Magics that hold additional state"
def __init__(self, shell, data):
# You must call the parent constructor
super(StatefulMagics, self).__init__(shell)
self.data = data
# etc...
def load_ipython_extension(ipython):
"""
Any module file that define a function named `load_ipython_extension`
can be loaded via `%load_ext module.path` or be configured to be
autoloaded by IPython at startup time.
"""
# This class must then be registered with a manually created instance,
# since its constructor has different arguments from the default:
magics = StatefulMagics(ipython, some_data)
ipython.register_magics(magics)
注意
在早期 IPython 版本 0.12 及之前,行魔术是使用 define_magic()
API 函数创建的。此 API 已在 IPython 0.13 中替换为上述 API,然后在 IPython 5 中完全删除。建议仍然使用 define_magic()
函数的 IPython 扩展的维护人员针对当前 API 调整其代码。
访问用户命名空间和局部作用域¶
在创建行魔术时,您可能需要访问周围作用域以获取用户变量(例如在函数内调用时)。IPython 提供了 @needs_local_scope
装饰器,可以从 IPython.core.magics
导入。使用 @needs_local_scope
装饰时,将把 local_ns
作为参数传递给魔术。为了方便,@needs_local_scope
也可以应用于单元格魔术,即使单元格魔术不会出现在局部作用域上下文中。
使魔术输出静音¶
有时,定义一个可通过在要执行的 Python 代码末尾添加分号来禁用的魔术可能很有用,即以与非魔术表达式相同的方式。这可以通过使用装饰器 @output_can_be_silenced
来实现,该装饰器可以从 IPython.core.magics
导入。使用此装饰器时,IPython 将解析魔术使用的 Python 代码,如果最后一个标记是 ;
,则魔术创建的输出将不会显示在屏幕上。如果您想查看此装饰器在操作中的示例,请查看 IPython.core.magics.execution.py
中定义的 time
魔术。
完整示例¶
以下是魔术包的完整示例。您可以使用 setuptools、distutils 或任何其他分发工具(如 flit,适用于纯 Python 包)来分发魔术。
在将魔术作为包的一部分进行分发时,建议的最佳做法是在 load_ipython_extension
中执行注册,如下面的示例所示,而不是直接在模块中执行(如带有 @register_*
装饰器的初始示例中)。这意味着用户需要使用 %load_ext
明确选择加载您的魔术,而不是在导入模块时隐式获取它。如果加载您的魔术有副作用、加载速度慢或可能覆盖具有相同名称的另一个魔术,则这一点尤其重要。
.
├── example_magic
│ ├── __init__.py
│ └── abracadabra.py
└── setup.py
$ cat example_magic/__init__.py
"""An example magic"""
__version__ = '0.0.1'
from .abracadabra import Abracadabra
def load_ipython_extension(ipython):
ipython.register_magics(Abracadabra)
$ cat example_magic/abracadabra.py
from IPython.core.magic import (Magics, magics_class, line_magic, cell_magic)
@magics_class
class Abracadabra(Magics):
@line_magic
def abra(self, line):
return line
@cell_magic
def cadabra(self, line, cell):
return line, cell