定义自定义魔法命令#
定义自己的魔法命令主要有两种方式:从独立的函数定义,或者通过继承 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
# 在交互式会话中,需要删除这些以避免
# 自动魔法(automagic)在行魔法上工作时发生名称冲突。
del lmagic, lcmagic
你还可以通过继承 IPython.core.magic.Magics
类来创建所有三种类型的魔法命令。这使你可以创建能够在调用之间保持状态的魔法命令,并且能够完全访问主 IPython 对象:
Show code cell content
# 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提供了 @needs_local_scope
装饰器,可以从 IPython.core.magic
导入。当用 @needs_local_scope
装饰时,魔法函数将被传递 local_ns
作为参数。为了方便起见,@needs_local_scope
也可以应用于单元魔法,即使单元魔法不能出现在局部作用域上下文中。
静默魔法输出
有时定义一个可以像非魔法表达式一样静默输出的魔法可能很有用,即通过在要执行的Python代码末尾附加分号来实现。这可以通过使用从 IPython.core.magic
导入的 @output_can_be_silenced
装饰器来实现。当使用此装饰器时,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