Python:相对于当前运行脚本添加到sys.path的最佳方法

Jam*_*arr 79 python python-import

我有一个充满脚本的目录(比方说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
Run Code Online (Sandbox Code Playgroud)

这有点麻烦,丑陋,必须在每个文件的开头粘贴.有一个更好的方法吗?

我真正希望的是这样的顺利:

#!/usr/bin/python
import sys.path
from os.path import pardir, sep
sys.path.append_relative(pardir + sep + "lib")

import mylib
Run Code Online (Sandbox Code Playgroud)

或者甚至更好,当我的编辑(或其他有提交访问权限的人)决定重新排序导入作为其清理过程的一部分时,这些东西不会破坏:

#!/usr/bin/python --relpath_append ../lib
import mylib
Run Code Online (Sandbox Code Playgroud)

这不会直接移植到非posix平台,但它会保持干净.

jte*_*ace 101

这是我使用的:

import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))
Run Code Online (Sandbox Code Playgroud)

  • 我会做sys.path.insert(0,..),以便它不会被其他路径覆盖。 (2认同)
  • 这有点冒险。如果 `__file__` 是相对于当前工作目录的相对文件名(例如,`setup.py`),则 `os.path.dirname(__file__)` 将是 **空字符串。** 对于此和 [类似John Jiang 提出的问题](/sf/users/205110951/ Jiang), [ekhumoro](/sf/users/68909501/) 的[更通用的解决方案](/sf/answers/606449021/) 是非常可取的。 (2认同)

小智 23

我正在使用:

import sys,os
sys.path.append(os.getcwd())
Run Code Online (Sandbox Code Playgroud)

  • **永远不要这样做。**当前工作目录(CWD)*不*保证是您所认为的那样 - 特别是在不可预见的边缘情况下,您绝对应该在一英里之外看到。只需像任何准理智的开发人员一样引用“__file__”即可。 (8认同)
  • 我经常只做`sys.path.append('.')` (7认同)
  • 如果脚本从不同的目录运行怎么办?例如通过给出完整的系统路径从根目录?那么 os.getcwd() 将返回“/” (4认同)

Anu*_*yal 19

如果您不想编辑每个文件

  • 安装你的库像普通的python libray
  • 设置PYTHONPATH为你的lib

或者如果您愿意为每个文件添加一行,请在顶部添加一个import语句,例如

import import_my_lib
Run Code Online (Sandbox Code Playgroud)

保持import_my_lib.py在bin中,import_my_lib可以正确地将python路径设置为lib您想要的任何内容


ekh*_*oro 13

创建一个包装器模块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
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用以下命令替换脚本顶部的所有残缺:

#!/usr/bin/python
from lib import mylib
Run Code Online (Sandbox Code Playgroud)


Eya*_*vin 9

您可以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
Run Code Online (Sandbox Code Playgroud)

  • 使用 m 开关是执行此操作的推荐方法,而不是 sys 路径 hack (4认同)

Gol*_*Jer 7

使用python 3.4+
禁止使用cx_freeze或在IDLE中使用。

import sys
from pathlib import Path

sys.path.append(Path(__file__).parent / "lib")
Run Code Online (Sandbox Code Playgroud)


Kha*_*Hua 6

如果您不想以任何方式更改脚本内容,请将当前工作目录添加.到$ PYTHONPATH(请参阅下面的示例)

PYTHONPATH=.:$PYTHONPATH alembic revision --autogenerate -m "First revision"
Run Code Online (Sandbox Code Playgroud)

并称它为一天!


Ben*_*Key 5

提供的每个答案都存在一个问题,可以概括为“只需将这个神奇的咒语添加到脚本的开头即可。看看只用一两行代码可以做什么。” 它们不会在所有可能的情况下都起作用!

例如,一种这样的魔法咒语使用__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)
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这不是一件容易的事!


gil*_*niy 5

这就是我多次这样做的方式:

import os
import sys

current_path = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.join(current_path, "lib"))
Run Code Online (Sandbox Code Playgroud)