Làm thế nào để kiểm tra nếu một mô-đun python tồn tại mà không cần nhập nó


179

Tôi cần biết nếu một mô-đun python tồn tại, mà không cần nhập nó.

Nhập một cái gì đó có thể không tồn tại (không phải những gì tôi muốn):

try:
    import eggs
except ImportError:
    pass

4
Tôi tò mò, những nhược điểm của việc sử dụng nhập khẩu là gì?
Chuck

15
Nếu mô-đun của bạn có tác dụng phụ, gọi nhập có thể có hậu quả không mong muốn. Vì vậy, nếu bạn muốn kiểm tra phiên bản nào của tệp sẽ chạy trước, bạn có thể kiểm tra với câu trả lời bên dưới và thực hiện nhập sau. Tôi không cho rằng nên viết các mô-đun có tác dụng phụ - nhưng tất cả chúng ta đều là người trưởng thành và có thể tự đưa ra quyết định xung quanh việc chúng ta muốn viết mã nguy hiểm như thế nào.
yarbelk


1
@ArtOfWarfare Tôi chỉ đóng rằng câu hỏi mà bạn liên kết như là một bản sao của này một. Bởi vì câu hỏi này rõ ràng hơn và cũng là giải pháp được đề xuất ở đây tốt hơn tất cả các câu hỏi khác được liệt kê ở đó. Tôi muốn chỉ ra bất cứ ai muốn một câu trả lời cho giải pháp tốt hơn này là chỉ cho mọi người tránh xa nó.
Bakuriu

7
@Chuck Ngoài ra, mô-đun có thể tồn tại, nhưng bản thân nó có thể chứa lỗi nhập. Việc nhập ImportErrors như trong đoạn mã trên có thể dẫn đến việc chỉ ra mô-đun không tồn tại, khi thực tế nó có nhưng có lỗi.
Michael Barton

Câu trả lời:


198

Python2

Để kiểm tra xem nhập có thể tìm thấy cái gì đó trong python2 hay không, bằng cách sử dụng imp

import imp
try:
    imp.find_module('eggs')
    found = True
except ImportError:
    found = False

Để tìm hàng nhập khẩu chấm, bạn cần làm thêm:

import imp
try:
    spam_info = imp.find_module('spam')
    spam = imp.load_module('spam', *spam_info)
    imp.find_module('eggs', spam.__path__) # __path__ is already a list
    found = True
except ImportError:
    found = False

Bạn cũng có thể sử dụng pkgutil.find_loader(ít nhiều giống với phần python3

import pkgutil
eggs_loader = pkgutil.find_loader('eggs')
found = eggs_loader is not None

Python3

Python3 3,3

Bạn nên sử dụng importlib, Làm thế nào tôi đã làm điều này là:

import importlib
spam_loader = importlib.find_loader('spam')
found = spam_loader is not None

Kỳ vọng của tôi là, nếu bạn có thể tìm thấy một trình tải cho nó, thì nó tồn tại. Bạn cũng có thể thông minh hơn một chút về nó, như lọc ra những bộ tải bạn sẽ chấp nhận. Ví dụ:

import importlib
spam_loader = importlib.find_loader('spam')
# only accept it as valid if there is a source file for the module - no bytecode only.
found = issubclass(type(spam_loader), importlib.machinery.SourceFileLoader)

Python3 ≥ 3,4

Trong Python3.4 importlib.find_loader tài liệu python không được ủng hộ importlib.util.find_spec. Phương pháp được đề xuất là importlib.util.find_spec. Có những thứ khác giống như importlib.machinery.FileFinder, rất hữu ích nếu bạn đang tải một tệp cụ thể. Tìm hiểu làm thế nào để sử dụng chúng là vượt quá phạm vi của điều này.

import importlib
spam_spec = importlib.util.find_spec("spam")
found = spam_spec is not None

Điều này cũng hoạt động với nhập khẩu tương đối nhưng bạn phải cung cấp gói bắt đầu, vì vậy bạn cũng có thể làm:

import importlib
spam_spec = importlib.util.find_spec("..spam", package="eggs.bar")
found = spam_spec is not None
spam_spec.name == "eggs.spam"

Trong khi tôi chắc chắn có một lý do để làm điều này - tôi không chắc nó sẽ là gì.

CẢNH BÁO

Khi cố gắng tìm một mô hình con, nó sẽ nhập mô đun mẹ (cho tất cả các phương thức trên)!

food/
  |- __init__.py
  |- eggs.py

## __init__.py
print("module food loaded")

## eggs.py
print("module eggs")

were you then to run
>>> import importlib
>>> spam_spec = importlib.find_spec("food.eggs")
module food loaded
ModuleSpec(name='food.eggs', loader=<_frozen_importlib.SourceFileLoader object at 0x10221df28>, origin='/home/user/food/eggs.py')

ý kiến ​​hoan nghênh về việc này

Sự nhìn nhận

  • @rvighne cho nhập khẩu
  • @ lucas-guido cho python3.3 + depricating find_loader
  • @enpenax cho hành vi pkgutils.find_loader trong python2.7

3
Điều này chỉ hoạt động cho các mô-đun cấp cao nhất, không cho eggs.ham.spam.
hemflit

3
@hemflit nếu bạn muốn tìm spamtrong eggs.hambạn sẽ sử dụngimp.find_module('spam', ['eggs', 'ham'])
gitaarik

5
+1, nhưng impđang bị phản đối ủng hộ importlibbằng Python 3.
rvighne

4
Điều gì xảy ra nếu mô-đun được nhập chứa một "ImportError" thực tế. Đó là những gì đã xảy ra với tôi. Sau đó, mô-đun tồn tại nhưng sẽ không được "tìm thấy".
enpenax

1
Sau một năm, tôi đã gặp một vấn đề tương tự mà tôi đã đề cập ở trên và đang tìm kiếm một giải pháp cho Python 2: pkgutil.find_loader("my.package.module")trả về một trình tải nếu gói / mô-đun tồn tại và Nonenếu không. Vui lòng cập nhật câu trả lời của bạn cho Python 2, vì việc che giấu ImportError đã khiến tôi phát điên ngày hôm qua xP
enpenax

13

Sau khi sử dụng phản hồi của yarbelk, tôi đã thực hiện điều này để không phải nhập ìmp.

try:
    __import__('imp').find_module('eggs')
    # Make things with supposed existing module
except ImportError:
    pass

Hữu ích trong Django settings.pychẳng hạn.


4
Tôi đã đánh giá thấp, bởi vì mặt nạ này nhập lỗi trong mô-đun, điều này làm cho nó rất khó phát hiện ra lỗi.
enpenax

1
Downvote là một ý tưởng tồi, một ý tưởng tốt là "luôn luôn ghi lại các lỗi bị bắt". Đây là một ví dụ sau khi bạn viết như thế nào bạn muốn nó.
Zulu

2
Làm thế nào bạn sẽ ghi lại một lỗi nếu mô-đun đã nhập không thành công trên dòng 1 với ImportError và việc thử bắt của bạn làm cho nó thất bại trong âm thầm?
enpenax

Tôi vừa gặp phải vấn đề che giấu lỗi nhập khẩu trong cuộc sống thực và nó rất tệ (gây ra các bài kiểm tra đáng lẽ không vượt qua được!).
David đưa ra

Tôi tình
cờ

13

Python 3> = 3.6: ModuleNotFoundError

ModuleNotFoundErrorđã được giới thiệu trong python 3.6 và có thể được sử dụng cho mục đích này

try:
    import eggs
except ModuleNotFoundError:
    # Error handling
    pass

Lỗi được đưa ra khi một mô-đun hoặc một trong các cha mẹ của nó không thể được tìm thấy. Vì thế

try:
    import eggs.sub
except ModuleNotFoundError as err:
    # Error handling
    print(err)

sẽ in một thông báo trông giống như No module named 'eggs'nếu eggsmô-đun không thể được tìm thấy; nhưng sẽ in một cái gì đó giống như No module named 'eggs.sub'nếu subkhông thể tìm thấy mô-đun nhưng eggscó thể tìm thấy gói.

Xem tài liệu của hệ thống nhập khẩu để biết thêm thông tin vềModuleNotFoundError


1
Điều này không trả lời câu hỏi vì nó nhập gói nếu nó tồn tại
divenex

11

Python 2, không dựa vào ImportError

Cho đến khi câu trả lời hiện tại được cập nhật, đây là cách cho Python 2

import pkgutil
import importlib

if pkgutil.find_loader(mod) is not None:
    return importlib.import_module(mod)
return None

Tại sao câu trả lời khác?

Rất nhiều câu trả lời sử dụng bắt một ImportError. Vấn đề với điều đó là, chúng ta không thể biết cái gì némImportError .

Nếu bạn nhập mô-đun tồn tại của bạn và có một ImportErrormô-đun trong mô-đun của bạn (ví dụ: lỗi đánh máy trên dòng 1), kết quả sẽ là mô-đun của bạn không tồn tại. Bạn sẽ mất khá nhiều thời gian để quay lại để nhận ra rằng mô-đun của bạn tồn tại và ImportErrorbị bắt và khiến mọi thứ thất bại trong âm thầm.


Nó có thể không rõ ràng, nhưng tất cả trừ các khối mã đầu tiên không dựa vào ImportError- vui lòng chỉnh sửa nếu nó không rõ ràng với bạn.
yarbelk

Tôi thấy bạn đang sử dụng công cụ bắt nhập ImportError trong hai ví dụ Python 2 đầu tiên. Tại sao họ ở đó, sau đó?
enpenax

3
Điều này sẽ ném ImportError nếu mod == 'not_ex hiện_package.mymodule'. Xem giải pháp
Marcin Raczyński

1
Tất nhiên nó ném một lỗi nhập khẩu. Nó được cho là ném lỗi nhập nếu mô-đun không tồn tại. Bằng cách đó bạn có thể bắt nó nếu bạn cần. Vấn đề với các giải pháp khác là chúng che dấu các lỗi khác.
enpenax

Thử / ngoại trừ không có nghĩa là bạn không được đăng nhập hoặc đảm bảo mọi thứ. Bạn hoàn toàn có thể nắm bắt bất kỳ truy nguyên cơ bản nào và làm bất cứ điều gì bạn muốn với.
Zulu

8

câu trả lời của go_as như một lớp lót

 python -c "help('modules');" | grep module

6

Tôi đã gặp câu hỏi này trong khi tìm kiếm một cách để kiểm tra xem một mô-đun có được tải từ dòng lệnh hay không và muốn chia sẻ suy nghĩ của tôi cho những người đến sau tôi và tìm kiếm tương tự:

Phương pháp tệp tập lệnh Linux / UNIX : tạo một tệp module_help.py:

#!/usr/bin/env python

help('modules')

Sau đó, đảm bảo rằng nó có thể thực thi được: chmod u+x module_help.py

Và gọi nó với một pipeđến grep:

./module_help.py | grep module_name

Gọi hệ thống trợ giúp tích hợp . (Chức năng này dành cho sử dụng tương tác .) Nếu không có đối số nào được đưa ra, hệ thống trợ giúp tương tác sẽ khởi động trên bảng điều khiển trình thông dịch. Nếu đối số là một chuỗi , thì chuỗi đó được tra cứu dưới dạng tên của mô-đun , hàm, lớp, phương thức, từ khóa hoặc chủ đề tài liệu và một trang trợ giúp được in trên bàn điều khiển. Nếu đối số là bất kỳ loại đối tượng nào khác, một trang trợ giúp về đối tượng sẽ được tạo.

Phương thức tương tác : trong tải giao diện điều khiểnpython

>>> help('module_name')

Nếu tìm thấy hãy bỏ đọc bằng cách gõ q
Để thoát phiên tương tác python, nhấn Ctrl+D

Phương pháp tệp tập lệnh Windows cũng tương thích với Linux / UNIX và tốt hơn về tổng thể :

#!/usr/bin/env python

import sys

help(sys.argv[1])

Gọi nó từ lệnh như:

python module_help.py site  

Sẽ xuất:

Trợ giúp trên trang web mô-đun:

NAME trang web - Nối các đường dẫn tìm kiếm mô-đun cho các gói của bên thứ ba vào sys.path.

FILE /usr/lib/python2.7/site.py

MODULE DOCS http://docs.python.org/l Library / site

DESCRIPTION
...
:

và bạn phải nhấn qđể thoát chế độ tương tác.

Sử dụng mô-đun chưa biết:

python module_help.py lkajshdflkahsodf

Sẽ xuất:

không tìm thấy tài liệu Python nào cho 'lkajshdflkahsodf'

và thoát.


5

Sử dụng một trong các chức năng từ pkgutil , ví dụ:

from pkgutil import iter_modules

def module_exists(module_name):
    return module_name in (name for loader, name, ispkg in iter_modules())


4

Bạn chỉ có thể viết một tập lệnh nhỏ sẽ cố gắng nhập tất cả các mô-đun và cho bạn biết cái nào bị lỗi và cái nào đang hoạt động:

import pip


if __name__ == '__main__':
    for package in pip.get_installed_distributions():
        pack_string = str(package).split(" ")[0]
        try:
            if __import__(pack_string.lower()):
                print(pack_string + " loaded successfully")
        except Exception as e:
            print(pack_string + " failed with error code: {}".format(e))

Đầu ra:

zope.interface loaded successfully
zope.deprecation loaded successfully
yarg loaded successfully
xlrd loaded successfully
WMI loaded successfully
Werkzeug loaded successfully
WebOb loaded successfully
virtualenv loaded successfully
...

Lời cảnh báo này sẽ cố gắng nhập mọi thứ để bạn sẽ thấy những thứ như PyYAML failed with error code: No module named pyyamlvì tên nhập thực tế chỉ là yaml. Vì vậy, miễn là bạn biết hàng nhập khẩu của mình, điều này sẽ giúp bạn thực hiện thủ thuật này.


3

Tôi đã viết chức năng trợ giúp này:

def is_module_available(module_name):
    if sys.version_info < (3, 0):
        # python 2
        import importlib
        torch_loader = importlib.find_loader(module_name)
    elif sys.version_info <= (3, 3):
        # python 3.0 to 3.3
        import pkgutil
        torch_loader = pkgutil.find_loader(module_name)
    elif sys.version_info >= (3, 4):
        # python 3.4 and above
        import importlib
        torch_loader = importlib.util.find_spec(module_name)

    return torch_loader is not None

2

Bạn cũng có thể sử dụng importlibtrực tiếp

import importlib

try:
    importlib.import_module(module_name)
except ImportError:
    # Handle error

Điều này có vấn đề thực sự nhập khẩu nó. Tác dụng phụ và tất cả
yarbelk

2

Không có cách nào để kiểm tra một cách đáng tin cậy nếu "mô-đun chấm" có thể nhập được mà không cần nhập gói chính của nó. Nói điều này, có nhiều giải pháp cho vấn đề "làm thế nào để kiểm tra xem mô-đun Python có tồn tại không".

Giải pháp bên dưới giải quyết vấn đề mô-đun nhập khẩu có thể nâng cao ImportError ngay cả khi nó tồn tại. Chúng tôi muốn phân biệt tình huống đó với mô-đun không tồn tại.

Con trăn 2 :

import importlib
import pkgutil
import sys

def find_module(full_module_name):
    """
    Returns module object if module `full_module_name` can be imported. 

    Returns None if module does not exist. 

    Exception is raised if (existing) module raises exception during its import.
    """
    module = sys.modules.get(full_module_name)
    if module is None:
        module_path_tail = full_module_name.split('.')
        module_path_head = []
        loader = True
        while module_path_tail and loader:
            module_path_head.append(module_path_tail.pop(0))
            module_name = ".".join(module_path_head)
            loader = bool(pkgutil.find_loader(module_name))
            if not loader:
                # Double check if module realy does not exist
                # (case: full_module_name == 'paste.deploy')
                try:
                    importlib.import_module(module_name)
                except ImportError:
                    pass
                else:
                    loader = True
        if loader:
            module = importlib.import_module(full_module_name)
    return module

Con trăn 3 :

import importlib

def find_module(full_module_name):
    """
    Returns module object if module `full_module_name` can be imported. 

    Returns None if module does not exist. 

    Exception is raised if (existing) module raises exception during its import.
    """
    try:
        return importlib.import_module(full_module_name)
    except ImportError as exc:
        if not (full_module_name + '.').startswith(exc.name + '.'):
            raise

1

trong django.utils.module_loading.module_has_submodule


import sys
import os
import imp

def module_has_submodule(package, module_name):
    """
    check module in package
    django.utils.module_loading.module_has_submodule
    """
    name = ".".join([package.__name__, module_name])
    try:
        # None indicates a cached miss; see mark_miss() in Python/import.c.
        return sys.modules[name] is not None
    except KeyError:
        pass
    try:
        package_path = package.__path__   # No __path__, then not a package.
    except AttributeError:
        # Since the remainder of this function assumes that we're dealing with
        # a package (module with a __path__), so if it's not, then bail here.
        return False
    for finder in sys.meta_path:
        if finder.find_module(name, package_path):
            return True
    for entry in package_path:
        try:
            # Try the cached finder.
            finder = sys.path_importer_cache[entry]
            if finder is None:
                # Implicit import machinery should be used.
                try:
                    file_, _, _ = imp.find_module(module_name, [entry])
                    if file_:
                        file_.close()
                    return True
                except ImportError:
                    continue
            # Else see if the finder knows of a loader.
            elif finder.find_module(name):
                return True
            else:
                continue
        except KeyError:
            # No cached finder, so try and make one.
            for hook in sys.path_hooks:
                try:
                    finder = hook(entry)
                    # XXX Could cache in sys.path_importer_cache
                    if finder.find_module(name):
                        return True
                    else:
                        # Once a finder is found, stop the search.
                        break
                except ImportError:
                    # Continue the search for a finder.
                    continue
            else:
                # No finder found.
                # Try the implicit import machinery if searching a directory.
                if os.path.isdir(entry):
                    try:
                        file_, _, _ = imp.find_module(module_name, [entry])
                        if file_:
                            file_.close()
                        return True
                    except ImportError:
                        pass
                # XXX Could insert None or NullImporter
    else:
        # Exhausted the search, so the module cannot be found.
        return False

Điều này đáp ứng câu hỏi tiêu chuẩn của tôi khi lập trình python: WWDD (Django sẽ làm gì?) Và tôi nên nhìn vào đó
yarbelk
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.