在python中,如何获取脚本文件所在路径?
首先说明,不是获取“当前路径”,即os.getcwd()
这个问题包括两个容易混淆的问题,一是获取当前脚本文件所在的路径,二是获取启动python解释器的脚本所在的路径。
获取当前脚本文件所在路径,解决方案是1、2、3
获取启动python解释器的脚本文件所在路径,解决方案是4
当当前脚本是被直接运行的脚本(而不是被当作模块调用)时,两个问题的结果是一样的。
使用cx_freeze将脚本转换为可执行文件后,每种方案都会遇到自己的问题,不能在所有情况下都正确地获取当前文件路径。所以将几种办法结合起来,得到解决方案5,解决了所有问题。
1. 获取调用该函数的脚本所在路径,跨模块跨文件可调用的函数:
//tbc: 使用python解释器运行没有问题,编译成为exe后有问题,显示出来的是"当前路径",但不知道是为什么。我打印了一下inspect.stack(),发现文件名都对,但路径都是调用时的“当前路径”,不知道为什么?
# function: get the directory of script that calls this function
# usage: path = script_path()
def script_path():
import inspect, os
caller_file = inspect.stack()[1][1] # caller's filename
return os.path.abspath(os.path.dirname(caller_file))# path
2. 得到定义该函数的脚本路径,多文件使用要拷贝多份:
# function: get absolute directory of the file
# in which this function defines
# usage: path = script_path()
# note: please copy the function to target script,
# don't use it like <module>.script_path()
def script_path():
import inspect, os
this_file = inspect.getfile(inspect.currentframe())
return os.path.abspath(os.path.dirname(this_file))
3. 使用模块内置成员变量,但文件经cx_freeze编译为可执行文件后就会报错,说找不到变量定义(或为内置模块,无对应成员):
# warning: 编译为exe后,报错:找不到__file__定义
# usage: path = script_path(__file__)
def script_path(file_macro):
return os.path.abspath(os.path.dirname(file_macro))
# warning: 编译为exe后,报错:模块没有__file__属性
# usage: path = script_path(__name__)
def script_path(__name__):
return os.path.abspath(os.path.dirname(
sys.modules[__name__].__file__))
# warning: 编译为exe后,抛出异常:模块为内置模块,无法使用getfile获取文件名
# usage: path = script_path(__name__)
def script_path(__name__):
import inspect
return os.path.abspath(inspect.getfile(sys.modules[__name__]))
4. 获取启动python解释器的脚本路径:
def script_path():
path = os.path.realpath(sys.path[0])
if os.path.isfile(path):
path = os.path.dirname(path)
return os.path.abspath(path)
def script_path():
path = os.path.realpath(sys.argv[0])
if os.path.isfile(path):
path = os.path.dirname(path)
return os.path.abspath(path)
5. 综合遇到的问题,将以上方法结合起来:如果在可执行文件中,则返回可执行文件所在路径。如果在脚本中,则返回调用函数的脚本所在路径。
但这样也有问题,我发现inspect.stack()记录的路径都是相对路径,相对于启动时的os.getcwd()。所以如果使用了os.chdir(),那就会错误。
# function: get directory of current script, if script is built
# into an executable file, get directory of the excutable file
def current_file_directory():
import os, sys, inspect
path = os.path.realpath(sys.path[0]) # interpreter starter's path
if os.path.isfile(path): # starter is excutable file
path = os.path.dirname(path)
return os.path.abspath(path) # return excutable file's directory
else: # starter is python script
caller_file = inspect.stack()[1][1] # function caller's filename
return os.path.abspath(os.path.dirname(caller_file))# return function caller's file's directory