【问题标题】:Local collection of Python packages: best way to import them?Python 包的本地集合:导入它们的最佳方式?
【发布时间】:2013-02-22 14:40:34
【问题描述】:

我需要发送一个 集合 Python 程序,这些程序使用多个 存储在本地 Library 目录中:目标是避免让用户在使用之前安装包我的程序(软件包在Library 目录中提供)。导入Library 中包含的包的最佳方法是什么?

我尝试了三种方法,但没有一种是完美的:有没有更简单、更可靠的方法?还是这些方法中的一种是最好的?

  1. 在第一种方法中,将Library文件夹简单地添加到库路径中:

    import sys
    import os
    sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'Library'))
    
    import package_from_Library
    

    Library 文件夹放在开头,以便我的程序附带的软件包优先于用户安装的相同模块(这样我可以确定它们有正确的版本来使用我的程序)。当Library 文件夹不在当前目录中时,此方法也有效,这很好。然而,这种方法有缺点。我的每个程序都将相同路径的副本添加到sys.path,这是一种浪费。此外,所有程序都必须包含相同的三个路径修改行,这违反了不要重复自己的原则。

  2. 对上述问题的改进在于尝试只添加一次Library 路径,方法是在导入的模块中进行:

    # In module add_Library_path:
    sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'Library'))
    

    然后在我的每个程序中使用:

    import add_Library_path
    import package_from_Library
    

    这样,得益于CPython的缓存机制,模块add_Library_path只运行一次,Library路径只添加到sys.path中一次。然而,这种方法的一个缺点是import add_Library_path 有一个不可见的副作用,并且导入的顺序很重要:这使得代码更不清晰,更脆弱。此外,这会迫使我分发的程序包含用户不会使用的add_Library_path.py 程序。

  3. 来自Library

    Python modules 也可以通过将其制成一个包(其中存储有空的__init__.py 文件)来导入,这样可以:

    from Library import module_from_Library
    

    但是,对于Library 中的,这会中断,因为它们可能会执行类似于from xlutils.filter import … 的操作,这会中断,因为xlutilssys.path 中找不到。因此,此方法有效,但仅当在 Library 中包含模块时才有效。

所有这些方法都有一些缺点。

有没有更好的方法来运送程序,其中包含存储在本地 Library 目录中的一组包(他们使用的)?还是上述方法之一(方法 1?)是最好的方法之一?

PS:在我的例子中,Library 中的所有包都是纯 Python 包,但适用于任何操作系统的更通用的解决方案是最好的。

PPS:目标是用户能够使用我的程序而无需安装任何东西(除了复制我定期发送的目录之外),就像上面的示例一样。

PPPS:更准确地说,我们的目标是通过以下方式灵活地更新我从Library 收集的程序及其相关的第三方包我的用户对包含我的程序和“隐藏”第三方包的Library 文件夹的目录进行简单复制。 (我经常更新,所以我不想强迫用户也更新他们的 Python 发行版。)

【问题讨论】:

  • 任何操作系统!

标签: python module package local software-distribution


【解决方案1】:

sys.path() 混在一起会导致痛苦...modern package templateDistribute 包含大量信息,部分是为了解决您的问题。

我要做的是设置 setup.py 将所有包安装到特定的站点包位置,或者如果你可以将它安装到系统的站点包。在前一种情况下,本地站点包将被添加到系统/用户的 PYTHONPATH 中。在后一种情况下,无需更改任何内容

您也可以使用批处理文件来设置 python 路径。或者将 python 可执行文件更改为指向包含修改后的 PYTHONPATH 的 shell 脚本,然后执行 python 解释器。当然,后者意味着您必须有权访问用户的机器,而您没有。但是,如果您的用户只运行脚本而不导入您自己的库,您可以使用自己的脚本包装器:

#!/path/to/my/python

/path/to/my/python 脚本类似于:

#!/bin/sh
PYTHONPATH=/whatever/lib/path:$PYTHONPATH /usr/bin/python $*

【讨论】:

  • 我添加了两个 PS 两个问题,以表明我不希望用户必须做任何事情(就像我给出的解决方案一样)。
  • 很遗憾,我无法访问用户的计算机,因为我“运送”了他们的程序(而且,我再次寻找他们不必做任何事情的解决方案)。跨度>
  • +1 评论!好问题。 :) 实际上,我也特别发布了 Python,安装了一些模块(针对他们的平台);他们通过简单的副本“安装”它。他们通过设置命令路径的批处理文件访问我的程序,以便在运行 python 命令时使用随附的 python。我不想太频繁地安装这个 Python+base 模块,因为更新它对他们来说很麻烦。相反,我想根据我的程序后续版本的需要,在本地 Library 文件夹中“隐藏”一些简单的附加包。
  • 好点。 :) 是的,这对他们有用。然而——我忘了​​提——一些用户不使用相同的方法,也不必运行批处理文件(包括我在内)。你让我更新了我的问题。 :) 我可以建议您在答案中添加一些 cmets 吗?我会为它 +1,因为它充满了对其他人有用的好主意。
  • 对该问题+1,因为它可能对其他用户有用。就我而言,它不能满足在不同操作系统上运行发行版的需要(Windows 版本有一个批处理文件,用于设置路径并制作正确的 CD;Mac 不需要这样的批处理文件操作系统/Linux 版本)。
【解决方案2】:

我认为你应该看看path import hooks,它允许在搜索模块时修改 python 的行为。

例如,您可以尝试像 kde 的脚本引擎对 python 插件所做的那样[1]。 它向sys.path 添加了一个特殊标记(就像"<plasmaXXXXXX>"XXXXXX 是一个随机数,只是为了避免名称冲突),然后当python 尝试导入模块并且在其他路径中找不到它们时,它会调用您的进口商可以处理它。

一个更简单的替代方法是使用一个主脚本作为启动器,它只是将路径添加到sys.path 并执行目标文件(这样您就可以安全地避免在每个文件上放置sys.path.append(...) 行)。

另一种适用于 python2.6+ 的替代方法是将库安装在 per-user site-packages 目录下。


[1] 你可以在/usr/share/kde4/apps/plasma_scriptengine_python 下找到源代码,用kde 安装linux。

【讨论】:

  • 导入钩子页面很有趣,谢谢!我在我的问题中添加了两个 PS 以使其更准确。
猜你喜欢
  • 2021-06-08
  • 1970-01-01
  • 1970-01-01
  • 2018-10-15
  • 2010-10-15
  • 2010-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多