Jak sprawdzić, czy moduł Pythona istnieje bez importowania go

179

Muszę wiedzieć, czy istnieje moduł Pythona, bez importowania go.

Importowanie czegoś, co może nie istnieć (nie tego, czego chcę):

try:
    import eggs
except ImportError:
    pass
yarbelk
źródło
4
Jestem ciekaw, jakie są wady używania importu?
Chuck,
15
Jeśli twój moduł ma efekty uboczne, wywołanie importu może mieć niepożądane konsekwencje. Tak więc, jeśli chcesz sprawdzić, która wersja pliku ma zostać uruchomiona jako pierwsza, możesz sprawdzić poniższą odpowiedź i zaimportować później. Nie sugeruję, że pisanie modułów z efektami ubocznymi jest dobrym pomysłem - ale wszyscy jesteśmy dorośli i możemy podejmować własne decyzje dotyczące tego, jak niebezpiecznie chcemy kodować.
yarbelk
1
@ArtOfWarfare Właśnie zamknąłem to pytanie, które podałeś jako duplikat tego . Ponieważ to pytanie jest jaśniejsze, a także zaproponowane tutaj rozwiązanie jest lepsze niż wszystkie inne wymienione tam. Wolę wskazać komukolwiek, kto chce odpowiedzi na to lepsze rozwiązanie, niż odciągać ludzi od niego.
Bakuriu,
7
@Chuck Dodatkowo moduł może istnieć, ale sam może zawierać błędy importu. Wyłapanie błędów ImportErrors jak w powyższym kodzie mogłoby prowadzić do wskazania modułu nie istnieje, jeśli faktycznie ma, ale zawiera błędy.
Michael Barton

Odpowiedzi:

199

Python2

Aby sprawdzić, czy import może znaleźć coś w python2, użyj imp

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

Aby znaleźć import z kropkami, musisz zrobić więcej:

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

Możesz także użyć pkgutil.find_loader(mniej więcej tego samego, co część python3

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

Python3

Python3 ≤ 3,3

Powinieneś użyć importlib, jak sobie z tym radziłem:

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

Oczekuję, że jeśli znajdziesz do tego ładowarkę, to ona istnieje. Możesz też podejść do tego nieco sprytniej, na przykład odfiltrowywać, które programy ładujące zaakceptujesz. Na przykład:

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

W importlib.find_loader Pythonie 3.4 dokumentacja pythona została wycofana na korzyść importlib.util.find_spec. Zalecaną metodą jest importlib.util.find_spec. Istnieją inne importlib.machinery.FileFinder, które są przydatne, jeśli chcesz załadować określony plik. Ustalenie, jak ich używać, wykracza poza zakres tego.

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

Działa to również w przypadku importu względnego, ale musisz dostarczyć pakiet startowy, więc możesz również:

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

Chociaż jestem pewien, że istnieje powód, aby to zrobić - nie jestem pewien, co by to było.

OSTRZEŻENIE

Próbując znaleźć podmoduł, zaimportuje on moduł nadrzędny (dla wszystkich powyższych metod)!

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

mile widziane komentarze dotyczące obejścia tego problemu

Podziękowanie

  • @rvighne dla importlib
  • @ lucas-guido dla Pythona3.3 + depricating find_loader
  • @enpenax dla zachowania pkgutils.find_loader w pythonie2.7
yarbelk
źródło
3
Działa to tylko dla modułów najwyższego poziomu, a nie dla eggs.ham.spam.
hemflit
3
@hemflit jeśli chcesz dowiedzieć spamw eggs.hamjakich można użyćimp.find_module('spam', ['eggs', 'ham'])
gitaarik
5
+1, ale w Pythonie 3 impjest wycofywane na korzyść importlib.
rvighne
4
Co się stanie, jeśli zaimportowany moduł zawiera rzeczywisty „ImportError”. To właśnie się ze mną działo. Wtedy moduł istnieje, ale nie zostanie „znaleziony”.
enpenax
1
Po roku natknąłem się na ten sam problem, o którym wspomniałem powyżej, i szukałem rozwiązania dla Pythona 2: pkgutil.find_loader("my.package.module")zwraca program ładujący, jeśli pakiet / moduł istnieje, a Nonejeśli nie. Zaktualizuj swoją odpowiedź dla Pythona 2, ponieważ maskowanie
błędu
13

Po użyciu odpowiedzi yarbelk zrobiłem to, aby nie musieć importować ìmp.

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

Przydatne settings.pyna przykład w Django .

Zulus
źródło
4
Głosowałem w dół, ponieważ maskuje to błędy importu w module, co sprawia, że ​​naprawdę trudno jest wykryć błąd.
enpenax
1
Głosowanie przeciwne to zły pomysł, dobrą praktyką jest „zawsze rejestruj wyłapane błędy”. To jest przykład po tym, jak napiszesz, jak chcesz.
Zulu
2
Jak zarejestrowałbyś błąd, jeśli importowany moduł zawiedzie w linii 1 z błędem ImportError, a twój try catch sprawi, że zawiedzie po cichu?
enpenax
Właśnie napotkałem problem błędów maskowania-importu w prawdziwym życiu i był zły (powodując testy, które nie powinny przejść pomyślnie!).
David Given
Wpadłem na to, że ktoś używał tego błędu do wywołania monkeypatch na innym module ... to było szaleństwo do znalezienia
yarbelk
13

Python 3> = 3.6: ModuleNotFoundError

ModuleNotFoundErrorZostał wprowadzony w Pythonie 3.6 i mogą być wykorzystane do tego celu

try:
    import eggs
except ModuleNotFoundError:
    # Error handling
    pass

Błąd jest zgłaszany, gdy nie można znaleźć modułu lub jednego z jego elementów nadrzędnych . Więc

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

wypisze komunikat, który wygląda tak, No module named 'eggs'jakby eggsnie można było znaleźć modułu; ale wypisze coś takiego No module named 'eggs.sub', jak gdyby subnie można było znaleźć modułu, ale można znaleźć eggspakiet.

Zobacz dokumentację systemu importu, aby uzyskać więcej informacji na tematModuleNotFoundError

J.Baoby
źródło
1
To nie daje odpowiedzi na pytanie, ponieważ importuje pakiet, jeśli istnieje
divenex
11

Python 2, bez polegania na ImportError

Dopóki aktualna odpowiedź nie zostanie zaktualizowana, oto sposób na Python 2

import pkgutil
import importlib

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

Dlaczego inna odpowiedź?

Wiele odpowiedzi wykorzystuje przechwycenie pliku ImportError. Problem w tym, że nie wiemy, co rzuca ImportError.

Jeśli zaimportujesz istniejący moduł i zdarzy się, że ImportErrorw Twoim module znajduje się jakiś (np. Literówka w linii 1), to spowoduje, że moduł nie istnieje. Zajmie ci to sporo czasu, aby dowiedzieć się, że twój moduł istnieje, ImportErrorzostał złapany i powoduje cichą awarię.

enpenax
źródło
Mogło to być niejasne, ale wszystkie bloki kodu oprócz pierwszych nie polegają na ImportError- prosimy o dokonanie edycji, jeśli nie jest to dla Ciebie jasne.
yarbelk
Widzę, że używasz haczyka ImportError w pierwszych dwóch przykładach Pythona 2. Dlaczego więc tam są?
enpenax
3
Powoduje to wywołanie ImportError if mod == 'not_existing_package.mymodule'. Zobacz moje pełne rozwiązanie poniżej
Marcin Raczyński
1
Oczywiście zgłasza błąd importu. Powinien zgłosić błąd importu, jeśli moduł nie istnieje. W ten sposób możesz go złapać, jeśli potrzebujesz. Problem z innymi rozwiązaniami polega na tym, że maskują one inne błędy.
enpenax
Spróbuj / z wyjątkiem nie oznacza, że ​​nie możesz rejestrować ani zapewniać rzeczy. Możesz w pełni przechwycić wszelkie podstawowe elementy śledzenia i zrobić, co chcesz.
Zulu
8

odpowiedź go_as jako jedna linijka

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

źródło
6

Natknąłem się na to pytanie, szukając sposobu na sprawdzenie, czy moduł jest ładowany z linii poleceń i chciałbym podzielić się przemyśleniami na temat tych, którzy idą za mną i szukają tego samego:

Metoda pliku skryptu Linux / UNIX : utwórz plik module_help.py:

#!/usr/bin/env python

help('modules')

Następnie upewnij się, że jest wykonywalny: chmod u+x module_help.py

I zadzwoń pipedo grep:

./module_help.py | grep module_name

Wywołaj wbudowany system pomocy . (Ta funkcja jest przeznaczona do użytku interaktywnego .) Jeśli nie podano żadnego argumentu, system pomocy interaktywnej uruchamia się na konsoli interpretera. Jeśli argument jest ciągiem znaków , wówczas ciąg jest wyszukiwany jako nazwa modułu , funkcji, klasy, metody, słowa kluczowego lub tematu dokumentacji, a na konsoli jest drukowana strona pomocy. Jeśli argumentem jest obiekt innego rodzaju, generowana jest strona pomocy dotycząca tego obiektu.

Metoda interaktywna : w konsoli loadpython

>>> help('module_name')

Jeśli zostanie znaleziony, zakończ czytanie, wpisując q
Aby wyjść z sesji interaktywnej Pythona, naciśnij Ctrl+D

Metoda pliku skryptu Windows również kompatybilna z Linux / UNIX i ogólnie lepsza :

#!/usr/bin/env python

import sys

help(sys.argv[1])

Wywołanie go z polecenia, takiego jak:

python module_help.py site  

Wynikałoby:

Pomoc na stronie modułu:

NAME witryna - Dołącz ścieżki wyszukiwania modułów dla pakietów innych firm do sys.path.

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

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

DESCRIPTION
...
:

i musiałbyś nacisnąć, qaby wyjść z trybu interaktywnego.

Korzystanie z nieznanego modułu:

python module_help.py lkajshdflkahsodf

Wynikałoby:

nie znaleziono dokumentacji Pythona dla 'lkajshdflkahsodf'

i wyjdź.

go_as
źródło
5

Użyj jednej z funkcji z pkgutil , na przykład:

from pkgutil import iter_modules

def module_exists(module_name):
    return module_name in (name for loader, name, ispkg in iter_modules())
ArtOfWarfare
źródło
4

Możesz po prostu napisać mały skrypt, który spróbuje zaimportować wszystkie moduły i powie Ci, które z nich nie działają, a które działają:

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

Wynik:

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

Słowo ostrzeżenia, że ​​spróbuje zaimportować wszystko, więc zobaczysz rzeczy, takie jak PyYAML failed with error code: No module named pyyamlfaktyczna nazwa importu to po prostu yaml. Tak długo, jak wiesz, że importujesz, powinno to załatwić sprawę.

Użytkownik9123
źródło
3

Napisałem tę funkcję pomocniczą:

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
Fardin
źródło
2

Możesz również użyć importlibbezpośrednio

import importlib

try:
    importlib.import_module(module_name)
except ImportError:
    # Handle error
jackotonye
źródło
To ma problem z faktycznym zaimportowaniem go. Efekty uboczne i wszystko
yarbelk
2

Nie ma sposobu, aby wiarygodnie sprawdzić, czy „moduł z kropkami” jest możliwy do zaimportowania bez importowania jego pakietu nadrzędnego. Mówiąc to, istnieje wiele rozwiązań problemu „jak sprawdzić, czy istnieje moduł Pythona”.

Poniższe rozwiązanie rozwiązuje problem polegający na tym, że importowany moduł może wywołać ImportError, nawet jeśli istnieje. Chcemy odróżnić tę sytuację od takiej, w której moduł nie istnieje.

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
Marcin Raczyński
źródło
1

w 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
dibrovsd
źródło
To spełnia moje standardowe pytanie podczas programowania w Pythonie: WWDD (Co zrobiłby Django?) I powinienem był tam zajrzeć
yarbelk