__file__ 在 Jupyter Notebook 中不存在

138
我正在使用Python 3.4.2版本的Jupyter Notebook服务器(v4.2.2),希望能够使用全局名称__file__。因为笔记本将从其他用户克隆,并且在一个部分中,我需要运行:
def __init__(self, trainingSamplesFolder='samples', maskFolder='masks'):
    self.trainingSamplesFolder = self.__getAbsPath(trainingSamplesFolder)
    self.maskFolder = self.__getAbsPath(maskFolder)

def __getAbsPath(self, path):
    if os.path.isabs(path):
        return path
    else:
        return os.path.join(os.path.dirname(__file__), path)

__getAbsPath(self, path)函数用于检查path参数是相对路径还是绝对路径,并返回path参数的绝对路径。这样我稍后就可以安全地使用返回的path

但是,我遇到了以下错误:

NameError: name '__file__' is not defined

我在网上搜索了这个错误并发现了一个“解决方案”,即最好使用sys.argv [0],但print(sys.argv[0])返回了:

/usr/local/lib/python3.4/dist-packages/ipykernel/__main__.py

但是正确的笔记本位置应该是/home/ubuntu/notebooks/
感谢 Martijn Pieters 的参考我如何获得当前的IPython Notebook名称(评论),最后一个答案(未被接受)非常适合我的需求。

print(os.getcwd())

/家/乌班图/笔记本


4
__file__ 只适用于模块和Python脚本,而不适用于notebook。你找到的答案在这里不适用。 - Martijn Pieters
3
如何获取当前的IPython Notebook名称,看起来更相关? - Martijn Pieters
7个回答

121

如果您想获取脚本所在目录的路径,我强烈建议使用:

os.path.abspath('')

优点

  • 可以在Jupyter Notebook中使用
  • 可以在REPL中使用
  • 不需要Python 3.4的pathlib

请注意,其中__file__具有优势的一种情况是当您从目录A调用Python但在目录B中运行脚本时。 在这种情况下,上述大多数方法将返回A而不是B。 但是对于Jupyter笔记本电脑,您始终会得到.ipynb文件的文件夹,而不是从哪里启动jupyter notebook的目录。


18
这对我不起作用。当我使用papermill运行笔记本时,它给我返回“工作目录”。 - untidyhair
这很惊奇。在我的测试中,所有三种情况都有效。这可能是因为 papermill(我不熟悉它)吗? - Shital Shah
9
@Shital,untidyhair是正确的。就像下面的mab的另一个建议一样,这并不起作用。它只是给你当前工作目录。 - germ

48

__file__可能对您不可用,但是您可以通过其他方式获取笔记本所在的当前文件夹。

全局变量中有一些痕迹,如果调用globals(),您将看到其中有一个键为_dh的元素,这可能会帮助您。以下是我如何成功加载位于与我的笔记本相同文件夹中的data.csv文件:

import os

current_folder = globals()['_dh'][0]

# Calculating path to the input data
data_location = os.path.join(current_folder,'data.csv')

6
哇,祝贺@shytikov,你的方法确实有效并应成为首选答案。你找到了一种避免丑陋的JS黑科技的方法。非常感谢。现在,如果我们能找到一种获取笔记本名称的方法就好了... - germ
看起来这在Notebook中可以工作,但在常规的Python解释器中不行(至少在导入为模块时)。我在使用Jupyter内核和常规Python解释器之间切换,所以我需要一些适用于两者的东西。我想我要使用环境变量。 - Kyle Barron
这对我来说在JupyterLab和Pycharm中都有效,而其他建议则无效。 - aimfeld
为什么不使用os.getcwd()代替globals()['_dh'][0] - John Doe
2
当我使用papermill从不同的目录执行笔记本时,这对我没有起作用。os.getcwd()globals()["_dh"]都显示了我运行papermill命令的目录,而不是笔记本模板所在的目录或执行的笔记本所在的目录。 - Ben Lindsay

44

在现代的Python(v3.4+)中,我们可以使用pathlib来获取notebook所在的目录:

from pathlib import Path

cwd = Path().resolve()
# cwd == PosixPath('/path/to/this/jupyter/ipynb/file's/directory/')

# or this way, thanks @NunoAndré:
cwd = Path.cwd()
# cwd == PosixPath('/path/to/this/jupyter/ipynb/file's/directory/')



更新

@ShitalShah 报告的错误我无法复现。无论应用程序启动时的工作目录如何,Jupyter笔记本似乎都可以正常工作。

例如: 文件~/dir1/dir2/untitled.ipynb和以~/dir1为起始目录启动的Jupyter笔记本:

Jupyter notebook started in ~/dir1

~/dir1/dir2中启动的Jupyter笔记本:

Jupyter notebook started in ~/dir1/dir2


1
@ShitalShah请查看更新后的答案。也许您可以澄清一下,您遇到了什么问题。 - mab
2
Path has a class method just for that: Path.cwd() - Nuno André
8
@mac,这个不起作用。如果您在笔记本中更改了cwd(当前工作目录),然后重新执行该单元格,它将给出新的cwd,而不是笔记本的位置。这是Jupyter Notebook中一个非常缺失的功能。我不得不采用涉及JavaScript的技巧。真麻烦。 - germ
2
@mab:尝试使用import os;os.chdir('path/to/new/dir')。如果您现在尝试Path.cwd()Path().resolve(),它们都指向path/to/new/dir而不是您笔记本的位置! - germ
1
@mab,这是不可靠的。因为笔记本界面允许您以任意顺序执行单元格,这是一个非常好的功能。您可以在笔记本开头保存目录。然而,如果您稍后使用了os.chdir命令,那么重新执行笔记本中的第一个单元格时,仍将得到错误的答案。 - germ
显示剩余3条评论

18

无法获取笔记本的路径。你可能会找到一种只在一个环境中起作用的方法(例如os.getcwd()),但如果以不同的方式加载笔记本,则不一定有效。

相反,尝试编写笔记本,使其不需要知道自己的路径。如果像获取pwd这样的东西,请确保快速失败/打印错误,而不是悄悄地继续尝试。

另请参见:https://github.com/ipython/ipython/issues/10123


2
你是对的,使用 os.getcwd() 无法获取笔记本的路径。它只获取当前工作目录。如果您已经在笔记本中更改了该目录,则返回到该单元格时,您将无法获取笔记本目录。这是一个巨大的问题。不,我不同意您应该编写脚本以避免知道它在哪里。有很多很好的理由这样做,比如将副本复制到另一个位置。 - germ
虽然可能不容易实现OP所请求的内容,但有充分的理由去寻找这条路。例如,在使用jupytext进行Python和笔记本之间的双向同步时,辅助文件的路径非常关键。我想要找到一个可靠的解决方案。 - WestCoastProjects

3

我是一个新手,但这对我有用。

您可以通过以下方式获得os.path.dirname(__file__)的等效值:

sys.path[0]

也许我错过了一些可能会失败的情况,但在“标准”条件下,这似乎是最佳解决方案。 - undefined

3
对于Jupyter Notebooks,用引号括起文件,即"文件"。然后使用dirname方法获取文件DIR。
import os

THIS_DIR = os.path.dirname(os.path.abspath("__file__"))
print(THIS_DIR)

# /home/brightkoech/Projects/EPL-Streamlit-Visualisation/src

这个回答是误导性的。它给人们一种错误的印象,认为字符串__file__对于abspath有特殊的含义。实际上并没有。你可以在那里放任何字符串。abspath所做的只是使用当前工作目录解析给定的相对路径,然后dirname会提取目录。因此,这与getcwd完全等效。 - Vittorio Ballestra

2

在新版本的Python和Notebook中,__file__可以正常工作...如果您使用的是旧版本,则可以使用以下方法获取__file__

import inspect
from pathlib import Path

module_path = Path(inspect.getframeinfo(inspect.currentframe()).filename).resolve()

但是...这种方法速度要慢得多... 另一方面,它不会返回当前工作目录,而是返回模块的路径,即使该模块从其他地方导入。


半年后,Jupyter 5.2.0不再支持__file__。您指的是哪个版本? - WestCoastProjects

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接