แสดงรายการโมดูลทั้งหมดที่เป็นส่วนหนึ่งของแพ็คเกจ python?


107

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


6
@ S.Lott: มีโซลูชันทั่วไปที่พร้อมใช้งานแพคเกจ python ไม่ได้อยู่ในไดเร็กทอรีในระบบไฟล์เสมอไป แต่ยังสามารถอยู่ภายในซิปได้
u0b34a0f6ae

4
ทำไมต้องคิดค้นล้อใหม่? หาก python ได้รับ hypermodules ใน Python 4, pkgutil และอัปเดตด้วยรหัสของฉันจะยังใช้งานได้ ฉันชอบใช้นามธรรมที่มีอยู่ ใช้วิธีการที่ชัดเจนซึ่งได้รับการทดสอบและทราบว่าได้ผล ตอนนี้คุณต้องค้นหาและแก้ไขปัญหาทุกมุมด้วยตัวคุณเอง
u0b34a0f6ae

1
@ S.Lott: ดังนั้นทุกครั้งที่แอปพลิเคชันเริ่มทำงานมันจะเปิดเครื่องรูดไข่ของมันเองหากติดตั้งไว้ข้างในเพื่อตรวจสอบสิ่งนี้? กรุณาส่งแพทช์กับโครงการของฉันที่จะบูรณาการล้อในฟังก์ชั่นนี้: git.gnome.org/cgit/kupfer/tree/kupfer/plugins.py#n17 โปรดพิจารณาทั้งไข่และไดเร็กทอรีปกติไม่เกิน 20 บรรทัด
u0b34a0f6ae

1
@ S.Lott: ทำไมคุณไม่เข้าใจว่ามันเกี่ยวข้องเป็นสิ่งที่คุณไม่เข้าใจ การค้นพบนี้โดยทางโปรแกรมเป็นเรื่องเกี่ยวกับที่แอปพลิเคชันให้ความสนใจในเนื้อหาของแพ็กเกจไม่ใช่ผู้ใช้
u0b34a0f6ae

3
แน่นอนฉันหมายถึงการเขียนโปรแกรม! ไม่เช่นนั้นฉันจะไม่พูดถึง "การเปิดตัวโซลูชันของฉันเองด้วย os.listdir ()"
static_rtti

คำตอบ:


145

ใช่คุณต้องการบางอย่างที่อิงpkgutilหรือคล้ายกัน - วิธีนี้จะทำให้คุณสามารถปฏิบัติต่อแพ็คเกจทั้งหมดได้ไม่ว่าจะอยู่ในไข่หรือซิปหรือมากกว่านั้น (โดยที่ os.listdir จะไม่ช่วย)

import pkgutil

# this is the package we are inspecting -- for example 'email' from stdlib
import email

package = email
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
    print "Found submodule %s (is a package: %s)" % (modname, ispkg)

วิธีการนำเข้าด้วย? คุณสามารถใช้งานได้__import__ตามปกติ:

import pkgutil

# this is the package we are inspecting -- for example 'email' from stdlib
import email

package = email
prefix = package.__name__ + "."
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__, prefix):
    print "Found submodule %s (is a package: %s)" % (modname, ispkg)
    module = __import__(modname, fromlist="dummy")
    print "Imported", module

9
สิ่งนี้importerส่งคืนโดยpkgutil.iter_modulesอะไร? ฉันสามารถใช้เพื่อนำเข้าโมดูลแทนการใช้ "แฮ็ก" ได้__import__(modname, fromlist="dummy")หรือไม่
MestreLion

29
ฉันสามารถใช้ตัวนำเข้าได้เช่นนี้m = importer.find_module(modname).load_module(modname)แล้วก็mเป็นโมดูลตัวอย่างเช่น:m.myfunc()
chrisleague

@chrisleague ฉันใช้วิธี ur กับ python 2.7 แต่ตอนนี้ฉันต้องไปต่อด้วย python 3.4 ดังนั้นคุณจะรู้ว่าใน python 3 pkutil.iter_modules ให้ผลตอบแทน (module_finder, name, ispkg) แทน (module_loader, name, ispkg) ฉันจะทำอย่างไรให้มันใช้งานได้เหมือนก่อนหน้านี้
crax

ตัวอย่างแรกของคุณทำให้เกิดข้อผิดพลาดต่อไปนี้: วัตถุ "AttributeError:" module "ไม่มีแอตทริบิวต์" _path_ ""สิ่งนี้เกี่ยวข้องกับเวอร์ชัน Python หรือไม่ (ฉันใช้ Python 2.7)
Apostolos

@Apostolos คุณใช้ขีดล่างเพียงอันเดียวที่ด้านข้างของเส้นทาง (เช่น_path_) ควรมีสองด้านรวมเป็นสี่ (กล่าวคือ__path__)
therealmitchconnors

46

เครื่องมือที่เหมาะสมสำหรับงานนี้คือ pkgutil.walk_packages

ในการแสดงรายการโมดูลทั้งหมดในระบบของคุณ:

import pkgutil
for importer, modname, ispkg in pkgutil.walk_packages(path=None, onerror=lambda x: None):
    print(modname)

โปรดทราบว่า walk_packages นำเข้าแพ็กเกจย่อยทั้งหมด แต่ไม่ใช่โมดูลย่อย

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

import pkgutil
import scipy
package=scipy
for importer, modname, ispkg in pkgutil.walk_packages(path=package.__path__,
                                                      prefix=package.__name__+'.',
                                                      onerror=lambda x: None):
    print(modname)

iter_modules แสดงเฉพาะโมดูลที่มีความลึกระดับเดียว walk_packages รับโมดูลย่อยทั้งหมด ในกรณีของ scipy ตัวอย่างเช่น walk_packages จะส่งกลับ

scipy.stats.stats

ในขณะที่ iter_modules ส่งคืนเท่านั้น

scipy.stats

เอกสารประกอบเกี่ยวกับ pkgutil ( http://docs.python.org/library/pkgutil.html ) ไม่แสดงรายการฟังก์ชันที่น่าสนใจทั้งหมดที่กำหนดไว้ใน /usr/lib/python2.6/pkgutil.py

บางทีหมายความว่าฟังก์ชันนี้ไม่ได้เป็นส่วนหนึ่งของอินเทอร์เฟซ "สาธารณะ" และอาจมีการเปลี่ยนแปลงได้

อย่างไรก็ตามอย่างน้อยที่สุดใน Python 2.6 (และอาจเป็นเวอร์ชันก่อนหน้านี้?) pkgutil มาพร้อมกับวิธีการ walk_packages ซึ่งจะเดินผ่านโมดูลทั้งหมดที่มีอยู่ซ้ำ ๆ


5
walk_packagesอยู่ในเอกสารประกอบ: docs.python.org/library/pkgutil.html#pkgutil.walk_packages
Mechanical snail

1
ตัวอย่างที่สองของคุณทำให้เกิดข้อผิดพลาดต่อไปนี้: วัตถุ "AttributeError:" module "ไม่มีแอตทริบิวต์" _path_ "" - ฉันไม่ได้ทดสอบด้วย "scipy" แต่มีแพ็กเกจอื่น ๆ สิ่งนี้เกี่ยวข้องกับเวอร์ชัน Python หรือไม่? (ฉันใช้ Python 2.7)
Apostolos

1
@Apostolos: ควรมีเครื่องหมายขีดล่าง ( _) สองอันก่อนและหลังpathนั่นคือใช้package.__path__แทนpackage._path_. อาจจะง่ายกว่าที่จะลองตัดและวางโค้ดแทนที่จะพิมพ์ซ้ำ
unutbu

มีสองคนเมื่อฉันเขียนความคิดเห็น! :) แต่พวกเขาถูกปลดออกจากระบบ ความผิดฉันเอง; ฉันควรจะใส่สามอันเดอร์คอร์ แต่ถ้าอย่างนั้นนี่จะใช้ได้ถ้าฉันต้องการใช้ตัวเอียงซึ่งฉันไม่ทำ! ... มันคือสถานการณ์ขาดทุน - ขาดทุน :) อย่างไรก็ตามเมื่อฉันเรียกใช้รหัสฉันใช้สองรหัสแน่นอน (ฉันคัดลอกรหัส)
Apostolos

@Apostolos: ตรวจสอบให้แน่ใจว่าตัวแปรpackageชี้ไปที่แพ็คเกจไม่ใช่โมดูล โมดูลคือไฟล์ในขณะที่แพ็กเกจเป็นไดเร็กทอรี แพ็คเกจทั้งหมดมี__path__แอตทริบิวต์ (... เว้นแต่จะมีใครลบแอตทริบิวต์ด้วยเหตุผลบางประการ)
unutbu

2

สิ่งนี้ใช้ได้กับฉัน:

import types

for key, obj in nltk.__dict__.iteritems():
    if type(obj) is types.ModuleType: 
        print key

1
สิ่งนี้ล้มเหลวในสองวิธี 1. แพ็กเกจไม่ได้นำเข้าโมดูลย่อยอย่างชัดเจนไปยังเนมสเปซระดับบนสุดเสมอไป 2. แพ็กเกจอาจนำเข้าโมดูลของบุคคลที่สามอื่น ๆ ไปยังเนมสเปซระดับบนสุดของตน
wim

0

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

package=yourPackageName
import importlib
import pkgutil
for importer, modname, ispkg in pkgutil.walk_packages(path=package.__path__, prefix=package.__name__+'.', onerror=lambda x: None):
    try:
        modulesource = importlib.import_module(modname)
        reload(modulesource)
        print("reloaded: {}".format(modname))
    except Exception as e:
        print('Could not load {} {}'.format(modname, e))

-4

นี่เป็นวิธีหนึ่งที่ปิดด้านบนของหัวของฉัน:

>>> import os
>>> filter(lambda i: type(i) == type(os), [getattr(os, j) for j in dir(os)])
[<module 'UserDict' from '/usr/lib/python2.5/UserDict.pyc'>, <module 'copy_reg' from '/usr/lib/python2.5/copy_reg.pyc'>, <module 'errno' (built-in)>, <module 'posixpath' from '/usr/lib/python2.5/posixpath.pyc'>, <module 'sys' (built-in)>]

สามารถทำความสะอาดและปรับปรุงได้อย่างแน่นอน

แก้ไข:นี่เป็นเวอร์ชันที่ดีกว่าเล็กน้อย:

>>> [m[1] for m in filter(lambda a: type(a[1]) == type(os), os.__dict__.items())]
[<module 'copy_reg' from '/usr/lib/python2.5/copy_reg.pyc'>, <module 'UserDict' from '/usr/lib/python2.5/UserDict.pyc'>, <module 'posixpath' from '/usr/lib/python2.5/posixpath.pyc'>, <module 'errno' (built-in)>, <module 'sys' (built-in)>]
>>> [m[0] for m in filter(lambda a: type(a[1]) == type(os), os.__dict__.items())]
['_copy_reg', 'UserDict', 'path', 'errno', 'sys']

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


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