检查 Python 包是否安装

172

在Python脚本中有什么好方法可以检查包是否已安装?我知道在解释器中很容易,但我需要在脚本中执行。

我猜我可以检查系统上是否有在安装期间创建的目录,但我觉得有更好的方法。 我试图确保Skype4Py包已安装,如果没有,我将安装它。

我的检查实现想法:

  • 检查典型安装路径中是否存在目录
  • 尝试导入包并抛出异常,然后再安装包

编写一个Python脚本来自动启动Skype并使用tcpdump收集数据包,以便分析在进行会议通话时网络的运行方式。 - Kevin
21个回答

157

如果您想使用Python脚本,只需像这样做:

Python 3.3+使用sys.modules和find_spec

import importlib.util
import sys

# For illustrative purposes.
name = 'itertools'

if name in sys.modules:
    print(f"{name!r} already in sys.modules")
elif (spec := importlib.util.find_spec(name)) is not None:
    # If you choose to perform the actual import ...
    module = importlib.util.module_from_spec(spec)
    sys.modules[name] = module
    spec.loader.exec_module(module)
    print(f"{name!r} has been imported")
else:
    print(f"can't find the {name!r} module")

Python 3:

try:
    import mymodule
except ImportError as e:
    pass  # module doesn't exist, deal with it.

Python 2:

try:
    import mymodule
except ImportError, e:
    pass  # module doesn't exist, deal with it.

11
警告:我今天遇到了一个情况,即在模块内部抛出了ImportError。这种情况不应经常发生,但请注意,这种检查并不总是可靠的。 - Koen Bok
8
不仅会检查,还会导入它;请参阅https://dev59.com/F2Yq5IYBdhLWcg3w-FXQ。 - Jorge Leitao
8
是的,通常情况下您想要的就是这样。您可以使用导入工具模块进行更复杂的检查,但大多数时候您关心某个模块是否安装是因为您想要使用它。 - Christopher
1
当然没问题,但那是一个完全不同的问题。如果在您的查找路径中有两个名称相同的不同模块或包,则会出现问题。 - Christopher
1
@Oak_3260548 这是一个新的海象操作符:https://docs.python.org/3/whatsnew/3.8.html - Pugsley
显示剩余4条评论

98

从Python 3.3版本开始,您可以使用find_spec()方法

import importlib.util

# For illustrative purposes.
package_name = 'pandas'

spec = importlib.util.find_spec(package_name)
if spec is None:
    print(package_name +" is not installed")

8
干净简洁,这里是最佳答案。这需要更多点赞。 - David Parks
15
注意:如果包名包含破折号,find_spec() 将无法找到它,除非你使用点号。例如:importlib.util.find_spec('google-cloud-logging') 找不到任何东西,而 importlib.util.find_spec('google.cloud.logging') 可以找到。官方名称和pip显示的名称都带有破折号而不是点号。这可能会令人感到困惑,因此请谨慎使用。 - dwich

67

更新的回答

更好的方法是:

import subprocess
import sys

reqs = subprocess.check_output([sys.executable, '-m', 'pip', 'freeze'])
installed_packages = [r.decode().split('==')[0] for r in reqs.split()]

结果:
print(installed_packages)

[
    "Django",
    "six",
    "requests",
]

检查是否已安装requests

if 'requests' in installed_packages:
    # Do something

为什么这样做?有时会出现应用程序名称冲突。从应用程序命名空间导入无法完整显示系统上安装了什么。
请注意,建议的解决方案有效:
  • 当使用pip从PyPI或其他替代源(如pip install http://some.site/package-name.zip或任何其他档案类型)安装时。
  • 手动安装时使用python setup.py install
  • 从系统存储库安装时,例如sudo apt install python-requests
可能不适用的情况:
  • 在开发模式下安装,例如python setup.py develop
  • 在开发模式下安装,例如pip install -e / path / to / package / source /

旧答案

更好的方法是:

import pip
installed_packages = pip.get_installed_distributions()

对于pip>=10.x,请使用:

from pip._internal.utils.misc import get_installed_distributions

为什么这样做?有时会出现应用程序名称冲突。从app命名空间导入,不能完全了解系统上安装了什么。

因此,您会得到一个pkg_resources.Distribution对象列表。以下是一个示例:

print installed_packages
[
    "Django 1.6.4 (/path-to-your-env/lib/python2.7/site-packages)",
    "six 1.6.1 (/path-to-your-env/lib/python2.7/site-packages)",
    "requests 2.5.0 (/path-to-your-env/lib/python2.7/site-packages)",
]

把它们列成一张清单:

flat_installed_packages = [package.project_name for package in installed_packages]

[
    "Django",
    "six",
    "requests",
]

检查是否已安装requests库:

if 'requests' in flat_installed_packages:
    # Do something

2
正如其他答案中提到的那样,错误可能没有被捕获。因此,我认为这应该是被接受的答案。 - Jorrick Sleijster
1
这种方法无法与诸如“json”之类的包一起使用(当import json实际上正在工作时返回False)。在这种情况下,可以采用ice.nicer的方法。 - mountrix
1
我需要使用更新的答案中的检查if“u'requests'”in installed_packages:才能让它对我起作用。在我的macOS python上,print()的输出如下:[..., u'pytz', u'requests', ...] - Buju
1
@ArturBarseghyan 我使用的是Python 2.7.10(macOS系统自带的Python)...实际上我必须使用if "requests'"(注意末尾的单引号)。这根本没有意义。 - Buju
2
这并没有回答问题:“包'x'是否已安装?”。有许多方法可以安装软件包而不使用pip。例如,您可以从源代码构建和安装软件包。这个答案无法找到那些。@ice.nicer关于find_spec()的答案是正确的。 - Arthur
显示剩余15条评论

43

如果您想从终端获取检查结果,可以运行以下命令

pip3 show package_name

如果没有返回任何内容,则表示该软件包未安装。

如果您希望自动化此检查,以便在缺少软件包时可以安装它,您可以在bash脚本中加入以下内容:

pip3 show package_name 1>/dev/null #pip for Python 2
if [ $? == 0 ]; then
   echo "Installed" #Replace with your actions
else
   echo "Not Installed" #Replace with your actions, 'pip3 install --upgrade package_name' ?
fi

这看起来很不错,但为什么它总是对我返回“未安装”? - Elliptica
你能分享一下“$pip3 --version”和“$pip3 show package_name”的输出吗?在这里,你需要将package_name替换为所需的包名。 - Ognjen Vukovic
Shell提示:您可以使用if直接测试成功的命令:if pip3 show package_name 1>/dev/null; then ... - MestreLion

11

打开命令提示符并输入

pip3 list

8

在终端中输入以下命令:

pip show some_package_name

例子

pip show matplotlib

6

作为这个回答的延伸:

对于Python 2.*, pip show <package_name>将执行相同的任务。

例如,pip show numpy将返回以下内容或类似内容:

Name: numpy
Version: 1.11.1
Summary: NumPy: array processing for numbers, strings, records, and objects.
Home-page: http://www.numpy.org
Author: NumPy Developers
Author-email: numpy-discussion@scipy.org
License: BSD
Location: /home/***/anaconda2/lib/python2.7/site-packages
Requires: 
Required-by: smop, pandas, tables, spectrum, seaborn, patsy, odo, numpy-stl, numba, nfft, netCDF4, MDAnalysis, matplotlib, h5py, GridDataFormats, dynd, datashape, Bottleneck, blaze, astropy

非脚本但适用于命令行检查。谢谢。 - Jj Rivero

5
您可以使用setuptools中的pkg_resources模块。例如:
import pkg_resources

package_name = 'cool_package'
try:
    cool_package_dist_info = pkg_resources.get_distribution(package_name)
except pkg_resources.DistributionNotFound:
    print('{} not installed'.format(package_name))
else:
    print(cool_package_dist_info)

请注意,Python模块和Python包之间存在差异。一个包可以包含多个模块,而模块的名称可能与包名称不匹配。

是的,包和模块之间的区别非常重要。例如,python-magicfile-magic 都提供名为 magic 的模块,但它们的接口彼此不兼容。此外,在 3.8 版本中添加了 importlib.metadata,我认为它非常方便。 - ish-west

4

方法一

要查找包是否存在,请使用pip3 list命令。

#**pip3 list** will display all the packages and **grep** command will search for a particular package
pip3 list | grep your_package_name_here

Method 2

You can use ImportError

try:
    import your_package_name
except ImportError as error:
    print(error,':( not found')

Method 3

!pip install your_package_name
import your_package_name
...
...

4
if pip list | grep -q \^'PACKAGENAME\s'
  # installed ...
else
  # not installed ...
fi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接