Cara memeriksa apakah modul python ada tanpa mengimpornya


179

Saya perlu tahu apakah ada modul python, tanpa mengimpornya.

Mengimpor sesuatu yang mungkin tidak ada (bukan yang saya inginkan):

try:
    import eggs
except ImportError:
    pass

4
Saya ingin tahu, apa kerugian menggunakan impor?
Chuck

15
Jika modul Anda memiliki efek samping, memanggil impor mungkin memiliki konsekuensi yang tidak diinginkan. Jadi, jika Anda ingin memeriksa versi file mana yang akan dijalankan terlebih dahulu, Anda dapat melakukan periksa dengan jawaban di bawah ini, dan lakukan impor nanti. Saya tidak menyarankan itu adalah ide yang baik untuk menulis modul dengan efek samping - tetapi kita semua orang dewasa dan dapat membuat keputusan sendiri tentang betapa berbahayanya kita ingin kode.
yarbelk


1
@ArtOfWarfare Aku hanya menutup bahwa pertanyaan Anda terhubung sebagai duplikat dari ini satu. Karena pertanyaan ini lebih jelas dan juga solusi yang diusulkan di sini lebih baik daripada semua yang terdaftar di sana. Saya lebih suka menunjuk siapa pun yang menginginkan jawaban untuk solusi yang lebih baik ini daripada mengarahkan orang menjauh darinya.
Bakuriu

7
@Chuck Selain itu modul mungkin ada, tetapi mungkin saja mengandung kesalahan impor. Menangkap ImportErrors seperti dalam kode di atas dapat mengarah ke menunjukkan modul tidak ada, padahal sebenarnya ada kesalahan.
Michael Barton

Jawaban:


198

Python2

Untuk memeriksa apakah impor dapat menemukan sesuatu di python2, gunakan imp

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

Untuk menemukan impor bertitik, Anda perlu melakukan lebih banyak:

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

Anda juga dapat menggunakan pkgutil.find_loader(kurang lebih sama dengan bagian python3

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

Python3

Python3 ≤ 3.3

Anda harus menggunakan importlib, Bagaimana saya melakukan ini adalah:

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

Harapan saya adalah, jika Anda dapat menemukan loader untuknya, maka itu ada. Anda juga bisa sedikit lebih pintar tentangnya, seperti memfilter loader apa yang akan Anda terima. Sebagai contoh:

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

Dalam Python3.4 importlib.find_loader dokumen python sudah tidak digunakan lagi importlib.util.find_spec. Metode yang disarankan adalah importlib.util.find_spec. Ada yang lain suka importlib.machinery.FileFinder, yang berguna jika Anda mencari file tertentu untuk memuat. Mencari tahu bagaimana menggunakannya adalah di luar cakupan ini.

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

Ini juga berfungsi dengan impor relatif tetapi Anda harus menyediakan paket awal, sehingga Anda juga bisa melakukan:

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

Meskipun saya yakin ada alasan untuk melakukan ini - saya tidak yakin apa yang akan terjadi.

PERINGATAN

Ketika mencoba menemukan submodule, itu akan mengimpor modul induk (untuk semua metode di atas)!

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')

komentar selamat datang untuk menyiasati ini

Ucapan Terima Kasih

  • @rvighne untuk importlib
  • @ lucas-guido untuk python3.3 + depricating find_loader
  • @enpenax untuk perilaku pkgutils.find_loader di python2.7

3
Ini hanya berfungsi untuk modul tingkat atas, bukan untuk eggs.ham.spam.
hemflit

3
@hemflit jika Anda ingin mencari spamdi eggs.hamAnda akan menggunakanimp.find_module('spam', ['eggs', 'ham'])
gitaarik

5
1, tetapi impyang usang dalam mendukung importlibPython 3.
rvighne

4
Bagaimana jika modul yang diimpor berisi "ImportError" yang sebenarnya. Itulah yang terjadi pada saya. Maka modul ada tetapi tidak akan "ditemukan".
enpenax

1
Setelah satu tahun saya menemukan masalah yang sama saya sebutkan di atas dan sedang menggali solusi untuk Python 2: pkgutil.find_loader("my.package.module")mengembalikan loader jika paket / modul ada dan Nonejika tidak. Harap perbarui jawaban Anda untuk Python 2, karena menyembunyikan ImportError membuat saya gila kemarin xP
enpenax

13

Setelah menggunakan respons yarbelk, saya membuat ini untuk tidak harus mengimpor ìmp.

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

Berguna dalam Django settings.pymisalnya.


4
Saya downvoted, karena ini topeng kesalahan impor dalam modul, yang membuatnya sangat sulit untuk menemukan kesalahan.
enpenax

1
Downvote adalah ide yang buruk, praktik yang baik adalah "selalu mencatat kesalahan". Ini adalah contoh setelah Anda menulis seperti yang Anda inginkan.
Zulu

2
Bagaimana Anda mencatat kesalahan jika modul yang diimpor gagal pada baris 1 dengan ImportError dan try catch Anda membuatnya gagal secara diam-diam?
enpenax

Saya baru saja mengalami masalah masking-impor-kesalahan dalam kehidupan nyata, dan itu buruk (menyebabkan tes yang seharusnya gagal lulus!).
David Diberi

Saya menemukan bahwa di mana seseorang menggunakan kesalahan itu untuk memicu monkeypatch pada modul yang berbeda ... itu gila untuk menemukan
yarbelk

13

Python 3> = 3.6: ModuleNotFoundError

The ModuleNotFoundErrortelah diperkenalkan di python 3.6 dan dapat digunakan untuk tujuan ini

try:
    import eggs
except ModuleNotFoundError:
    # Error handling
    pass

Kesalahan muncul ketika modul atau salah satu dari orang tuanya tidak dapat ditemukan. Begitu

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

akan mencetak pesan yang terlihat seperti No module named 'eggs'jika eggsmodul tidak dapat ditemukan; tetapi akan mencetak sesuatu seperti No module named 'eggs.sub'jika hanya submodul tidak dapat ditemukan tetapi eggspaket dapat ditemukan.

Lihat dokumentasi sistem impor untuk info lebih lanjut tentangModuleNotFoundError


1
Ini tidak menjawab pertanyaan karena mengimpor paket jika ada
divenex

11

Python 2, tanpa mengandalkan ImportError

Sampai jawaban saat ini diperbarui, berikut adalah cara untuk Python 2

import pkgutil
import importlib

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

Mengapa ada jawaban lain?

Banyak jawaban memanfaatkan penangkapan ImportError. Masalahnya adalah, kita tidak bisa tahu apa yang melempar ImportError.

Jika Anda mengimpor Anda punah modul dan kebetulan ada seorang ImportErrordalam modul Anda (misalnya kesalahan ketik pada baris 1), hasilnya akan bahwa modul Anda tidak ada. Ini akan membawa Anda cukup banyak mundur untuk mengetahui bahwa modul Anda ada dan ImportErrortertangkap dan membuat semuanya gagal diam-diam.


Mungkin tidak jelas, tetapi semua kecuali blok kode pertama tidak dapat diandalkan ImportError- harap edit jika tidak jelas bagi Anda.
yarbelk

Saya melihat Anda menggunakan tangkapan ImportError di dua contoh Python 2 pertama. Mengapa mereka ada di sana?
enpenax

3
Ini melempar ImportError jika mod == 'not_existing_package.mymodule'. Lihat solusi
Marcin Raczyński

1
Tentu saja ada kesalahan impor. Seharusnya melempar kesalahan impor jika modul tidak ada. Dengan begitu Anda bisa menangkapnya jika perlu. Masalah dengan solusi lain adalah mereka menutupi kesalahan lain.
enpenax

Coba / kecuali tidak berarti Anda tidak boleh login atau memastikan sesuatu. Anda dapat sepenuhnya menangkap traceback yang mendasarinya dan melakukan apa pun yang Anda inginkan.
Zulu

8

jawaban go_as sebagai satu baris

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

6

Saya menemukan pertanyaan ini ketika mencari cara untuk memeriksa apakah modul dimuat dari baris perintah dan ingin berbagi pemikiran saya untuk yang datang setelah saya dan mencari yang sama:

Metode file skrip Linux / UNIX : buat file module_help.py:

#!/usr/bin/env python

help('modules')

Kemudian pastikan itu dapat dieksekusi: chmod u+x module_help.py

Dan menyebutnya dengan pipeuntuk grep:

./module_help.py | grep module_name

Meminta sistem bantuan bawaan . (Fungsi ini dimaksudkan untuk penggunaan interaktif .) Jika tidak ada argumen yang diberikan, sistem bantuan interaktif dimulai pada konsol juru bahasa. Jika argumennya adalah string , maka string tersebut dicari sebagai nama modul , fungsi, kelas, metode, kata kunci, atau topik dokumentasi, dan halaman bantuan dicetak pada konsol. Jika argumennya adalah objek jenis apa pun, halaman bantuan tentang objek tersebut dihasilkan.

Metode interaktif : di konsol memuatpython

>>> help('module_name')

Jika ditemukan berhenti membaca dengan mengetik q
Untuk keluar dari sesi interaktif python, tekan Ctrl+D

Metode file skrip Windows juga kompatibel dengan Linux / UNIX, dan secara keseluruhan lebih baik :

#!/usr/bin/env python

import sys

help(sys.argv[1])

Memanggilnya dari perintah seperti:

python module_help.py site  

Akan menghasilkan:

Bantuan di situs modul:

NAME site - Tambahkan jalur pencarian modul untuk paket pihak ketiga ke sys.path.

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

MODULE DOCS http://docs.python.org/library/site

DESCRIPTION
...
:

dan Anda harus menekan quntuk keluar dari mode interaktif.

Menggunakannya modul tidak dikenal:

python module_help.py lkajshdflkahsodf

Akan menghasilkan:

tidak ditemukan dokumentasi Python untuk 'lkajshdflkahsodf'

dan keluar.


5

Gunakan salah satu fungsi dari pkgutil , misalnya:

from pkgutil import iter_modules

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


4

Anda bisa menulis skrip kecil yang akan mencoba mengimpor semua modul dan memberi tahu Anda mana yang gagal dan mana yang berfungsi:

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))

Keluaran:

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
...

Kata peringatan ini akan mencoba mengimpor semuanya sehingga Anda akan melihat hal-hal seperti PyYAML failed with error code: No module named pyyamlkarena nama impor sebenarnya hanya yaml. Jadi selama Anda tahu impor Anda ini harus melakukan trik untuk Anda.


3

Saya menulis fungsi pembantu ini:

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

Anda juga bisa menggunakannya importlibsecara langsung

import importlib

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

Ini memiliki masalah untuk benar-benar mengimpornya. Efek samping dan semua
yarbelk

2

Tidak ada cara untuk secara andal memeriksa apakah "modul bertitik" dapat diimpor tanpa mengimpor paket induknya. Mengatakan ini, ada banyak solusi untuk masalah "bagaimana memeriksa apakah modul Python ada".

Solusi di bawah mengatasi masalah bahwa modul yang diimpor dapat meningkatkan ImportError meskipun ada. Kami ingin membedakan situasi itu dari yang modulnya tidak ada.

Python 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

Python 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

dalam 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

Ini memenuhi pertanyaan standar saya ketika memprogram python: WWDD (Apa yang akan dilakukan Django?) Dan saya harus melihat di sana
yarbelk
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.