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

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

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

I have a directory full of scripts (let’s say project/bin). I also have a library located in project/lib and want the scripts to automatically load it. This is what I normally use at the top of each script:

#!/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

This is kind of cumbersome, ugly, and has to be pasted at the beginning of every file. Is there a better way to do this?

Really what I’m hoping for is something as smooth as this:

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

import mylib

Or even better, something that wouldn’t break when my editor (or someone else who has commit access) decides to reorder the imports as part of its clean-up process:

#!/usr/bin/python --relpath_append ../lib
import mylib

That wouldn’t port directly to non-posix platforms, but it would keep things clean.


回答 0

如果您不想编辑每个文件

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

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

import import_my_lib

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

If you don’t want to edit each file

  • Install you library like a normal python libray
    or
  • Set PYTHONPATH to your lib

or if you are willing to add a single line to each file, add a import statement at top e.g.

import import_my_lib

keep import_my_lib.py in bin and import_my_lib can correctly set the python path to whatever lib you want


回答 1

这是我用的:

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

This is what I use:

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

回答 2

我正在使用:

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

I’m using:

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

回答 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

Create a wrapper module project/bin/lib, which contains this:

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

Then you can replace all the cruft at the top of your scripts with:

#!/usr/bin/python
from lib import mylib

回答 4

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

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

并称之为一天!

If you don’t want to change the script content in any ways, prepend the current working directory . to $PYTHONPATH (see example below)

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

And call it a day!


回答 5

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

import sys
from pathlib import Path

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

Using python 3.4+
Barring the use of cx_freeze or using in IDLE. 😃

import sys
from pathlib import Path

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

回答 6

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

You can run the script with python -m from the relevant root dir. And pass the “modules path” as argument.

Example: $ python -m module.sub_module.main # Notice there is no '.py' at the end.


Another example:

$ 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

回答 7

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

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

如您所见,这绝非易事!

There is a problem with every answer provided that can be summarized as “just add this magical incantation to the beginning of your script. See what you can do with just a line or two of code.” They will not work in every possible situation!

For example, one such magical incantation uses file. Unfortunately, if you package your script using cx_Freeze or you are using IDLE, this will result in an exception.

Another such magical incantation uses os.getcwd(). This will only work if you are running your script from the command prompt and the directory containing your script is the current working directory (that is you used the cd command to change into the directory prior to running the script). Eh gods! I hope I do not have to explain why this will not work if your Python script is in the PATH somewhere and you ran it by simply typing the name of your script file.

Fortunately, there is a magical incantation that will work in all the cases I have tested. Unfortunately, the magical incantation is more than just a line or two of code.

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)

As you can see, this is no easy task!


回答 8

这个最适合我。用:

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('../'))

This one works best for me. Use:

os.path.abspath('')

On mac it should print something like:

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

To get the abs path to the current wd, this one is better because now you can go up if you want, like this:

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

And now:

 '/Users/<your username>/'

So if you wanna import utils from here '/Users/<your username>/'
All you’ve got left to do is:

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

回答 9

在您的示例中,我看到了一个爆炸。如果您将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

I see a shebang in your example. If you’re running your bin scripts as ./bin/foo.py, rather than python ./bin/foo.py, there’s an option of using the shebang to change $PYTHONPATH variable.

You can’t change environment variables directly in shebangs though, so you’ll need a small helper script. Put this python.sh into your bin folder:

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

And then change the shebang of your ./bin/foo.py to be #!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

When we try to run python file with path from terminal.

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)

Save the file (test.py).

Runing system.

Open terminal and go the that dir where is save file. then write

python test.py "/home/saiful/Desktop/bird.jpg"

Hit enter

Output:

File Name: test, Argument dir: /home/saiful/Desktop/bird.jpg