您如何正确确定Python中的当前脚本目录?

2020/10/31 17:02 · python ·  · 0评论

我想看看在Python中确定当前脚本目录的最佳方法是什么。

我发现,由于调用Python代码的方式很多,很难找到一个好的解决方案。

这里有一些问题:

  • __file__如果脚本与执行没有定义execexecfile
  • __module__ 仅在模块中定义

用例:

  • ./myfile.py
  • python myfile.py
  • ./somedir/myfile.py
  • python somedir/myfile.py
  • execfile('myfile.py') (来自另一个脚本,该脚本可以位于另一个目录中,并且可以具有另一个当前目录。

我知道没有完美的解决方案,但是我正在寻找能够解决大多数情况的最佳方法。

最常用的方法是,os.path.dirname(os.path.abspath(__file__))但是如果您使用来从另一个脚本执行脚本,则此方法实际上不起作用exec()

警告

使用当前目录的任何解决方案都会失败,这可能会因调用脚本的方式而有所不同,或者可以在运行的脚本中进行更改。

os.path.dirname(os.path.abspath(__file__))

确实是您将获得的最好的。

exec/来执行脚本是很不寻常的execfile通常,您应该使用模块基础结构来加载脚本。如果必须使用这些方法,我建议设置__file__globals传递给脚本,以便它可以读取该文件名。

没有其他方法可以在执行代码中获取文件名:如您所述,CWD可能位于完全不同的位置。

如果您确实想解决通过调用脚本的情况,则execfile(...)可以使用inspect模块来推断文件名(包括路径)。据我所知,这将适用于您列出的所有情况:

filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
#!/usr/bin/env python
import inspect
import os
import sys

def get_script_dir(follow_symlinks=True):
    if getattr(sys, 'frozen', False): # py2exe, PyInstaller, cx_Freeze
        path = os.path.abspath(sys.executable)
    else:
        path = inspect.getabsfile(get_script_dir)
    if follow_symlinks:
        path = os.path.realpath(path)
    return os.path.dirname(path)

print(get_script_dir())

它适用于CPython,Jython,Pypy。如果使用脚本执行它的工作原理execfile()sys.argv[0]__file__基于解决方案会在这里失败)。如果脚本位于可执行的zip文件(/卵)中,则该脚本有效如果脚本是PYTHONPATH=/path/to/library.zip python -mscript_to_run从zip文件“导入”()的,则该脚本有效在这种情况下,它将返回存档路径。如果脚本被编译成独立的可执行文件(sys.frozen),它将起作用它适用于符号链接(realpath消除符号链接)。它在交互式解释器中工作;在这种情况下,它将返回当前的工作目录。

在Python 3.4+中,您可以使用更简单的pathlib模块:

from inspect import currentframe, getframeinfo
from pathlib import Path

filename = getframeinfo(currentframe()).filename
parent = Path(filename).resolve().parent

您也可以完全__file__避免使用inspect模块:

from pathlib import Path
parent = Path(__file__).resolve().parent

os.path...方法是Python 2中的“完成的事情”。

在Python 3中,您可以找到脚本目录,如下所示:

from pathlib import Path
cwd = Path(__file__).parents[0]

只需使用os.path.dirname(os.path.abspath(__file__))并非常仔细地检查使用情况下是否真的需要exec如果您不能将脚本用作模块,则可能是设计麻烦的迹象。

请记住,Python#8的Zen,如果您认为有一个必须在其中使用的用例exec,那么请告诉我们有关问题背景的更多详细信息。

import os
cwd = os.getcwd()

你想做什么?我不确定“当前脚本目录”到底是什么意思。您给出的用例的预期输出是什么?

首先..如果我们正在讨论注入匿名代码的方式,那么这里有一些丢失的用例。

code.compile_command()
code.interact()
imp.load_compiled()
imp.load_dynamic()
imp.load_module()
__builtin__.compile()
loading C compiled shared objects? example: _socket?)

但是,真正的问题是,您的目标是什么-您是否要强制实施某种安全性?或者,您只是对正在加载的内容感兴趣?

如果您对安全性感兴趣,则通过exec / execfile导入的文件名无关紧要-您应该使用rexec,它提供以下内容:

该模块包含RExec类,该类支持r_eval(),r_execfile(),r_exec()和r_import()方法,它们是标准Python函数eval(),execfile()以及exec和import语句的受限版本。在此受限环境中执行的代码只能访问被认为安全的模块和功能;您可以根据需要为RExec子类添加或删除功能。

但是,如果这更多是学术上的追求,那么您可以通过以下两种愚蠢的方法来深入研究。

示例脚本:

./deep.py

print ' >> level 1'
execfile('deeper.py')
print ' << level 1'

./deeper.py

print '\t >> level 2'
exec("import sys; sys.path.append('/tmp'); import deepest")
print '\t << level 2'

/tmp/deepest.py

print '\t\t >> level 3'
print '\t\t\t I can see the earths core.'
print '\t\t << level 3'

./codespy.py

import sys, os

def overseer(frame, event, arg):
    print "loaded(%s)" % os.path.abspath(frame.f_code.co_filename)

sys.settrace(overseer)
execfile("deep.py")
sys.exit(0)

输出量

loaded(/Users/synthesizerpatel/deep.py)
>> level 1
loaded(/Users/synthesizerpatel/deeper.py)
    >> level 2
loaded(/Users/synthesizerpatel/<string>)
loaded(/tmp/deepest.py)
        >> level 3
            I can see the earths core.
        << level 3
    << level 2
<< level 1

当然,这是一种资源密集的方式,您会跟踪所有代码。效率不是很高。但是,我认为这是一种新颖的方法,因为即使您深入巢穴,它仍然可以继续工作。您无法覆盖“评估”。尽管您可以覆盖execfile()。

注意,此方法仅覆盖exec / execfile,而不覆盖“ import”。对于更高级别的“模块”负载挂钩,您可能可以使用
sys.path_hooks(由PyMOTW致谢)。

多数民众赞成在我的头上。

这是一个部分解决方案,仍然比到目前为止所有已发布的解决方案都要好。

import sys, os, os.path, inspect

#os.chdir("..")

if '__file__' not in locals():
    __file__ = inspect.getframeinfo(inspect.currentframe())[0]

print os.path.dirname(os.path.abspath(__file__))

现在,此操作将适用于所有呼叫,但是如果有人使用chdir()来更改当前目录,则此操作也会失败。

笔记:

  • sys.argv[0]将无法正常工作,-c如果您使用以下命令执行脚本,则会返回python -c "execfile('path-tester.py')"
  • 我在https://gist.github.com/1385555上发布了完整的测试,欢迎您进行改进。

这在大多数情况下应该有效:

import os,sys
dirname=os.path.dirname(os.path.realpath(sys.argv[0]))

希望这会有所帮助:-如果您从任何地方运行脚本/模块,您将能够访问__file__变量,变量是表示脚本位置的模块变量。

另一方面,如果您使用的是解释器,则您将无权访问该变量,在该变量中您将获得一个名称,NameError并且os.getcwd()如果您正在其他地方运行该文件则会为您提供错误的目录。

在所有情况下,解决方案都可以为您提供所需的信息:

from inspect import getsourcefile
from os.path import abspath
abspath(getsourcefile(lambda:0))

我尚未对其进行彻底的测试,但是它解决了我的问题。

对于.py脚本以及交互式用法:

我经常使用脚本的目录(用于访问它们旁边存储的文件),但是我也经常在交互式外壳中运行这些脚本以进行调试。我定义__dir__为:

  • 运行或导入.py文件时,为文件的基本目录。这始终是正确的路径。
  • 运行.ipyn笔记本时,当前工作目录。这始终是正确的路径,因为Jupyter会将工作目录设置为.ipynb基本目录。
  • 在REPL中运行时,为当前工作目录。嗯,从文件中分离代码时实际的“正确路径”是什么?相反,在调用REPL之前,您有责任更改为“正确的路径”。

Python 3.4(及更高版本):

from pathlib import Path
__dir__ = Path(globals().get("__file__", "./_")).absolute().parent

Python 2(及更高版本):

import os
__dir__ = os.path.dirname(os.path.abspath(globals().get("__file__", "./_")))

说明:

  • globals() 返回所有全局变量作为字典。
  • .get("__file__", "./_")从键中返回值("__file__"如果存在)globals(),否则返回提供的默认值"./_"
  • 其余代码只是扩展__file__(或"./_")为绝对文件路径,然后返回文件路径的基本目录。
本文地址:http://python.askforanswer.com/ninruhezhengquequedingpythonzhongdedangqianjiaobenmulu.html
文章标签: ,   ,  
版权声明:本文为原创文章,版权归 admin 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!