วิธีตรวจสอบว่ามีโมดูลหลามอยู่หรือไม่โดยไม่ต้องนำเข้า


179

ฉันจำเป็นต้องรู้ว่ามีโมดูลหลามอยู่หรือไม่โดยไม่ต้องนำเข้า

การนำเข้าสิ่งที่อาจไม่มีอยู่ (ไม่ใช่สิ่งที่ฉันต้องการ):

try:
    import eggs
except ImportError:
    pass

4
ฉันอยากรู้ว่าอะไรคือข้อเสียในการใช้การนำเข้า
Chuck

15
หากโมดูลของคุณมีผลข้างเคียงการโทรเข้าอาจมีผลกระทบที่ไม่พึงประสงค์ ดังนั้นหากคุณต้องการตรวจสอบว่าไฟล์เวอร์ชันใดที่จะเรียกใช้ก่อนคุณสามารถตรวจสอบด้วยคำตอบด้านล่างและดำเนินการนำเข้าในภายหลัง ฉันไม่ได้แนะนำว่าควรเขียนโมดูลที่มีผลข้างเคียง - แต่เราทุกคนเป็นผู้ใหญ่และสามารถตัดสินใจได้เองว่าเราต้องการโค้ดอย่างไร
yarbelk


1
@ArtOfWarfare ฉันเพียงแค่ปิดที่คำถามที่คุณเชื่อมโยงเป็นซ้ำนี้อย่างใดอย่างหนึ่ง เพราะคำถามนี้ชัดเจนกว่าและวิธีแก้ปัญหาที่นำเสนอในที่นี้ดีกว่าที่อยู่ในรายการทั้งหมด ฉันควรจะชี้ให้ใครก็ตามที่ต้องการคำตอบสำหรับวิธีแก้ปัญหาที่ดีกว่านี้
Bakuriu

7
@Chuck นอกจากนี้โมดูลอาจมีอยู่ แต่ตัวเองอาจมีข้อผิดพลาดในการนำเข้า การตรวจสอบ ImportErrors ดังในโค้ดด้านบนอาจนำไปสู่การระบุว่าไม่มีโมดูลเมื่อจริงแล้วมันมี แต่มีข้อผิดพลาด
Michael Barton

คำตอบ:


199

Python2

เพื่อตรวจสอบว่าการนำเข้าสามารถค้นหาบางสิ่งใน python2 โดยใช้ imp

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

ในการค้นหาการนำเข้าประคุณต้องทำสิ่งต่อไปนี้:

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

นอกจากนี้คุณยังสามารถใช้pkgutil.find_loader(มากขึ้นหรือน้อยลงเช่นเดียวกับส่วน python3

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

Python3

Python3 ≤ 3.3

คุณควรใช้importlibวิธีที่ฉันทำเกี่ยวกับการทำเช่นนี้คือ:

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

ความคาดหวังของฉันคือถ้าคุณสามารถหาตัวโหลดได้มันก็มีอยู่ นอกจากนี้คุณยังฉลาดกว่านี้อีกเล็กน้อยเช่นกรองตัวตักที่คุณจะยอมรับ ตัวอย่างเช่น:

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

ใน Python3.4 importlib.find_loader เอกสารหลามimportlib.util.find_specได้รับการยกเลิกในความโปรดปรานของ importlib.util.find_specวิธีที่แนะนำคือ มีคนอื่นที่ชอบimportlib.machinery.FileFinderซึ่งมีประโยชน์ถ้าคุณหลังจากโหลดไฟล์เฉพาะ การหาวิธีใช้มันเกินขอบเขตของสิ่งนี้

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

วิธีนี้ใช้ได้กับการนำเข้าแบบสัมพัทธ์ แต่คุณต้องระบุแพ็คเกจเริ่มต้นดังนั้นคุณสามารถทำได้:

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

ในขณะที่ฉันแน่ใจว่ามีเหตุผลในการทำเช่นนี้ - ฉันไม่แน่ใจว่ามันจะเป็นอะไร

คำเตือน

เมื่อพยายามหา submodule มันจะนำเข้าโมดูลหลัก (สำหรับวิธีการทั้งหมดข้างต้น)!

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

ยินดีต้อนรับความคิดเห็นเกี่ยวกับการเดินทางรอบนี้

กิตติกรรมประกาศ

  • @rvighne สำหรับ importlib
  • @ lucas-guido สำหรับ python3.3 + depricating find_loader
  • @enpenax สำหรับพฤติกรรม pkgutils.find_loader ใน python2.7

3
eggs.ham.spamนี้จะทำงานเฉพาะสำหรับโมดูลระดับบนสุดไม่ได้สำหรับ
hemflit

3
@hemflit ถ้าคุณต้องการที่จะหาspamในeggs.hamที่คุณจะใช้imp.find_module('spam', ['eggs', 'ham'])
gitaarik

5
+1 แต่impจะเลิกในความโปรดปรานของimportlibในหลาม 3.
rvighne

4
จะทำอย่างไรถ้าโมดูลที่นำเข้ามี "ImportError" จริง นั่นคือสิ่งที่เกิดขึ้นกับฉัน จากนั้นโมดูลจะมีอยู่ แต่จะไม่ถูก "พบ"
enpenax

1
หลังจากปีที่ฉันเจอปัญหาเดียวกันฉันกล่าวข้างต้นและถูกขุดหาทางออกสำหรับ Python 2: pkgutil.find_loader("my.package.module")คืนค่าตัวโหลดหากแพ็กเกจ / โมดูลอยู่และNoneถ้าไม่ โปรดอัปเดตคำตอบของคุณสำหรับ Python 2 เนื่องจากการปิดบัง ImportError ทำให้ฉันเป็นบ้าเมื่อวานนี้ xP
enpenax

13

หลังจากที่การตอบสนองการใช้งาน yarbelk ìmpของผมได้ทำนี้ไม่จำเป็นต้องนำเข้า

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

มีประโยชน์ในsettings.pyตัวอย่างของ Django


4
ฉันลงคะแนนเนื่องจากมาสก์นี้นำเข้าข้อผิดพลาดในโมดูลซึ่งทำให้ยากต่อการตรวจพบข้อผิดพลาด
enpenax

1
Downvote เป็นความคิดที่ไม่ดี pratice ที่ดีคือ "บันทึกข้อผิดพลาดที่ตรวจพบเสมอ" นี่คือตัวอย่างหลังจากที่คุณเขียนว่าคุณต้องการมันอย่างไร
ซูลู

2
คุณจะบันทึกข้อผิดพลาดได้อย่างไรหากโมดูลที่นำเข้าล้มเหลวในบรรทัดที่ 1 พร้อมด้วย ImportError และลอง catch ของคุณทำให้มันล้มเหลวในทันที
enpenax

ฉันเพิ่งเจอปัญหาการหลอกลวง - นำเข้า - ข้อผิดพลาดในชีวิตจริงและมันก็ไม่ดี (ทำให้เกิดการทดสอบที่น่าจะล้มเหลว!
เดวิดกิฟ

ฉันพบสิ่งที่มีคนใช้ข้อผิดพลาดนั้นเพื่อกระตุ้นให้ลิงอยู่ในโมดูลที่ต่างออกไป ... นั่นเป็นความบ้าคลั่งที่จะตามหา
yarbelk

13

Python 3> = 3.6: ModuleNotFoundError

ModuleNotFoundErrorได้รับการแนะนำในหลาม 3.6และสามารถนำมาใช้เพื่อการนี้

try:
    import eggs
except ModuleNotFoundError:
    # Error handling
    pass

เกิดข้อผิดพลาดขึ้นเมื่อไม่พบโมดูลหรือหนึ่งในพาเรนต์ ดังนั้น

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

จะพิมพ์ข้อความที่ดูเหมือนNo module named 'eggs'ว่าeggsไม่พบโมดูล; แต่จะพิมพ์บางอย่างเช่นNo module named 'eggs.sub'หากsubไม่พบโมดูลเท่านั้น แต่สามารถค้นหาeggsแพ็คเกจได้

ดูเอกสารประกอบของระบบนำเข้าสำหรับข้อมูลเพิ่มเติมเกี่ยวกับModuleNotFoundError


1
doe นี้ไม่ตอบคำถามเพราะมันนำเข้าแพคเกจถ้ามันมีอยู่
divenex

11

Python 2 โดยไม่ต้องพึ่งพา ImportError

จนกว่าจะมีการอัปเดตคำตอบปัจจุบันต่อไปนี้เป็นวิธีสำหรับPython 2

import pkgutil
import importlib

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

ทำไมต้องมีคำตอบอื่นอีก?

คำตอบจำนวนมากใช้ประโยชน์จากการจับImportErrorและ ปัญหาที่เกิดขึ้นคือเราไม่สามารถรู้ได้ว่าสิ่งใดที่ทำให้เกิดปัญหาImportErrorปัญหา

ถ้าคุณนำเข้าของคุณexistantโมดูลและมีเกิดขึ้นเป็นImportErrorในโมดูลของคุณ (เช่นพิมพ์ผิดในบรรทัด 1) ผลที่ได้จะเป็นไปได้ว่าโมดูลของคุณไม่อยู่ มันจะทำให้คุณมีจำนวน backtracking ค่อนข้างมากที่จะคิดว่าโมดูลของคุณมีอยู่และImportErrorถูกจับและทำให้สิ่งต่าง ๆ ล้มเหลวอย่างเงียบ ๆ


มันอาจจะไม่ชัดเจน แต่ทั้งหมดยกเว้นบล็อคโค้ดแรกไม่ได้พึ่งพาImportError- โปรดแก้ไขหากไม่ชัดเจนสำหรับคุณ
yarbelk

ฉันเห็นคุณใช้การจับ ImportError ในตัวอย่าง Python 2 สองตัวแรก ทำไมพวกเขาอยู่ที่นั่นแล้ว?
enpenax

3
สิ่งนี้จะทำให้ ImportError ถ้า mod == 'not_existing_package.mymodule' ดูโซลูชัน
Marcin Raczyński

1
แน่นอนว่ามันมีข้อผิดพลาดในการนำเข้า มันควรจะโยนข้อผิดพลาดในการนำเข้าถ้าไม่มีโมดูล ด้วยวิธีนี้คุณสามารถจับมันถ้าคุณต้องการ ปัญหาเกี่ยวกับการแก้ไขปัญหาอื่น ๆ คือพวกเขาปกปิดข้อผิดพลาดอื่น ๆ
enpenax

ลอง / ยกเว้นไม่ได้หมายความว่าคุณจะต้องไม่บันทึกหรือตรวจสอบสิ่งต่าง ๆ คุณสามารถตรวจสอบย้อนกลับพื้นฐานและทำทุกอย่างที่คุณต้องการ
Zulu

8

คำตอบของ go_as ในฐานะหนึ่งซับ

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

6

ฉันเจอคำถามนี้ในขณะที่ค้นหาวิธีการตรวจสอบว่ามีการโหลดโมดูลจากบรรทัดคำสั่งหรือไม่ และต้องการแบ่งปันความคิดของฉันสำหรับสิ่งที่ตามมาและมองหาสิ่งเดียวกัน:

วิธีไฟล์สคริปต์ Linux / UNIX : สร้างไฟล์module_help.py:

#!/usr/bin/env python

help('modules')

จากนั้นตรวจสอบให้แน่ใจว่าปฏิบัติการได้: chmod u+x module_help.py

และเรียกมันด้วย a pipeถึงgrep:

./module_help.py | grep module_name

เรียกใช้ระบบช่วยเหลือในตัว (ฟังก์ชั่นนี้มีไว้สำหรับการใช้งานแบบอินเทอร์แอคทีฟ ) หากไม่มีการระบุอาร์กิวเมนต์ระบบวิธีใช้แบบโต้ตอบจะเริ่มต้นที่คอนโซลของล่าม หากอาร์กิวเมนต์เป็นสตริงสตริงนั้นจะถูกค้นหาเป็นชื่อของโมดูลฟังก์ชันคลาสเมธอดคำสำคัญหรือเอกสารคู่มือและหน้าวิธีใช้ถูกพิมพ์บนคอนโซล หากอาร์กิวเมนต์เป็นวัตถุชนิดอื่นหน้าความช่วยเหลือบนวัตถุจะถูกสร้างขึ้น

วิธีการโต้ตอบ : ในการโหลดคอนโซลpython

>>> help('module_name')

หากพบว่าเลิกอ่านโดยพิมพ์q
เพื่อออกจากเซสชันการโต้ตอบของหลามกดCtrl+D

วิธีการไฟล์สคริปต์ Windowsยังรองรับ Linux / UNIX และโดยรวมดีขึ้น :

#!/usr/bin/env python

import sys

help(sys.argv[1])

เรียกมันจากคำสั่งเช่น:

python module_help.py site  

จะส่งออก:

ช่วยในเว็บไซต์โมดูล:

NAME site - ผนวกพา ธ การค้นหาโมดูลสำหรับแพ็คเกจบุคคลที่สามไปยัง sys.path

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

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

DESCRIPTION
...
:

และคุณต้องกดqเพื่อออกจากโหมดโต้ตอบ

ใช้มันโมดูลที่ไม่รู้จัก:

python module_help.py lkajshdflkahsodf

จะส่งออก:

ไม่พบเอกสาร Python สำหรับ 'lkajshdflkahsodf'

และออกจาก




4

คุณสามารถเขียนสคริปต์เล็กน้อยที่จะพยายามนำเข้าโมดูลทั้งหมดและบอกคุณว่าโมดูลใดทำงานล้มเหลวและโมดูลใดทำงานได้:

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

เอาท์พุท:

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

คำเตือนนี้จะพยายามนำเข้าทุกอย่างดังนั้นคุณจะเห็นสิ่งต่าง ๆ เช่นPyYAML failed with error code: No module named pyyamlเนื่องจากชื่อการนำเข้าที่แท้จริงเป็นเพียง yaml ดังนั้นตราบใดที่คุณรู้การนำเข้าของคุณสิ่งนี้ควรทำเพื่อคุณ


3

ฉันเขียนฟังก์ชันผู้ช่วยนี้:

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

คุณยังสามารถใช้importlibโดยตรง

import importlib

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

ปัญหานี้มีปัญหาการนำเข้าจริง ผลข้างเคียงและทั้งหมด
yarbelk

2

ไม่มีวิธีที่จะตรวจสอบได้อย่างน่าเชื่อถือว่า "โมดูลจุด" สามารถนำเข้าได้โดยไม่ต้องนำเข้าแพ็คเกจหลัก การพูดแบบนี้มีวิธีแก้ไขปัญหามากมาย "วิธีตรวจสอบว่ามีโมดูล Python อยู่หรือไม่"

ด้านล่างวิธีการแก้ไขปัญหาที่โมดูลที่นำเข้าสามารถยก ImportError แม้มันจะมีอยู่ เราต้องการแยกแยะสถานการณ์นั้นจากสิ่งที่ไม่มีอยู่ในโมดูล

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

ใน 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

สิ่งนี้ตอบสนองคำถามมาตรฐานของฉันเมื่อตั้งโปรแกรมหลาม: WWDD (Django ทำอะไรได้บ้าง) และฉันควรดูที่นั่น
yarbelk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.