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


98

我有一个充满脚本的目录(假设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平台,但是可以保持干净。


Answers:


25

如果您不想编辑每个文件

  • 安装你的资料库像一个正常的Python libray
  • 设置PYTHONPATH为您的lib

或者如果您愿意在每个文件中添加一行,则在顶部添加导入语句,例如

import import_my_lib

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


118

这是我用的:

import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))

2
我会做sys.path.insert(0,..),以便它不会被其他路径覆盖。
约翰·姜

真的没有办法让它自动运行吗?
丹尼斯·德伯纳迪

1
这有点冒险。如果__file__是相对于当前工作目录的相对文件名(例如setup.py),os.path.dirname(__file__)则将为空字符串。对于John Jiang提出的类似问题ekhumoro通用解决方案是非常可取的。
Cecil Curry

29

我正在使用:

import sys,os
sys.path.append(os.getcwd())

5
我经常这样做sys.path.append('.')
kimbo,

1
如果脚本从其他目录运行怎么办?例如从根目录通过提供完整的系统路径?那么os.getcwd()将返回“ /”
obayhan

2
永远不要这样做。无法保证当前的工作目录(CWD)就是您想的那样-尤其是在无法预见的极端情况下,您肯定应该已经目睹了一英里。__file__就像其他准开发人员一样参考。
Cecil Curry

14

创建一个包装器模块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

8

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

PYTHONPATH=.:$PYTHONPATH alembic revision --autogenerate -m "First revision"

并称之为一天!


docs.python.org/3/tutorial/modules.html#the-module-search-path说:“ sys.path从以下位置初始化:-包含输入脚本的目录”。我认为这是不正确的。

我倾向于这样做,尤其是在.envrc中,以便使用direnv甚至是自动和隔离的。
EdvardM

8

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

import sys
from pathlib import Path

sys.path.append(Path(__file__).parent / "lib")

兼容Python2:导入pathlib导入os sys.path.append(os.path.dirname(file))
迈克尔

5

您可以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

1
建议使用m开关来执行此操作,而不要使用sys path hack
Mr_and_Mrs_D

4

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

例如,这样一种神奇的咒语使用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)

如您所见,这绝非易事!


0

这个最适合我。用:

os.path.abspath('')

在Mac上,它应打印如下内容:

'/Users/<your username>/<path_to_where_you_at>'

为了获得到当前wd的绝对路径,这是更好的方法,因为现在您可以根据需要向上走,如下所示:

os.path.abspath('../')

现在:

 '/Users/<your username>/'

因此,如果您想utils从这里导入,'/Users/<your username>/'
剩下要做的就是:

import sys
sys.path.append(os.path.abspath('../'))

0

在您的示例中,我看到了一个爆炸。如果您将bin脚本运行为./bin/foo.py,而不是python ./bin/foo.py,则可以选择使用shebang更改$PYTHONPATH变量。

但是您不能直接在shebang中更改环境变量,因此您将需要一个小的帮助程序脚本。将此python.sh放入您的bin文件夹:

#!/usr/bin/env bash
export PYTHONPATH=$PWD/lib
exec "/usr/bin/python" "$@"

然后将您的shebang更改./bin/foo.py#!bin/python.sh


0

当我们尝试运行带有终端路径的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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.