Python:相对于当前运行脚本添加到 sys.path 的最佳方式
Posted
技术标签:
【中文标题】Python:相对于当前运行脚本添加到 sys.path 的最佳方式【英文标题】:Python: Best way to add to sys.path relative to the current running script 【发布时间】:2012-01-29 14:08:15 【问题描述】:我有一个充满脚本的目录(比如说project/bin
)。我还有一个位于project/lib
的库,并希望脚本自动加载它。这是我通常在每个脚本顶部使用的:
#!/usr/bin/python
from os.path import dirname, realpath, sep, pardir
import sys
sys.path.append(dirname(realpath(__file__)) + sep + pardir + sep + "lib")
# ... now the real code
import mylib
这有点麻烦,丑陋,并且必须在每个文件的开头粘贴。有没有更好的方法来做到这一点?
我真正希望的是像这样顺利的事情:
#!/usr/bin/python
import sys.path
from os.path import pardir, sep
sys.path.append_relative(pardir + sep + "lib")
import mylib
或者更好的是,当我的编辑(或具有提交访问权限的其他人)决定重新排序导入作为其清理过程的一部分时不会破坏的东西:
#!/usr/bin/python --relpath_append ../lib
import mylib
这不会直接移植到非 posix 平台,但它会保持干净。
【问题讨论】:
另见:***.com/questions/2349991/… 我建议读者查看下面的@EyalLevin 答案,因为它会在脚本的命令行调用中设置路径,并避免完全接触 shell 环境设置。您也不必在提交的代码中加入任何路径依赖项。 【参考方案1】:这是我用的:
import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))
【讨论】:
我会做 sys.path.insert(0, ..) 这样它就不会被其他路径覆盖。 真的没有办法让它自动运行吗? 这有点冒险。如果__file__
是相对于当前工作目录的相对文件名(例如,setup.py
),那么os.path.dirname(__file__)
将是空字符串。对于这个和similar concerns raised by John Jiang,ekhumoro' s more general-purpose solution 是非常可取的。【参考方案2】:
如果您不想编辑每个文件
像普通 python 库一样安装库 或 将PYTHONPATH
设置为您的lib
或者如果您愿意在每个文件中添加一行,请在顶部添加一个导入语句,例如
import import_my_lib
将import_my_lib.py
保留在bin 中,import_my_lib
可以正确地将python 路径设置为您想要的任何lib
【讨论】:
【参考方案3】:创建一个包装模块project/bin/lib
,其中包含:
import sys, os
sys.path.insert(0, os.path.join(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib'))
import mylib
del sys.path[0], sys, os
然后您可以将脚本顶部的所有内容替换为:
#!/usr/bin/python
from lib import mylib
【讨论】:
【参考方案4】:如果您不想以任何方式更改脚本内容,请将当前工作目录 .
添加到 $PYTHONPATH(参见下面的示例)
PYTHONPATH=.:$PYTHONPATH alembic revision --autogenerate -m "First revision"
收工吧!
【讨论】:
docs.python.org/3/tutorial/modules.html#the-module-search-path 说:“ sys.path 是从这些位置初始化的:-包含输入脚本的目录”。我认为这不是真的。 我倾向于这样做,尤其是在 .envrc 中,以便使用 direnv 甚至是自动和隔离的。【参考方案5】:我正在使用:
import sys,os
sys.path.append(os.getcwd())
【讨论】:
我经常只是做sys.path.append('.')
如果脚本从不同的目录运行怎么办?例如从根目录提供完整的系统路径?然后 os.getcwd() 将返回“/”
永远不要这样做。 当前的工作目录 (CWD)不保证是你认为的那样——尤其是在你无法预见的边缘情况下绝对应该在一英里外看到。只需引用__file__
,而不是像任何准理智的开发人员一样。【参考方案6】:
提供的每个答案都有一个问题,可以概括为“只需将这个神奇的咒语添加到脚本的开头。看看你可以用一两行代码做什么。” 它们不会在所有可能的情况下都起作用!
例如,一个这样的魔法咒语使用__file__
。不幸的是,如果您使用 cx_Freeze 打包脚本或使用 IDLE,这将导致异常。
另一个这样的魔法咒语使用 os.getcwd()。这仅在您从命令提示符运行脚本并且包含脚本的目录是当前工作目录(即您在运行脚本之前使用 cd 命令切换到该目录)时才有效。天哪!我希望我不必解释为什么如果您的 Python 脚本位于 PATH 某处并且您只需键入脚本文件的名称即可运行它,为什么这将不起作用。
幸运的是,有一个魔法咒语适用于我测试过的所有情况。不幸的是,神奇的咒语不仅仅是一两行代码。
import inspect
import os
import sys
# Add script directory to sys.path.
# This is complicated due to the fact that __file__ is not always defined.
def GetScriptDirectory():
if hasattr(GetScriptDirectory, "dir"):
return GetScriptDirectory.dir
module_path = ""
try:
# The easy way. Just use __file__.
# Unfortunately, __file__ is not available when cx_Freeze is used or in IDLE.
module_path = __file__
except NameError:
if len(sys.argv) > 0 and len(sys.argv[0]) > 0 and os.path.isabs(sys.argv[0]):
module_path = sys.argv[0]
else:
module_path = os.path.abspath(inspect.getfile(GetScriptDirectory))
if not os.path.exists(module_path):
# If cx_Freeze is used the value of the module_path variable at this point is in the following format.
# PathToExeFile\NameOfPythonSourceFile. This makes it necessary to strip off the file name to get the correct
# path.
module_path = os.path.dirname(module_path)
GetScriptDirectory.dir = os.path.dirname(module_path)
return GetScriptDirectory.dir
sys.path.append(os.path.join(GetScriptDirectory(), "lib"))
print(GetScriptDirectory())
print(sys.path)
如您所见,这并非易事!
【讨论】:
【参考方案7】:使用python 3.4+
import sys
from pathlib import Path
# As PosixPath
sys.path.append(Path(__file__).parent / "lib")
# Or as str as explained in https://***.com/a/32701221/11043825
sys.path.append(str(Path(__file__).parent / "lib"))
【讨论】:
Python2 兼容:import pathlib import os sys.path.append(os.path.dirname(file))【参考方案8】:您可以使用相关根目录中的python -m
运行脚本。并将“模块路径”作为参数传递。
示例:$ python -m module.sub_module.main # Notice there is no '.py' at the end.
另一个例子:
$ tree # Given this file structure
.
├── bar
│ ├── __init__.py
│ └── mod.py
└── foo
├── __init__.py
└── main.py
$ cat foo/main.py
from bar.mod import print1
print1()
$ cat bar/mod.py
def print1():
print('In bar/mod.py')
$ python foo/main.py # This gives an error
Traceback (most recent call last):
File "foo/main.py", line 1, in <module>
from bar.mod import print1
ImportError: No module named bar.mod
$ python -m foo.main # But this succeeds
In bar/mod.py
【讨论】:
推荐使用 m 开关,而不是 sys path hacks 哦,这比设置 PYTHONPATH 环境或 sys.path 答案要好。我今天学到了一些东西——对此我非常非常感激!【参考方案9】:我在你的例子中看到了一个 shebang。如果您以./bin/foo.py
而不是python ./bin/foo.py
运行bin 脚本,则可以选择使用shebang 更改$PYTHONPATH
变量。
您不能直接在 shebangs 中更改环境变量,因此您需要一个小的帮助脚本。将此python.sh
放入您的bin
文件夹:
#!/usr/bin/env bash
export PYTHONPATH=$PWD/lib
exec "/usr/bin/python" "$@"
然后将./bin/foo.py
的shebang更改为#!bin/python.sh
【讨论】:
【参考方案10】:当我们尝试从终端运行带有路径的 python 文件时。
import sys
#For file name
file_name=sys.argv[0]
#For first argument
dir= sys.argv[1]
print("File Name: , argument dir: ".format(file_name, dir)
保存文件(test.py)。
运行系统。
打开终端并转到保存文件的那个目录。 然后写
python test.py "/home/saiful/Desktop/bird.jpg"
回车
输出:
File Name: test, Argument dir: /home/saiful/Desktop/bird.jpg
【讨论】:
【参考方案11】:我用:
from site import addsitedir
然后,可以使用任何相对目录!
addsitedir('..\lib')
;这两个点表示首先移动(向上)一个目录。
请记住,这完全取决于您当前的工作目录是从什么开始的。 如果 C:\Joe\Jen\Becky,那么 addsitedir('..\lib') 导入到您的路径 C:\Joe\Jen\lib
C:\
|__Joe
|_ Jen
| |_ Becky
|_ lib
【讨论】:
感谢您的响应,但这并没有添加相对于当前运行脚本的路径。它添加了一个相对于当前工作目录的路径【参考方案12】:这就是我多次这样做的方式:
import os
import sys
current_path = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.join(current_path, "lib"))
【讨论】:
以上是关于Python:相对于当前运行脚本添加到 sys.path 的最佳方式的主要内容,如果未能解决你的问题,请参考以下文章