【问题标题】:How can I check if code is executed in the IPython notebook?如何检查代码是否在 IPython 笔记本中执行?
【发布时间】:2013-03-02 23:04:00
【问题描述】:

我想分享一些 Python 代码示例,如果在终端 Python/IPython 或 IPython 笔记本中执行,它应该会有所不同。

如何检查我的 Python 代码是否在 IPython 笔记本中运行?

【问题讨论】:

  • 我建议接受Gustavo Bezerra's answer。当前接受的答案没有回答问题,而 Gustavo 的答案是在最新版本的 Jupyter Notebook 中仍然有效的得分最高的答案。

标签: python ipython ipython-notebook


【解决方案1】:

以下内容满足我的需要:

get_ipython().__class__.__name__

它在终端 IPython 上返回 'TerminalInteractiveShell',在 Jupyter(笔记本和 qtconsole)上返回 'ZMQInteractiveShell',并在常规 Python 解释器上失败(NameError)。启动IPython时,方法get_python()似乎默认在全局命名空间中可用。

将其包装在一个简单的函数中:

def isnotebook():
    try:
        shell = get_ipython().__class__.__name__
        if shell == 'ZMQInteractiveShell':
            return True   # Jupyter notebook or qtconsole
        elif shell == 'TerminalInteractiveShell':
            return False  # Terminal running IPython
        else:
            return False  # Other type (?)
    except NameError:
        return False      # Probably standard Python interpreter

以上内容在 macOS 10.12 和 Ubuntu 14.04.4 LTS 上使用 Python 3.5.2、IPython 5.1.0 和 Jupyter 4.2.1 进行了测试

【讨论】:

  • jupyter console 上,不幸的是get_ipython() 也返回了ZMQInteractiveShell 的实例
  • 如果有人有兴趣检测笔记本是否在 Google Colab 上运行,您可以查看:get_ipython().__class__.__module__ == "google.colab._shell"
  • 这仅适用于笔记本中的代码。如果函数在导入的包中,则不起作用。
  • @ChristopherBarber 这不是我看到的。如果我将此函数粘贴到文件 test.py 中,然后在 Jupyter Notebook 中运行 from test import isnotebook; print(isnotebook()),它会打印 True。 (在笔记本服务器版本 5.2.1 和 6.0.1 上测试。)
  • 我认为有些情况对我不起作用,但不幸的是我不记得细节了。也许这不再是问题,或者我只是感到困惑。
【解决方案2】:

检查您是否在笔记本中,这可能很重要,例如在确定使用哪种进度条时,这对我有用:

def in_ipynb():
    try:
        cfg = get_ipython().config 
        if cfg['IPKernelApp']['parent_appname'] == 'ipython-notebook':
            return True
        else:
            return False
    except NameError:
        return False

【讨论】:

  • 在我的 IPython-Notebook(IPython 3.1 版)中,cfg['IPKernelApp']['parent_appname'] 是一个IPython.config.loader.LazyConfigValue,与True"iypthon-notebook" 相比是不同的
  • @juanjux get_ipython 在 ipynb (Jupyter) 中返回一个 IPython.kernel.zmq.zmqshell.ZMQInteractiveShell 实例,在终端 REPL 中返回一个 IPython.terminal.interactiveshell.TerminalInteractiveShell,以防您需要区分笔记本和终端/控制台(这会影响绘图)。
  • ^ 因此您可以将 try 块的内部替换为:return str(type(get_ipython())) == "<class 'ipykernel.zmqshell.ZMQInteractiveShell'>"
  • 就像@Dux,这对我不起作用;它总是返回 false,即使在笔记本中也是如此。怀疑随着某种延迟配置加载系统的引入,这个答案已经过时了。
  • 还请注意,您的配置可能会以空字典的形式返回,在这种情况下,您需要将 KeyError 添加到 except 块。然而,使用基于 Gustavo Bezerra 答案的代码可能会更好。即使我得到一个空配置,我在检查类名时也会得到shell='PyDevTerminalInteractiveShell'
【解决方案3】:

您可以使用以下sn -p [1]检查python是否处于交互模式:

def is_interactive():
    import __main__ as main
    return not hasattr(main, '__file__')

我发现这种方法非常有用,因为我在笔记本中做了很多原型设计。出于测试目的,我使用默认参数。否则,我从sys.argv读取参数。

from sys import argv

if is_interactive():
    params = [<list of default parameters>]
else:
    params = argv[1:]

按照autonotebook的实现,你可以使用以下代码判断你是否在笔记本中。

def in_notebook():
    try:
        from IPython import get_ipython
        if 'IPKernelApp' not in get_ipython().config:  # pragma: no cover
            return False
    except ImportError:
        return False
    except AttributeError:
        return False
    return True

【讨论】:

  • python -c "def is_interactive(): > import main as main > return not hasattr(main, 'file') > print is_interactive ()" 是的
  • is_interactive() 不区分笔记本和控制台。
  • 另一个警告,从 ipython 发出 %run 是非交互式的。你可以说它应该是,但它仍然是一个问题。
  • 这个答案的后半部分很有用,但前半部分(大约is_interactive)在我看来基本上与问题无关。它的正确性也令人怀疑;正如@marscher 指出的那样,它会将使用python -c 运行的任何东西都视为处于“交互”模式,即使这不是真的。我不想自己做,因为这不是我的答案,但我认为只需删除整个答案的前半部分即可改善这一点。
  • 请注意,get_ipython 在从普通 python shell 运行或运行脚本时将返回 None,因此此代码需要防范这种情况。
【解决方案4】:

最近我遇到了一个需要解决方法的bug in Jupyter notebook,我想在不丢失其他shell 功能的情况下做到这一点。我意识到keflavich's solution 在这种情况下不起作用,因为get_ipython() 只能直接从笔记本获得,而不是从导入的模块获得。所以我找到了一种从我的模块中检测它是否是从 Jupyter 笔记本导入和使用的方法:

import sys

def in_notebook():
    """
    Returns ``True`` if the module is running in IPython kernel,
    ``False`` if in IPython shell or other Python shell.
    """
    return 'ipykernel' in sys.modules

# later I found out this:

def ipython_info():
    ip = False
    if 'ipykernel' in sys.modules:
        ip = 'notebook'
    elif 'IPython' in sys.modules:
        ip = 'terminal'
    return ip

如果这足够强大,我们将不胜感激。

类似的方式可以获得一些关于客户端的信息,以及 IPython 版本:

import sys

if 'ipykernel' in sys.modules:
    ip = sys.modules['ipykernel']
    ip_version = ip.version_info
    ip_client = ip.write_connection_file.__module__.split('.')[0]

# and this might be useful too:

ip_version = IPython.utils.sysinfo.get_sys_info()['ipython_version']

【讨论】:

  • 嗯,我使用的是 Fedora 23 Jupyter,'Ipython' in sys.modules 的计算结果为 False。也许你的意思是'IPython' in sys.modules?这是我的 Jupyter 环境中的 Truesys.modules 字典也不包括 'ipykernel' 键 - 在笔记本中运行时。
  • 这是迄今为止最好的答案,IMO。简短而甜蜜。
  • @danielpcox 那么哪个子答案是最佳答案?
【解决方案5】:

已针对 python 3.7.3 测试

CPython 实现的名称 __builtins__ 可作为其全局变量的一部分,顺便说一句。可以通过函数 globals() 检索。
如果脚本在 Ipython 环境中运行,那么 __IPYTHON__ 应该是 __builtins__ 的属性。
因此,如果在 Ipython 下运行,下面的代码将返回 True,否则返回 False

hasattr(__builtins__,'__IPYTHON__')

【讨论】:

  • 又好又简单!
  • 您可能应该import builtins 并检查hasattr(builtins, "__IPYTHON__"),因为__builtins__ is an implementation detail 可能会改变。
  • 投反对票——在 IPython 会话中也是如此
  • NameError: name '__IPYTHON__' is not defined
  • @jtlz2 你可能错过了字符串周围的引号。
【解决方案6】:

问题是你想以不同的方式执行。

我们在 IPython 中尽最大努力防止内核知道连接到哪种类型的前端,实际上您甚至可以让一个内核同时连接到许多不同的前端。即使您可以查看stderr/out 的类型以了解您是否在 ZMQ 内核中,但它并不能保证您在另一边拥有什么。你甚至可以没有前端。

您可能应该以独立于前端的方式编写代码,但是如果您想显示不同的内容,可以使用rich display system (link pinned to version 4.x of IPython) 根据前端显示不同的内容,但前端会选择,而不是库。

【讨论】:

  • 我有一个例子:进度条。 Jupyter notebook 终端模拟器不支持\x1b[A(上移)等扩展终端控制字符,因此无法打印nested barsipywidgets 没问题,我们可以使用原生 Jupyter 小部件来显示进度条。但是我们有两种不同的方式来显示进度条,应用程序可能想知道显示环境是什么,以便适应和打印兼容的进度条。
  • 例如,我想将 IPython 配置设置为在充当笔记本时始终运行 %matplotlib inline,而不是在终端中运行,因为这不是必需的。
  • 虽然这是一个完全有效的意见,但此答案并未解决实际问题。无论您多么希望,否则总会存在可能重要的行为差异。
  • 本意是好的,但做法不同。有完全正当的理由使代码依赖于环境。我的应用程序是启动文件startup.ipy。在终端 IPython 中,为方便起见,我喜欢预先导入一些包。在笔记本中,这被认为是不好的风格,并且没有必要,因为我可以将导入的内容放在第一个单元格中,而不必一次又一次地键入它们。
  • 这不能回答所提出的问题。标题是“如何检查代码是否在 IPython 笔记本中执行?”请更改正确答案或问题的标题。
【解决方案7】:

以下捕获https://stackoverflow.com/a/50234148/1491619的情况,无需解析ps的输出

def pythonshell():
    """Determine python shell

    pythonshell() returns

    'shell' (started python on command line using "python")
    'ipython' (started ipython on command line using "ipython")
    'ipython-notebook' (e.g., running in Spyder or started with "ipython qtconsole")
    'jupyter-notebook' (running in a Jupyter notebook)

    See also https://stackoverflow.com/a/37661854
    """

    import os
    env = os.environ
    shell = 'shell'
    program = os.path.basename(env['_'])

    if 'jupyter-notebook' in program:
        shell = 'jupyter-notebook'
    elif 'JPY_PARENT_PID' in env or 'ipython' in program:
        shell = 'ipython'
        if 'JPY_PARENT_PID' in env:
            shell = 'ipython-notebook'

    return shell

【讨论】:

  • 对我来说,这只是显示jupyterjupyter consolejupyter qtconsole 还是jupyter notebook
【解决方案8】:

我建议避免检测特定的前端,因为有 too many of them。相反,您可以测试您是否在 iPython 环境中运行:

def is_running_from_ipython():
    from IPython import get_ipython
    return get_ipython() is not None

如果您从通常的 python 命令行调用running_from_ipython,上面将返回False。当您从 Jupyter Notebook、JupyterHub、iPython shell、Google Colab 等调用它时,它将返回 True

【讨论】:

  • 对我不起作用 -- 当我在 Ubuntu 上使用 Python3 的 Jupyter Notebook 中尝试此操作时,get_ipython() 返回 &lt;ipykernel.zmqshell.ZMQInteractiveShell at 0x7f750ba94320&gt;
  • 这种方法的问题在于它不能解决 OP 的问题,“我如何从我的 Python 代码中检查它是否在 IPython notebook 中运行?” (强调我的)。 IPython shell 不是笔记本,但是当我在 PyCharm 的 Python 控制台中运行它时,我得到 get_ipython() is not None 返回 True
  • hm,我如何检测我是在 jupyter 还是在运行?
  • @ntg:这是一个相当不同的区别。如果您真的想知道,请考虑提交一个新的 StackOverflow 问题。
  • @Cecil:嗯,有道理,其实好像有stackoverflow.com/questions/64676189/…
【解决方案9】:

您所要做的就是将这两个单元格放在笔记本的开头:

单元格 1:(标记为“代码”):

is_notebook = True

单元格 2:(标记为“原始 NBConvert”):

is_notebook = False

第一个单元格将始终执行,但第二个单元格只有在您将笔记本导出为 Python 脚本时才会执行。

稍后,您可以检查:

if is_notebook:
    notebook_code()
else:
    script_code()

希望这会有所帮助。

【讨论】:

    【解决方案10】:

    一个非常简单高效的解决方案是检查调用栈的顶部是否引用了IPython环境,如下:

    import traceback
    
    def is_in_notebook():
        rstk = traceback.extract_stack(limit=1)[0]
        return rstk[0].startswith("<ipython")
    

    此代码适用于 IPython 或 Jupyter 上的 Python 2 和 3,无需检查、设置或更改环境。

    【讨论】:

    • 有趣。你能提供你的调用堆栈的内容吗?
    【解决方案11】:

    据我所知,这里有3种使用ipykernel的ipython

    1. ipython qtconsole(简称“qtipython”)
    2. spyder 中的 IPython(简称“spyder”)
    3. jupyter notebook 中的 IPython(简称“jn”)

    使用'spyder' in sys.modules可以区分spyder

    但是对于 qtipython 和 jn 很难区分,因为

    它们具有相同的sys.modules 和相同的 IPython 配置:get_ipython().config

    我发现 qtipython 和 jn 的不同之处:

    首先在 IPython shell 中运行 os.getpid() 以获取 pid 号

    然后运行ps -ef|grep [pid number]

    我的 qtipython pid 是 8699

    yanglei   8699  8693  4 20:31 ?        00:00:01 /home/yanglei/miniconda2/envs/py3/bin/python -m ipykernel_launcher -f /run/user/1000/jupyter/kernel-8693.json
    

    我的 jn pid 是 8832

    yanglei   8832  9788 13 20:32 ?        00:00:01 /home/yanglei/miniconda2/bin/python -m ipykernel_launcher -f /run/user/1000/jupyter/kernel-ccb962ec-3cd3-4008-a4b7-805a79576b1b.json
    

    qtipython和jn的不同是ipython的json名,jn的json名比qtipython的长

    所以,我们可以使用以下代码自动检测所有 Python 环境:

    import sys,os
    def jupyterNotebookOrQtConsole():
        env = 'Unknow'
        cmd = 'ps -ef'
        try:
            with os.popen(cmd) as stream:
                if not py2:
                    stream = stream._stream
                s = stream.read()
            pid = os.getpid()
            ls = list(filter(lambda l:'jupyter' in l and str(pid) in l.split(' '), s.split('\n')))
            if len(ls) == 1:
                l = ls[0]
                import re
                pa = re.compile(r'kernel-([-a-z0-9]*)\.json')
                rs = pa.findall(l)
                if len(rs):
                    r = rs[0]
                    if len(r)<12:
                        env = 'qtipython'
                    else :
                        env = 'jn'
            return env
        except:
            return env
        
    pyv = sys.version_info.major
    py3 = (pyv == 3)
    py2 = (pyv == 2)
    class pyi():
        '''
        python info
        
        plt : Bool
            mean plt avaliable
        env :
            belong [cmd, cmdipython, qtipython, spyder, jn]
        '''
        pid = os.getpid()
        gui = 'ipykernel' in sys.modules
        cmdipython = 'IPython' in sys.modules and not gui
        ipython = cmdipython or gui
        spyder = 'spyder' in sys.modules
        if gui:
            env = 'spyder' if spyder else jupyterNotebookOrQtConsole()
        else:
            env = 'cmdipython' if ipython else 'cmd'
        
        cmd = not ipython
        qtipython = env == 'qtipython'
        jn = env == 'jn'
        
        plt = gui or 'DISPLAY' in os.environ 
    
    print('Python Envronment is %s'%pyi.env)
    

    源码在这里: Detection Python Environment, Especially distinguish Spyder, Jupyter notebook, Qtconsole.py

    【讨论】:

    • 令人着迷。这里的问题当然是不可移植性。您正在调用特定于 POSIX 的 ps 命令。那很糟。 Windows 不兼容 POSIX,除非您在 Windows Linux 子系统 (WSL) 下运行,这意味着这在世界上最流行的平台上失败了。 哎呀。
    【解决方案12】:

    我正在使用 Django Shell Plus 来启动 IPython,并且我想让“在笔记本中运行”作为 Django 设置值可用。 get_ipython() 在加载设置时不可用,所以我使用它(这不是防弹的,但对于使用它的本地开发环境来说已经足够了):

    import sys
    
    if '--notebook' in sys.argv:
        ENVIRONMENT = "notebook"
    else:
        ENVIRONMENT = "dev"
    

    【讨论】:

      【解决方案13】:

      假设您可以控制 Jupyter Notebook,您可以:

      1. 在将其用作代码中的标志的单元格中设置环境值。在该单元格(或您要排除的所有单元格)中放置唯一注释

        # exclude_from_export
        %set_env is_jupyter=1

      2. 将笔记本导出为 python 脚本以在不同的上下文中使用。导出将排除注释的单元格,然后排除设置环境值的代码。注意:将 your_notebook.ipynb 替换为您的实际笔记本文件的名称。

        jupyter nbconvert --to script --RegexRemovePreprocessor.patterns="['^# exclude_from_export']" your_notebook.ipynb

      这将生成一个没有设置 jupyter 环境标志的文件,允许使用它的代码确定性地执行。

      【讨论】:

      • 这是一个相当慷慨的假设。
      【解决方案14】:

      这样的事情怎么样:

      import sys
      
      inJupyter = sys.argv[-1].endswith('json')
      
      print(inJupyter);
      

      【讨论】:

      • 是的,这显然太可怕了。传递给当前 Python 进程的最后一个命令行参数是 "json"!= 当前 Python 进程是 Jupyter。这两个条件充其量只是切线相关。祝你好运,通过像这样天真的蛮力检测方案解决误报。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-16
      • 2017-10-21
      • 1970-01-01
      • 1970-01-01
      • 2013-10-19
      相关资源
      最近更新 更多