使用版本控制下的IPython / Jupyter笔记本

IPython笔记本置于版本控制之下的好策略是什么?

笔记本格式非常适合版本控制:如果想对笔记本和输出进行版本控制,那么这种方法非常有效。当人们只想对输入进行版本控制,而不包括单元格输出时,就会出现烦恼。“构建产品”),可以是大的二进制blob,特别是电影和情节。特别是,我试图找到一个好的工作流程:

  • 允许我在包含或不包含输出之间进行选择,
  • 防止我不小心提交输出,如果我不想要它,
  • 允许我保持本地版本的输出,
  • 允许我看到当我使用我的版本控制系统在输入中有变化时(即,如果我只控制输入,但我的本地文件有输出,那么我希望能够看到输入是否发生了变化(需要提交)。使用版本控制状态命令将总是注册一个差异,因为本地文件有输出。)
  • 允许我从更新的干净笔记本更新我的工作笔记本(其中包含输出)。# EYZ0

如前所述,如果我选择包含输出(例如,在使用nbviewer时,这是可取的),那么一切都没问题。问题是当我想要版本控制输出。有一些工具和脚本可以剥离笔记本的输出,但我经常遇到以下问题:

  1. 我不小心用输出提交了一个版本,从而污染了我的存储库。
  2. 我清除输出以使用版本控制,但实际上更愿意将输出保存在本地副本中(例如,有时需要一段时间才能重新生成)。
  3. Cell/All Output/Clear菜单选项相比,一些剥离输出的脚本稍微改变了格式,从而在差异中产生了不必要的杂音。有一些答案可以解决这个问题。
  4. 当将更改拉到文件的干净版本时,我需要找到某种方法将这些更改合并到我的工作笔记本中,而不必重新运行所有内容。 (更新)< / >强

我已经考虑了下面将要讨论的几个选项,但是还没有找到一个好的全面的解决方案。完整的解决方案可能需要对IPython进行一些更改,或者可能依赖于一些简单的外部脚本。我目前使用水银,但想要一个解决方案,也适用于git:一个理想的解决方案将是版本控制不可知。

这个问题已经讨论过很多次了,但是从用户的角度来看,还没有明确的解决方案。这个问题的答案应该能提供明确的策略。如果它需要IPython的最新(甚至是开发版)版本或易于安装的扩展,这是没问题的。

我一直在玩我修改过的笔记本版本,它可以选择保存一个.clean版本,每次保存使用格雷戈里·克劳斯怀特的建议。这满足了我的大部分约束条件,但留下了以下问题:

  1. 这还不是一个标准的解决方案(需要修改ipython源。是否有一种方法可以通过简单的扩展来实现这种行为?需要某种保存钩子。
  2. 我在当前工作流中遇到的一个问题是拉取更改。这些文件将被放到.clean文件中,然后需要以某种方式集成到我的工作版本中。(当然,我总是可以重新执行笔记本,但这可能是一个痛苦,特别是如果一些结果依赖于长计算,并行计算等)我还不知道如何解决这个问题。也许涉及ipycache这样的扩展的工作流可以工作,但这似乎有点太复杂了。

笔记

移除(剥离)输出

  • 当笔记本运行时,可以使用Cell/All Output/Clear菜单选项删除输出。
  • 有一些脚本用于删除输出,例如脚本nbstripout.py删除了输出,但不会产生与使用笔记本界面相同的输出。这最终包含在ipython / nbconvert回购中,但这已经关闭,声明这些更改现在包含在ipython / ipython中,但相应的功能似乎还没有包括在内。话虽如此,格雷戈里·克劳斯怀特的解决方案表明,即使不调用ipython / nbconvert,这也很容易做到,所以如果可以正确地连接,这种方法可能是可行的。(然而,将它附加到每个版本控制系统上似乎不是个好主意——这应该以某种方式与笔记本机制挂钩。)

新闻组

  • # EYZ0。

问题

把请求

82146 次浏览

不幸的是,我对Mercurial了解不多,但我可以为您提供一个与Git一起工作的可能解决方案,希望您能够将我的Git命令转换为对应的Mercurial命令。

对于后台,在Git中,add命令将对文件所做的更改存储到暂存区域。完成此操作后,Git将忽略对文件的任何后续更改,除非您告诉它也执行这些更改。因此,下面的脚本,对于每个给定的文件,剥离所有的outputsprompt_number sections,阶段性剥离文件,然后恢复原始文件:

如果运行这个脚本会得到类似ImportError: No module named IPython.nbformat的错误消息,那么使用ipython而不是python来运行脚本。

from IPython.nbformat import current
import io
from os import remove, rename
from shutil import copyfile
from subprocess import Popen
from sys import argv


for filename in argv[1:]:
# Backup the current file
backup_filename = filename + ".backup"
copyfile(filename,backup_filename)


try:
# Read in the notebook
with io.open(filename,'r',encoding='utf-8') as f:
notebook = current.reads(f.read(),format="ipynb")


# Strip out all of the output and prompt_number sections
for worksheet in notebook["worksheets"]:
for cell in worksheet["cells"]:
cell.outputs = []
if "prompt_number" in cell:
del cell["prompt_number"]


# Write the stripped file
with io.open(filename, 'w', encoding='utf-8') as f:
current.write(notebook,f,format='ipynb')


# Run git add to stage the non-output changes
print("git add",filename)
Popen(["git","add",filename]).wait()


finally:
# Restore the original file;  remove is needed in case
# we are running in windows.
remove(filename)
rename(backup_filename,filename)

在您想要提交更改的文件上运行脚本之后,只需运行git commit

这是我的解决方案与git。它允许你像往常一样添加和提交(和diff):这些操作不会改变你的工作树,同时(重新)运行一个笔记本不会改变你的git历史。

虽然这可能适用于其他VSC,但我知道它不能满足您的要求(至少VSC不可知)。不过,它对我来说是完美的,尽管它没有什么特别出色的地方,而且很多人可能已经在使用它了,但我没有通过谷歌找到关于如何实现它的明确说明。所以它可能对其他人有用。

  1. 保存一个有这个内容的文件(对于下面的内容,让我们假设是~/bin/ipynb_output_filter.py)

  2. 让它可执行(chmod +x ~/bin/ipynb_output_filter.py)

  3. 创建文件~/.gitattributes,包含以下内容

    *。ipynb过滤器= dropoutput_ipynb

  4. 执行以下命令:

    git配置全局核心。attributesfile ~ / .gitattributes Git配置——global filter.dropoutput_ipynb。干净的~ / bin / ipynb_output_filter.py Git配置——global filter.dropoutput_ipynb。涂抹猫< / p >

完成了!

限制:

  • 它只适用于git
  • 在git中,如果你在somebranch分支中执行git checkout otherbranch; git checkout somebranch,你通常期望工作树是不变的。在这里,您将丢失两个分支之间的源不同的笔记本的输出和单元格编号。
  • 更一般地说,输出完全没有版本控制,就像Gregory的解决方案一样。为了不只是在每次执行涉及签出的操作时都将其丢弃,可以通过将其存储在单独的文件中来改变方法(但请注意,在运行上述代码时,提交id是未知的!),并可能对它们进行版本控制(但请注意,这将需要比git commit notebook_file.ipynb更多的东西,尽管它至少可以使git diff notebook_file.ipynb免于base64垃圾)。
  • 也就是说,顺便提一下,如果你提取了包含一些输出的代码(即由其他人不使用这种方法提交的代码),输出将正常检出。只有当地生产的产品会损失。

我的解决方案反映了这样一个事实:我个人不喜欢对生成的东西进行版本控制——注意,涉及输出的合并几乎肯定会使输出和生产力都失效。

编辑:

  • 如果你确实采用了我建议的解决方案——也就是说,全局的——你会遇到麻烦,因为一些git会把想要回购到版本输出。因此,如果你想要禁用特定git存储库的输出过滤,只需在其中创建一个文件. /信息/属性, with

    **ipynb过滤器=

作为内容。显然,可以以同样的方式进行相反的操作:为特定的存储库启用筛选只有

  • 代码现在维护在它自己的git存储库

  • 如果上面的说明导致ImportErrors,尝试添加"ipython"在脚本路径之前:

      git config --global filter.dropoutput_ipynb.clean ipython ~/bin/ipynb_output_filter.py
    

编辑: 2016年5月(2017年2月更新):我的脚本有几个替代方案-为了完整性,这里是我知道的列表:nbstripout (其他 变体), nbstrip金桥

我使用一个非常务实的方法;它适用于多个笔记本,在多个侧面。它甚至可以让我四处“转移”笔记本。它适用于Windows和Unix/MacOS Al认为很简单,就是解决上面的问题

概念

基本上,跟踪.ipnyb-files,只跟踪相应的.py-files 通过使用--script选项启动notebook-server,该文件将在保存笔记本时自动创建/保存。< / p >

这些.py-files包含所有输入;非代码保存为注释,单元格边界也是如此。这些文件可以读取/导入(并拖动)到笔记本服务器以(重新)创建一个笔记本。只有输出消失了;直到它重新运行。

我个人使用水银来版本跟踪.py文件;并使用普通(命令行)命令进行添加、签入(等等)。大多数其他(D) vc都允许这样做。

现在追溯历史很简单;.py是小的,文本的和简单的区分。偶尔,我们需要一个克隆(只是分支;在那里启动第二个笔记本服务器),或者旧版本(签出并导入到笔记本服务器中),等等。

提示,技巧

  • * .ipynb添加到.hgignore,这样Mercurial就知道它可以忽略这些文件
  • 创建一个(bash)脚本来启动服务器(使用--script选项)并对其进行版本跟踪
  • 保存笔记本确实保存了# eyz0文件,但是是否签入了它。
    • 这是缺点:人们可以忘记这一点
    • 它也是功能:可以在不集群存储库历史的情况下保存一个笔记本(并在以后继续)。
    • 李< / ul > < / >

    祝愿

    • 如果在笔记本仪表板中有一个签到/添加/等按钮就好了
    • 签出到(例如)file@date+rev.py应该是有帮助的 要加上这一点,工作量太大了;也许我会这样做一次。到目前为止,我都是手工完成的

我们有一个合作项目,产品是Jupyter笔记本,我们在过去的六个月里使用了一种非常有效的方法:我们自动激活保存.py文件,并跟踪.ipynb文件和.py文件。

这样,如果有人想查看/下载最新的笔记本,他们可以通过github或nbviewer来完成,如果有人想查看笔记本代码是如何变化的,他们可以只查看.py文件的变化。

对于Jupyter笔记本服务器,这可以通过添加行来完成

import os
from subprocess import check_call


def post_save(model, os_path, contents_manager):
"""post-save hook for converting notebooks to .py scripts"""
if model['type'] != 'notebook':
return # only do this for notebooks
d, fname = os.path.split(os_path)
check_call(['jupyter', 'nbconvert', '--to', 'script', fname], cwd=d)


c.FileContentsManager.post_save_hook = post_save

jupyter_notebook_config.py文件并重新启动笔记本服务器。

如果您不确定在哪个目录中找到您的jupyter_notebook_config.py文件,您可以键入jupyter --config-dir,如果您在那里没有找到该文件,您可以键入jupyter notebook --generate-config来创建它。

对于Ipython 3笔记本服务器,这可以通过添加行来完成

import os
from subprocess import check_call


def post_save(model, os_path, contents_manager):
"""post-save hook for converting notebooks to .py scripts"""
if model['type'] != 'notebook':
return # only do this for notebooks
d, fname = os.path.split(os_path)
check_call(['ipython', 'nbconvert', '--to', 'script', fname], cwd=d)


c.FileContentsManager.post_save_hook = post_save

ipython_notebook_config.py文件并重新启动笔记本服务器。这些台词来自一个github问题的答案@minrk提供和@dror包括他们在他的SO答案以及。

对于Ipython 2笔记本服务器,这可以通过启动服务器来完成:

ipython notebook --script

或者加上这条直线

c.FileNotebookManager.save_script = True

ipython_notebook_config.py文件并重新启动笔记本服务器。

如果您不确定在哪个目录中找到您的ipython_notebook_config.py文件,您可以键入ipython locate profile default,如果您在那里没有找到该文件,您可以键入ipython profile create来创建它。

这是我们在github上的项目就是使用这种方法,这是Github的例子,探索笔记本电脑最近的变化

我们对此非常满意。

我做了阿尔伯特&Rich做了——不要给.ipynb文件做版本(因为这些文件可能包含图像,这会变得混乱)。相反,总是运行ipython notebook --script或在配置文件中放置c.FileNotebookManager.save_script = True,以便在保存笔记本时始终创建.py文件(可控制版本)。

为了重新生成笔记本(在签出回购或切换分支之后),我将脚本py_file_to_notebooks.py放在我存储笔记本的目录中。

现在,签出一个回购之后,只需运行python py_file_to_notebooks.py来生成ipynb文件。切换分支后,您可能必须运行python py_file_to_notebooks.py -ov来覆盖现有的ipynb文件。

为了安全起见,还可以添加

到你的.gitignore文件

编辑:我不再这样做了,因为(A)每次签出分支时,你都必须从py文件中重新生成你的笔记本,(B)在笔记本中有其他东西,如markdown,你会丢失。相反,我使用git过滤器从笔记本中剥离输出。关于如何做到这一点的讨论是在这里

下面是Cyrille rosant为IPython 3.0提供的一个新解决方案,它坚持标记文件,而不是基于json的ipymd文件:

https://github.com/rossant/ipymd < a href = " https://github.com/rossant/ipymd " > < / >

正如前面指出的,--script3.x中被弃用了。这种方法可以通过应用后保存钩子来使用。特别地,在ipython_notebook_config.py中添加以下内容:

import os
from subprocess import check_call


def post_save(model, os_path, contents_manager):
"""post-save hook for converting notebooks to .py scripts"""
if model['type'] != 'notebook':
return # only do this for notebooks
d, fname = os.path.split(os_path)
check_call(['ipython', 'nbconvert', '--to', 'script', fname], cwd=d)


c.FileContentsManager.post_save_hook = post_save

代码取自# 8009

好吧,所以看起来目前最好的解决方案,根据讨论在这里,是使一个git过滤器自动剥离ipynb文件的输出提交。

以下是我让它工作的方法(摘自讨论):

我稍微修改了cfriedline的nbstripout文件,当你不能导入最新的IPython时,会给出一个信息性错误: https://github.com/petered/plato/blob/fb2f4e252f50c79768920d0e47b870a8d799e92b/notebooks/config/strip_notebook_output 并将它添加到我的回购,让我们说在./relative/path/to/strip_notebook_output

还添加了文件.gitattributes文件到repo的根目录,包含:

*.ipynb filter=stripoutput

并创建了一个setup_git_filters.sh包含

git config filter.stripoutput.clean "$(git rev-parse --show-toplevel)/relative/path/to/strip_notebook_output"
git config filter.stripoutput.smudge cat
git config filter.stripoutput.required true

并运行source setup_git_filters.sh。花哨的$(git rev-parse…)事情是找到你的回购在任何(Unix)机器上的本地路径。

接下来是Pietro Battiston编写的优秀脚本,如果您遇到这样的Unicode解析错误:

Traceback (most recent call last):
File "/Users/kwisatz/bin/ipynb_output_filter.py", line 33, in <module>
write(json_in, sys.stdout, NO_CONVERT)
File "/Users/kwisatz/anaconda/lib/python2.7/site-packages/IPython/nbformat/__init__.py", line 161, in write
fp.write(s)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2014' in position 11549: ordinal not in range(128)

你可以在脚本开头添加:

reload(sys)
sys.setdefaultencoding('utf8')

我基于MinRKs要点创建了nbstripout,它同时支持Git和Mercurial(感谢mforbes)。它既可以在命令行中独立使用,也可以作为过滤器使用,可以通过nbstripout install / nbstripout uninstall轻松安装在当前存储库中。

PyPI或简单地获取它

pip install nbstripout

(2017 - 02)

策略

    <李> on_commit ():
    • 去掉输出>的名称。ipynb (nbstripout)
    • 清除输出> name.clean.ipynb (nbstripout,)
    • python: name.ipynb.py (nbconvert)
    • 始终转换为markdown: name.ipynb.md (nbconvertipymd)
    • 李< / ul > < / > <李> vcs.configure ():
      • Git difftool, mergetool: nbdiff和nbmerge from nbdime
      • 李< / ul > < / >

      工具

      • nbstripout:从笔记本上剥离输出
        • src: # EYZ0
        • Src: https://github.com/kynan/nbstripout
          • # EYZ0
          • 李< / ul > < / > 李< / ul > < / >
          • ipynb_output_filter:从笔记本上剥离输出
            • src: # EYZ0
            • 李< / ul > < / >
            • ipymd:转换{Jupyter, Markdown, O'Reilly Atlas Markdown, OpenDocument, .py}
              • src: # EYZ0
              • 李< / ul > < / > “工具的差异和合并的Jupyter笔记本。”(2015)
                • src: # EYZ0
                • 文档:

                  我已经构建了一个python包来解决这个问题

                  https://github.com/brookisme/gitnb

                  它提供了一个CLI,使用git启发的语法来跟踪/更新/区分git repo中的笔记本。

                  这里有一个例子

                  # add a notebook to be tracked
                  gitnb add SomeNotebook.ipynb
                  
                  
                  # check the changes before commiting
                  gitnb diff SomeNotebook.ipynb
                  
                  
                  # commit your changes (to your git repo)
                  gitnb commit -am "I fixed a bug"
                  

                  注意最后一步,我使用“gitnb commit”的地方是提交到你的git repo。它本质上是一个包装

                  # get the latest changes from your python notebooks
                  gitnb update
                  
                  
                  # commit your changes ** this time with the native git commit **
                  git commit -am "I fixed a bug"
                  

                  还有更多的方法,并且可以配置为在每个阶段需要或多或少的用户输入,但这是总体思想。

在四处搜寻之后,我终于找到了Jupyter文档中这个相对简单的预保存钩子。它剥离单元格输出数据。您必须将其粘贴到jupyter_notebook_config.py文件中(参见下面的说明)。

def scrub_output_pre_save(model, **kwargs):
"""scrub output before saving notebooks"""
# only run on notebooks
if model['type'] != 'notebook':
return
# only run on nbformat v4
if model['content']['nbformat'] != 4:
return


for cell in model['content']['cells']:
if cell['cell_type'] != 'code':
continue
cell['outputs'] = []
cell['execution_count'] = None
# Added by binaryfunt:
if 'collapsed' in cell['metadata']:
cell['metadata'].pop('collapsed', 0)


c.FileContentsManager.pre_save_hook = scrub_output_pre_save

从# EYZ0:

如果你不确定在哪个目录中找到你的jupyter_notebook_config.py文件,你可以输入jupyter --config-dir[进入命令提示符/终端],如果你没有找到文件,你可以输入jupyter notebook --generate-config来创建它。

下面文章中讨论的想法如何,笔记本的输出应该保存在哪里,理由是生成它可能需要很长时间,而且它很方便,因为GitHub现在可以渲染笔记本。添加了用于导出.py文件的自动保存钩子,用于diffs和.html,以便与不使用笔记本或git的团队成员共享。

https://towardsdatascience.com/version-control-for-jupyter-notebook-3e6cef13392d

你可以使用这个jupyter扩展。它可以让你直接上传你的ipython笔记本到github。

https://github.com/sat28/githubcommit

我还制作了一个视频来演示这些步骤 # EYZ0 < / p >

更新:现在你可以编辑Jupyter笔记本 文件直接在Visual Studio Code。您可以选择编辑

我终于找到了一个高效而简单的方法,可以让Jupyter和Git很好地一起工作。我还在第一步,但我已经认为它比所有其他复杂的解决方案要好得多。

Visual Studio代码是一个很酷的微软开源代码编辑器。它有一个优秀的Python扩展,现在允许您将导入Jupyter笔记本作为Python代码。现在你也可以直接编辑Jupyter笔记本

将笔记本导入python文件后,所有代码和标记都将放在一个普通的python文件中,注释中有特殊标记。如下图所示:

VSCode editor with a notebook convert to python

你的python文件只有笔记本输入单元格的内容。输出将在拆分窗口中生成。你的笔记本里有纯代码,你只是执行它,它不会改变。没有混合输出与您的代码。没有奇怪的JSON难以理解的格式来分析你的差异。

只是纯粹的python代码,你可以很容易地识别每一个差异。

我甚至不需要版本我的.ipynb文件了。我可以在.gitignore中放入*.ipynb行。

需要生成一个笔记本来发布或与他人共享?没问题,只需在交互式python窗口中点击导出按钮即可

 export a python file to Notebook format .

如果你直接编辑笔记本,现在有一个图标Convert and save to a python script。 # EYZ0 < / p >

这是Visual Studio Code中一个笔记本的截图:

Editing Notebook inside VSCode

我只使用了一天,但我终于可以愉快地使用Jupyter和Git了。

注:VSCode的代码完成比Jupyter好得多。

刚刚遇到的“jupytext”看起来像一个完美的解决方案。它从笔记本生成一个.py文件,然后保持两者同步。您可以通过.py文件进行版本控制、差异和合并输入,而不会丢失输出。当你打开笔记本时,它使用.py作为输入单元格,使用.ipynb作为输出单元格。如果你想在git中包含输出,那么你可以添加ipynb。

https://github.com/mwouts/jupytext

与2019年更好的方法相比,上面这些2016年非常流行的答案是不一致的。

有几个选项,最好的答案是Jupytext。

Jupytext

抓住在Jupytext上的朝向数据科学的文章

它与版本控制的工作方式是将.py和.ipynb文件放在版本控制中。如果您想要输入差异,请查看.py;如果您想要最新呈现的输出,请查看.ipynb。

值得一提的是:VS studio, nbconvert, nbdime, hydrogen

我认为再多做一些工作,VS studio和/或hydrogen(或类似的)将成为解决这个工作流程的主要参与者。

由于存在如此多的策略和工具来处理笔记本电脑的版本控制,我试图创建一个流程图来选择一个合适的策略(创建于2019年4月)

Decision flow to pick version control strategy

在删除笔记本的输出数年之后,我试图提出一个更好的解决方案。我现在使用Jupytext,这是我设计的Jupyter Notebook和Jupyter Lab的扩展。

Jupytext可以转换Jupyter笔记本到各种文本格式(脚本,Markdown和R Markdown)。反之。它还提供了将一对记事本转换为这些格式之一的选项,并自动同步记事本的两种表示形式(.ipynb.md/.py/.R文件)。

让我来解释一下Jupytext是如何回答上述问题的:

允许我在包含或不包含输出之间进行选择,

.md/.py/.R文件只包含输入单元格。您应该始终跟踪该文件。只有当您想跟踪输出时,才对.ipynb文件进行版本化。

防止我不小心提交输出,如果我不想要它,

添加*.ipynb.gitignore

允许我保持本地版本的输出,

输出保存在(本地).ipynb文件中

允许我看到当我使用我的版本控制系统在输入中有变化时(即,如果我只控制输入,但我的本地文件有输出,那么我希望能够看到输入是否发生了变化(需要提交)。使用版本控制状态命令将总是注册一个差异,因为本地文件有输出。)

.py/.R.md文件上的差异就是您要寻找的

允许我从更新的干净笔记本更新我的工作笔记本(其中包含输出)。(更新)

拉出最新版本的.py/.R.md文件,并在Jupyter中刷新您的笔记本(Ctrl+R)。您将从文本文件中获得最新的输入单元格,并从.ipynb文件中获得匹配的输出。内核不受影响,这意味着您的局部变量被保留-您可以继续在离开它的地方工作。

我喜欢Jupytext的原因是它的笔记本(以.py/.R.md文件的形式)可以在你最喜欢的IDE中编辑。使用这种方法,重构笔记本变得很容易。一旦你完成了,你只需要在Jupyter刷新笔记本。

如果你想尝试一下:用pip install jupytext安装Jupytext并重新启动你的Jupyter Notebook或Lab编辑器。打开你想要版本控制的笔记本,并使用Jupyter笔记本中的Jupytext菜单(或Jupyter实验室中的Jupytext命令)将对它转换为Markdown文件(或脚本)。保存您的笔记本,您将得到两个文件:原始的.ipynb,加上承诺的笔记本文本表示,这非常适合版本控制!

对于那些可能感兴趣的人:Jupytext也可以在命令行上使用。

现在是2020年4月,Jupyter笔记本版本控制有很多策略和工具。这里是你可以使用的所有工具的快速概述,

  • nbdime -不错的本地差异和笔记本合并

  • nbstripout -一个git过滤器,在每次提交前自动删除笔记本输出

  • jupytext -保持一个。py的伴侣文件同步到每个笔记本。只提交.py文件

  • nbconvert -将笔记本转换为python脚本或HTML(或两者都有)并提交这些替代文件类型

  • ReviewNB -显示笔记本diff(连同输出)在GitHub上的任何提交或拉请求。还可以在笔记本单元格上写注释来讨论更改(下面的截图)。

enter image description here

免责声明:我创建了ReviewNB。

我还将向其他人推荐https://nbdev.fast.ai/,它是Donald Knuth在1983年设想的最先进的“有文化的编程环境”!

它也有一些git钩子,帮助一些https://nbdev.fast.ai/#Avoiding-and-handling-git-conflicts和其他命令,如:

  • nbdev_read_nbs
  • nbdev_clean_nbs
  • nbdev_diff_nbs
  • nbdev_test_nbs

所以你也可以创建你的文档,就像在写一个库,例如:

  • < a href = " https://dev.fast。Ai /" rel="nofollow noreferrer">https://dev.fast.ai/
  • < a href = " https://ohmeow.github。Io /blurr/" rel="nofollow noreferrer">https://ohmeow.github.io/blurr/
  • < a href = " https://rbracco.github。Io /fastai2_audio/" rel="nofollow noreferrer">https://rbracco.github.io/fastai2_audio/

除了第一个链接,你可以在这里看到一个视频nbdev教程