在Python中要获得一个正在运行的脚本文件的位置(路径),使用__file__。这对于根据运行文件的位置加载其他文件很有用。
到 Python 3.8为止,__file__ 返回在执行 python 命令 (或在某些环境下的 python3 命令) 时指定的路径。如果指定了一个相对路径,则返回相对路径;如果指定了一个绝对路径,则返回绝对路径。
在Python 3.9及以后的版本中,无论在运行时指定的路径是什么,都会返回绝对路径。
对以下内容进行了说明。
os.getcwd()
,__file__
- 获取当前执行文件的文件名和目录名。
- 获取正在执行的文件的绝对路径。
- 根据当前执行的文件的位置,读取其他文件。
- 将当前目录移至正在执行的文件的目录。
- 在运行时,无论当前目录如何,都可以进行同样的处理。
有关获取和改变当前目录(工作目录)的信息,请参见以下文章。
注意 __file__ 不能在 Jupyter 笔记本(.ipynb)中使用。
.ipynb所在的目录将作为当前目录被执行,与启动Jupyter Notebook的目录无关。
可以在代码中使用os.chdir()来改变当前目录。
os.getcwd() 和 __file__。
在Windows中,你可以使用dir命令而不是pwd来检查当前目录。
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook
创建一个Python脚本文件(file_path.py),在下层(data/src)有以下内容。
import os
print('getcwd: ', os.getcwd())
print('__file__: ', __file__)
运行python命令(或在某些环境下的python3命令),指定脚本文件的路径。
python3 data/src/file_path.py
# getcwd: /Users/mbp/Documents/my-project/python-snippets/notebook
# __file__: data/src/file_path.py
当前目录的绝对路径可以用os.getcwd()获得。你也可以使用 __file__ 来获得由 python3 命令指定的路径。
到 Python 3.8为止,__file__ 将包含 python (或 python3) 命令中指定的路径。在上面的例子中,因为是相对路径,所以返回相对路径,但如果是绝对路径,则返回绝对路径。
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook
python3 /Users/mbp/Documents/my-project/python-snippets/notebook/data/src/file_path.py
# getcwd: /Users/mbp/Documents/my-project/python-snippets/notebook
# __file__: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src/file_path.py
Python 3.9 和更高版本会返回 __file__ 的绝对路径,与 python (或 python3) 命令中指定的路径无关。
在下面的例子中,我们将把代码添加到Python 3.7的同一个脚本文件(file_path.py)中,并相对于上述目录运行。
在Python 3.7中,使用的是绝对路径。结果显示在本节末尾。
获取当前执行文件的文件名和目录名。
要获得运行文件的文件名和目录名,请使用标准库os.path模块中的以下函数。
os.path.basename()
os.path.dirname()
print('basename: ', os.path.basename(__file__))
print('dirname: ', os.path.dirname(__file__))
执行结果。
# basename: file_path.py
# dirname: data/src
获取正在执行的文件的绝对路径。
如果用 __file__ 获得一个相对路径,它可以用 os.path.abspath() 转换为一个绝对路径。目录也可以作为绝对路径获得。
print('abspath: ', os.path.abspath(__file__))
print('abs dirname: ', os.path.dirname(os.path.abspath(__file__)))
执行结果。
# abspath: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src/file_path.py
# abs dirname: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src
如果在 os.path.abspath() 中指定了一个绝对路径,它将被原样返回。因此,如果 __file__ 是一个绝对路径,下面的内容将不会引起错误。
os.path.abspath(__file__)
根据当前执行的文件的位置,读取其他文件。
如果你想根据正在执行的文件的位置(路径)来读取其他文件,用os.path.join()连接下面两个文件。
- 正在执行的文件的目录
- 要从运行文件中读取的文件的相对路径。
如果你想读取与你正在运行的文件在同一目录下的文件,只需将文件名连接起来。
print('[set target path 1]')
target_path_1 = os.path.join(os.path.dirname(__file__), 'target_1.txt')
print('target_path_1: ', target_path_1)
print('read target file:')
with open(target_path_1) as f:
print(f.read())
执行结果。
# [set target path 1]
# target_path_1: data/src/target_1.txt
# read target file:
# !! This is "target_1.txt" !!
上层的代表是”. \”. 你可以让它保持原样,但你可以使用os.path.normpath()来规范化路径并删除多余的”. \”和其他字符。
print('[set target path 2]')
target_path_2 = os.path.join(os.path.dirname(__file__), '../dst/target_2.txt')
print('target_path_2: ', target_path_2)
print('normalize : ', os.path.normpath(target_path_2))
print('read target file:')
with open(target_path_2) as f:
print(f.read())
执行结果。
# [set target path 2]
# target_path_2: data/src/../dst/target_2.txt
# normalize : data/dst/target_2.txt
# read target file:
# !! This is "target_2.txt" !!
将当前目录移至正在执行的文件的目录。
使用os.chdir()将当前目录移动到脚本中正在执行的文件的目录。
你可以看到,它是由os.getcwd()移动的。
print('[change directory]')
os.chdir(os.path.dirname(os.path.abspath(__file__)))
print('getcwd: ', os.getcwd())
执行结果。
# [change directory]
# getcwd: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src
一旦当前目录被移动,在读取文件时就不需要将其与运行文件的目录连接起来。你可以直接指定相对于运行文件目录的路径。
print('[set target path 1 (after chdir)]')
target_path_1 = 'target_1.txt'
print('target_path_1: ', target_path_1)
print('read target file:')
with open(target_path_1) as f:
print(f.read())
print()
print('[set target path 2 (after chdir)]')
target_path_2 = '../dst/target_2.txt'
print('target_path_2: ', target_path_2)
print('read target file:')
with open(target_path_2) as f:
print(f.read())
执行结果。
# [set target path 1 (after chdir)]
# target_path_1: target_1.txt
# read target file:
# !! This is "target_1.txt" !!
#
# [set target path 2 (after chdir)]
# target_path_2: ../dst/target_2.txt
# read target file:
# !! This is "target_2.txt" !!
在运行时,无论当前目录如何,都可以进行同样的处理。
正如我们所展示的,可以根据脚本文件的位置来加载文件,与运行时的当前目录无关,使用以下方法之一。
- 使用os.path.join()将运行文件的目录和要从运行文件中读取的文件的相对路径串联起来。
- 将当前目录移至正在执行的文件的目录。
移动当前目录比较容易,当然,如果你之后想读或写更多的文件,你需要考虑到当前目录已经被移动。
前面例子的结果总结如下。
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook
python3 data/src/file_path.py
# getcwd: /Users/mbp/Documents/my-project/python-snippets/notebook
# __file__: data/src/file_path.py
# basename: file_path.py
# dirname: data/src
# abspath: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src/file_path.py
# abs dirname: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src
#
# [set target path 1]
# target_path_1: data/src/target_1.txt
# read target file:
# !! This is "target_1.txt" !!
#
# [set target path 2]
# target_path_2: data/src/../dst/target_2.txt
# normalize : data/dst/target_2.txt
# read target file:
# !! This is "target_2.txt" !!
#
# [change directory]
# getcwd: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src
#
# [set target path 1 (after chdir)]
# target_path_1: target_1.txt
# read target file:
# !! This is "target_1.txt" !!
#
# [set target path 2 (after chdir)]
# target_path_2: ../dst/target_2.txt
# read target file:
# !! This is "target_2.txt" !!
指定绝对路径的结果如下。
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook
python3 /Users/mbp/Documents/my-project/python-snippets/notebook/data/src/file_path.py
# getcwd: /Users/mbp/Documents/my-project/python-snippets/notebook
# __file__: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src/file_path.py
# basename: file_path.py
# dirname: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src
# abspath: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src/file_path.py
# abs dirname: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src
#
# [set target path 1]
# target_path_1: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src/target_1.txt
# read target file:
# !! This is "target_1.txt" !!
#
# [set target path 2]
# target_path_2: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src/../dst/target_2.txt
# normalize : /Users/mbp/Documents/my-project/python-snippets/notebook/data/dst/target_2.txt
# read target file:
# !! This is "target_2.txt" !!
#
# [change directory]
# getcwd: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src
#
# [set target path 1 (after chdir)]
# target_path_1: target_1.txt
# read target file:
# !! This is "target_1.txt" !!
#
# [set target path 2 (after chdir)]
# target_path_2: ../dst/target_2.txt
# read target file:
# !! This is "target_2.txt" !!
在终端中移动当前目录并执行同一个脚本文件的结果如下所示。你可以看到,即使从不同的位置执行同一个文件,也能读到它。
cd data/src
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook/data/src
python3 file_path.py
# getcwd: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src
# __file__: file_path.py
# basename: file_path.py
# dirname:
# abspath: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src/file_path.py
# abs dirname: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src
#
# [set target path 1]
# target_path_1: target_1.txt
# read target file:
# !! This is "target_1.txt" !!
#
# [set target path 2]
# target_path_2: ../dst/target_2.txt
# normalize : ../dst/target_2.txt
# read target file:
# !! This is "target_2.txt" !!
#
# [change directory]
# getcwd: /Users/mbp/Documents/my-project/python-snippets/notebook/data/src
#
# [set target path 1 (after chdir)]
# target_path_1: target_1.txt
# read target file:
# !! This is "target_1.txt" !!
#
# [set target path 2 (after chdir)]
# target_path_2: ../dst/target_2.txt
# read target file:
# !! This is "target_2.txt" !!