写一本充满Python代码的书很难

写一本充满Python代码的书很难,python,latex,ipython,workflow,jupyter,Python,Latex,Ipython,Workflow,Jupyter,我正在写一本关于使用Latex编写python代码的书。我计划在输出的同时,在整个过程中穿插大量包含python代码的文本。真正给我带来麻烦的是,当我需要返回并编辑我的python代码时,将其很好地恢复到我的最新文档中是一件非常痛苦的事情 我做了大量的研究,似乎找不到一个好的解决办法 这一个包括完整的文件作为一个,不能解决我的问题 这个也一样。 找到了解决方案1(糟糕) 我可以使用latex包将python代码复制并粘贴到latex ok中 优点: 易于更新的代码只有一小部分 缺点: 对于需

我正在写一本关于使用Latex编写python代码的书。我计划在输出的同时,在整个过程中穿插大量包含python代码的文本。真正给我带来麻烦的是,当我需要返回并编辑我的python代码时,将其很好地恢复到我的最新文档中是一件非常痛苦的事情

我做了大量的研究,似乎找不到一个好的解决办法

这一个包括完整的文件作为一个,不能解决我的问题

这个也一样。

找到了解决方案1(糟糕)

我可以使用latex包将python代码复制并粘贴到latex ok中

优点:

  • 易于更新的代码只有一小部分
  • 缺点:

  • 对于需要在python中运行的输出,请分别复制、粘贴
  • 初写速度慢,每章需要做几百次这个过程
  • 找到解决方案2(坏)

    使用带有标记的jupyter笔记本,导出到Latex,\n将文件包含到Latex主文档中

    优点:

  • 流线型
  • 已在中包含输出
  • 缺点:

    • 若要进行小的更改,需要重新导入整个文档,则不会保存在Latex编辑器中对标记文本所做的任何更改
    • 在jupyter notebook之后重命名python中的单个变量可能需要几个小时
    • 编辑似乎是一项艰巨的工作
    理想溶液

    • 用乳胶书写文本
    • 在jupyter笔记本中编写python,导出到latex
    • 以某种方式将代码片段(导出文件的小部分)包含到主latex手册的不同部分中这是我无法理解的部分
    • 当需要python更改时,在jupyter中进行更改,然后以相同名称重新导出为latex文件
    • Latex book会从includes自动更新
    这里的关键是导出的python笔记本被拆分并发送到文档的不同部分。为了让它工作,它需要在笔记本的标记或代码中加上标签或标记,所以当我重新导出它时,这些相同的部分会被发送到书中的相同位置

    优点:

  • Python编辑很容易,很容易传回书本
  • 文字用乳胶书写,可以使用乳胶的力量
  • 如果能帮我找到一个更接近我理想的解决方案,我将不胜感激。我快死了


    也许没关系,但我正在用VS代码编写latex和jupyter笔记本。如果要解决这些问题,我愿意更换工具。

    Jupyter不允许从笔记本导出特定的单元格,它只允许导出整个笔记本。为了尽可能接近理想场景,您需要模块化Jupyter设置:

  • 将您的单台Jupyter笔记本拆分为更小的笔记本
  • 然后可以通过文件>下载方式>LaTeX(.tex)将每个笔记本导出到LaTeX
  • 在LaTeX中,您可以通过导入生成的.tex文件

    \输入{filname.tex}

  • 如果要将较小的笔记本导入主笔记本的单元格中,可以通过(请参阅magic命令)执行此操作

    还可以通过插入python文件(请参见magic命令)

    它加载Python文件并允许您在主笔记本中执行它

    您还可以创建小的.py代码段,将它们加载到您的小Jupyter笔记本中,然后在您的大笔记本中运行该小笔记本

    您可以使用VS代码,但在浏览器中使用Jupyter可能会更快地进行编辑

    ()

    我习惯于将测试代码和源代码放在同一个文档中(为了方便起见,将它们分成几个文件)。这个包起源于R世界,但也可以与其他语言一起使用。这里有一个非常简单的例子:

    ---
    output: bookdown::pdf_document2
    ---
    
    ```{r setup, include=FALSE}
    knitr::opts_chunk$set(echo = TRUE)
    ```
    
    # Setup data
    
    First we define some varialbes with data.
    
    ```{python data}
    bob = ['Bob Smith', 42, 30000, 'software']
    sue = ['Sue Jones', 45, 40000, 'music']
    ```
    
    # Output data
    
    then we output some of the data.
    
    ```{python output}
    bob[0], sue[2]
    ```
    
    # Reference code block
    
    Finally lets repeate the code block without evaluating it.
    
    ```{python, ref.label="output", eval = FALSE}
    ```
    
    输出:


    这是我写的一个小脚本。它拆分单个
    *.ipynb
    文件,并将其转换为多个
    *.tex
    文件

    用途是:

  • 复制以下脚本并另存为类似于
    main.py的内容
  • 执行
    python main.py init
    。它将创建
    main.tex
    style\u ipython\u custom.tplx
  • 在jupyther笔记本中,添加额外的行
    #latex:tag_a
    #latex:tag_b
    。。要提取的每个单元格。相同的标签将被提取到相同的
    *.tex
    文件中
  • 将其另存为
    *.ipynb
    文件。幸运的是,当前的VSCode python插件支持导出到
    *.ipynb
    ,或者使用jupytext将
    *.py
    转换为
    *.ipynb
  • 运行
    python main.py path/to/your.ipynb
    ,它将创建
    tag_a.tex
    tag_b.tex
  • 编辑
    main.tex
    并添加
    \input{tag_a.tex}
    \input{tag_b.tex}
  • 运行
    pdflatex main.tex
    ,它将生成
    main.pdf
  • 这个脚本背后的想法是:

    使用默认值nbconvert.LatexExporter将jupyter笔记本转换为LaTex会生成包含宏定义的完整LaTex文件。使用它转换每个单元格可能会创建大型LaTex文件。为了避免这个问题,脚本首先创建只有宏定义的
    main.tex
    ,然后将每个单元格转换为没有宏定义的LaTex文件。这可以使用自定义模板文件来完成,该文件是从
    style\u ipython.tplx

    标记或标记单元格可能是使用单元格元数据来完成的,但我找不到如何在VSCode python plugin()中设置它,因此它使用regex模式
    ^#latex:(.*)
    扫描每个单元格的源代码,并在将其转换为latex文件之前将其删除

    资料来源:

    import sys
    import re
    import os
    from collections import defaultdict
    import nbformat
    from nbconvert import LatexExporter, exporters
    
    OUTPUT_FILES_DIR = './images'
    CUSTOM_TEMPLATE = 'style_ipython_custom.tplx'
    MAIN_TEX = 'main.tex'
    
    
    def create_main():
        # creates `main.tex` which only has macro definition
        latex_exporter = LatexExporter()
        book = nbformat.v4.new_notebook()
        book.cells.append(
            nbformat.v4.new_raw_cell(r'\input{__your_input__here.tex}'))
        (body, _) = latex_exporter.from_notebook_node(book)
        with open(MAIN_TEX, 'x') as fout:
            fout.write(body)
        print("created:", MAIN_TEX)
    
    
    def init():
        create_main()
        latex_exporter = LatexExporter()
        # copy `style_ipython.tplx` in `nbconvert.exporters` module to current directory,
        # and modify it so that it does not contain macro definition
        tmpl_path = os.path.join(
            os.path.dirname(exporters.__file__),
            latex_exporter.default_template_path)
        src = os.path.join(tmpl_path, 'style_ipython.tplx')
        target = CUSTOM_TEMPLATE
        with open(src) as fsrc:
            with open(target, 'w') as ftarget:
                for line in fsrc:
                    # replace the line so than it does not contain macro definition
                    if line == "((*- extends 'base.tplx' -*))\n":
                        line = "((*- extends 'document_contents.tplx' -*))\n"
                    ftarget.write(line)
        print("created:", CUSTOM_TEMPLATE)
    
    
    def group_cells(note):
        # scan the cell source for tag with regexp `^#latex:(.*)`
        # if sames tags are found group it to same list
        pattern = re.compile(r'^#latex:(.*?)$(\n?)', re.M)
        group = defaultdict(list)
        for num, cell in enumerate(note.cells):
            m = pattern.search(cell.source)
            if m:
                tag = m.group(1).strip()
                # remove the line which contains tag
                cell.source = cell.source[:m.start(0)] + cell.source[m.end(0):]
                group[tag].append(cell)
            else:
                print("tag not found in cell number {}. ignore".format(num + 1))
        return group
    
    
    def doit():
        with open(sys.argv[1]) as f:
            note = nbformat.read(f, as_version=4)
        group = group_cells(note)
        latex_exporter = LatexExporter()
        # use the template which does not contain LaTex macro definition
        latex_exporter.template_file = CUSTOM_TEMPLATE
        try:
            os.mkdir(OUTPUT_FILES_DIR)
        except FileExistsError:
            pass
        for (tag, g) in group.items():
            book = nbformat.v4.new_notebook()
            book.cells.extend(g)
            # unique_key will be prefix of image
            (body, resources) = latex_exporter.from_notebook_node(
                book,
                resources={
                    'output_files_dir': OUTPUT_FILES_DIR,
                    'unique_key': tag
                })
            ofile = tag + '.tex'
            with open(ofile, 'w') as fout:
                fout.write(body)
                print("created:", ofile)
            # the image data which is embedded as base64 in notebook
            # will be decoded and returned in `resources`, so write it to file
            for filename, data in resources.get('outputs', {}).items():
                with open(filename, 'wb') as fres:
                    fres.write(data)
                    print("created:", filename)
    
    
    if len(sys.argv) <= 1:
        print("USAGE: this_script [init|yourfile.ipynb]")
    elif sys.argv[1] == "init":
        init()
    else:
        doit()
    
    导入系统 进口稀土 导入操作系统 从集合导入defaultdict 导入NBF格式 来自nbconvert进口后期出口商 输出文件\目录='。/图像' 自定义\u模板='样式\u ip
    ---
    output: bookdown::pdf_document2
    ---
    
    ```{r setup, include=FALSE}
    knitr::opts_chunk$set(echo = TRUE)
    ```
    
    # Setup data
    
    First we define some varialbes with data.
    
    ```{python data}
    bob = ['Bob Smith', 42, 30000, 'software']
    sue = ['Sue Jones', 45, 40000, 'music']
    ```
    
    # Output data
    
    then we output some of the data.
    
    ```{python output}
    bob[0], sue[2]
    ```
    
    # Reference code block
    
    Finally lets repeate the code block without evaluating it.
    
    ```{python, ref.label="output", eval = FALSE}
    ```
    
    import sys
    import re
    import os
    from collections import defaultdict
    import nbformat
    from nbconvert import LatexExporter, exporters
    
    OUTPUT_FILES_DIR = './images'
    CUSTOM_TEMPLATE = 'style_ipython_custom.tplx'
    MAIN_TEX = 'main.tex'
    
    
    def create_main():
        # creates `main.tex` which only has macro definition
        latex_exporter = LatexExporter()
        book = nbformat.v4.new_notebook()
        book.cells.append(
            nbformat.v4.new_raw_cell(r'\input{__your_input__here.tex}'))
        (body, _) = latex_exporter.from_notebook_node(book)
        with open(MAIN_TEX, 'x') as fout:
            fout.write(body)
        print("created:", MAIN_TEX)
    
    
    def init():
        create_main()
        latex_exporter = LatexExporter()
        # copy `style_ipython.tplx` in `nbconvert.exporters` module to current directory,
        # and modify it so that it does not contain macro definition
        tmpl_path = os.path.join(
            os.path.dirname(exporters.__file__),
            latex_exporter.default_template_path)
        src = os.path.join(tmpl_path, 'style_ipython.tplx')
        target = CUSTOM_TEMPLATE
        with open(src) as fsrc:
            with open(target, 'w') as ftarget:
                for line in fsrc:
                    # replace the line so than it does not contain macro definition
                    if line == "((*- extends 'base.tplx' -*))\n":
                        line = "((*- extends 'document_contents.tplx' -*))\n"
                    ftarget.write(line)
        print("created:", CUSTOM_TEMPLATE)
    
    
    def group_cells(note):
        # scan the cell source for tag with regexp `^#latex:(.*)`
        # if sames tags are found group it to same list
        pattern = re.compile(r'^#latex:(.*?)$(\n?)', re.M)
        group = defaultdict(list)
        for num, cell in enumerate(note.cells):
            m = pattern.search(cell.source)
            if m:
                tag = m.group(1).strip()
                # remove the line which contains tag
                cell.source = cell.source[:m.start(0)] + cell.source[m.end(0):]
                group[tag].append(cell)
            else:
                print("tag not found in cell number {}. ignore".format(num + 1))
        return group
    
    
    def doit():
        with open(sys.argv[1]) as f:
            note = nbformat.read(f, as_version=4)
        group = group_cells(note)
        latex_exporter = LatexExporter()
        # use the template which does not contain LaTex macro definition
        latex_exporter.template_file = CUSTOM_TEMPLATE
        try:
            os.mkdir(OUTPUT_FILES_DIR)
        except FileExistsError:
            pass
        for (tag, g) in group.items():
            book = nbformat.v4.new_notebook()
            book.cells.extend(g)
            # unique_key will be prefix of image
            (body, resources) = latex_exporter.from_notebook_node(
                book,
                resources={
                    'output_files_dir': OUTPUT_FILES_DIR,
                    'unique_key': tag
                })
            ofile = tag + '.tex'
            with open(ofile, 'w') as fout:
                fout.write(body)
                print("created:", ofile)
            # the image data which is embedded as base64 in notebook
            # will be decoded and returned in `resources`, so write it to file
            for filename, data in resources.get('outputs', {}).items():
                with open(filename, 'wb') as fres:
                    fres.write(data)
                    print("created:", filename)
    
    
    if len(sys.argv) <= 1:
        print("USAGE: this_script [init|yourfile.ipynb]")
    elif sys.argv[1] == "init":
        init()
    else:
        doit()