Python: 在个人 Python 包中包含第三方库

Python: include a third party library in a personal Python package

我想在我的 Python 脚本文件夹中包含一个第三方库,以便将它们一起分发(我知道分发许可证,并且这个库可以分发)。这是为了避免在另一台机器上安装库。

说我有一个脚本 (my_script.py),它调用这个外部库。我试图将此库从 Python 目录的 site-packages 子目录复制到我的文件所在的目录中,但它似乎还不够(我认为原因在这个 __init__.py可能需要文件夹位于 PYTHONPATH).

中的库

my_script.py 中插入一些代码行以将其文件夹临时附加到 sys.path 以使所有功能正常运行是否合理?

例如,如果我有一个类似这样的结构:

Main_folder
my_script.py
  /external_lib_folder
   __init__.py
   external_lib.py

external_lib_folder 是我从站点包复制并插入到我的 Main_folder 中的外部库,如果我将这些行(例如)写在 my_script.py 中可以吗?

import os,sys
main_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(main_dir)

编辑

我最终选择了 sys.path.append 解决方案。我将这些行添加到我的 my_script.py:

import os, sys

# temporarily appends the folder containing this file into sys.path
main_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),'functions')
sys.path.append(main_dir)

无论如何,我选择将此作为编辑插入到我的问题中并接受 Torxed 的回答,因为他花时间帮助我(当然也因为他的解决方案也有效)。

Python3

import importlib.machinery, imp
namespace = 'external_lib'
loader = importlib.machinery.SourceFileLoader(namespace, '/home/user/external_lib_folder/external_lib.py')
external_lib = loader.load_module(namespace)

# How to use it:
external_lib.function(data_or_something)

这将是在 Python 3.
中加载自定义路径的理想方式 不完全确定这是你想要的,但它足够相关 post 作为添加到 sys.path.

的替代方法

Python2

在 python 2 中你可以这样做(如果我没记错的话,自从我使用旧版本的 Python 以来已经有一段时间了):

external_lib = __import__('external_lib_folder')

然而,这确实需要您在 sad 脚本中保留 __init__.py 和正确的函数声明,否则它将失败。
**同样重要的是,您尝试从中导入的文件夹与 sad 文件夹中的 __init__.py 脚本尝试从中导入其子库的名称相同,例如 geopy 将是:

./myscript.py
./geopy/
./geopy/__init__.py
./geopy/compat.py
...

myscript.py 的代码如下所示:

handle = __import__('geopy')
print(handle)

这将产生以下输出:

[user@machine project]$ python2 myscript.py 
<module 'geopy' from '/home/user/project/geopy/__init__.pyc'>

[user@machine project]$ tree -L 2
.
├── geopy
│   ├── compat.py
│   ├── compat.pyc
│   ├── distance.py
│   ├── distance.pyc
│   ├── exc.py
│   ├── exc.pyc
│   ├── format.py
│   ├── format.pyc
│   ├── geocoders
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── location.py
│   ├── location.pyc
│   ├── point.py
│   ├── point.pyc
│   ├── units.py
│   ├── units.pyc
│   ├── util.py
│   ├── util.pyc
│   └── version.pyc
└── myscript.py

2 directories, 20 files

因为在 geopy__init__.py 中,它定义了诸如 from geopy.point import Point 之类的导入,它需要存在 geopy 的命名空间或文件夹。
因为你不能将文件夹重命名为 functions 并在其中放置一个名为 geopy 的文件夹,因为这不起作用,也不能将 geopy 的内容放在名为 functions 因为那不是 geopy 会寻找的。

添加路径到sys.path (Py2 + 3)

如评论中所述,您还可以在导入之前将文件夹添加到 sys.path 变量。

import sys
sys.path.insert(0, './functions')

import geopy
print(geopy)

>>> <module 'geopy' from './functions/geopy/__init__.pyc'>

为什么这是个坏主意: 它会起作用,并且被许多人使用。可能出现的问题是您 可能 替换系统功能,或者如果您不注意导入内容的位置,可能会从其他文件夹加载其他模块。大多数情况下使用 .insert(0, ...) 并确保您确实想要冒险用 "shady" 路径名替换系统内置函数。

你的建议是不好的做法,是一个弱安排。最好的解决方案(也很容易做到)是正确打包并添加显式依赖项,如下所示:

从设置工具导入设置

setup(name='funniest',
      version='0.1',
      description='The funniest joke in the world',
      url='http://github.com/storborg/funniest',
      author='Flying Circus',
      author_email='flyingcircus@example.com',
      license='MIT',
      packages=['funniest'],
      install_requires=[
          'markdown',
      ],
      zip_safe=False)

如果第三方库在 pipy 上,这将起作用。如果不是,请使用此:

setup(
    ...
    dependency_links=['http://github.com/user/repo/tarball/master#egg=package-1.0']
    ...
)

(包装见this explanation)。