ใครช่วยให้ฉันมีวิธีที่ดีในการนำเข้าไดเรกทอรีทั้งหมดของโมดูล?
ฉันมีโครงสร้างเช่นนี้:
/Foo
bar.py
spam.py
eggs.py
ฉันพยายามแปลงเป็นแพ็คเกจด้วยการเพิ่ม__init__.py
และทำfrom Foo import *
แต่มันก็ไม่ทำงานอย่างที่ฉันหวังไว้
ใครช่วยให้ฉันมีวิธีที่ดีในการนำเข้าไดเรกทอรีทั้งหมดของโมดูล?
ฉันมีโครงสร้างเช่นนี้:
/Foo
bar.py
spam.py
eggs.py
ฉันพยายามแปลงเป็นแพ็คเกจด้วยการเพิ่ม__init__.py
และทำfrom Foo import *
แต่มันก็ไม่ทำงานอย่างที่ฉันหวังไว้
คำตอบ:
แสดงรายการ.py
ไฟล์หลาม ( ) ทั้งหมดในโฟลเดอร์ปัจจุบันและวางไว้เป็น__all__
ตัวแปร__init__.py
from os.path import dirname, basename, isfile, join
import glob
modules = glob.glob(join(dirname(__file__), "*.py"))
__all__ = [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]
if not os.path.basename(f).startswith('_')
หรืออย่างน้อยที่สุดif not f.endswith('__init__.py')
ในตอนท้ายของรายการความเข้าใจ
os.path.isfile(f)
True
ที่จะกรองออก symlink และไดเรกทอรีที่แตกสลายเช่นsomedir.py/
(กรณีมุมฉันยอมรับ แต่ก็ยัง ... )
from . import *
หลังจากการตั้งค่า__all__
ถ้าคุณต้องการ submodules ที่จะมีการใช้.
(เช่นเป็นmodule.submodule1
, module.submodule2
ฯลฯ )
เพิ่ม__all__
ตัวแปรเป็น__init__.py
:
__all__ = ["bar", "spam", "eggs"]
ดูเพิ่มเติมที่http://docs.python.org/tutorial/modules.html
moduleName.varName
อ้างอิง stackoverflow.com/a/710603/248616
อัปเดตในปี 2560: คุณอาจต้องการใช้ importlib
แทน
__init__.py
ทำให้ไดเรกทอรีฟูแพคเกจโดยการเพิ่ม ในการ__init__.py
เพิ่มที่:
import bar
import eggs
import spam
เนื่องจากคุณต้องการให้เป็นแบบไดนามิก (ซึ่งอาจจะใช่หรือไม่ใช่ความคิดที่ดี) ให้เขียนรายการ py-files ทั้งหมดที่มี list dir และนำเข้าสิ่งเหล่านี้:
import os
for module in os.listdir(os.path.dirname(__file__)):
if module == '__init__.py' or module[-3:] != '.py':
continue
__import__(module[:-3], locals(), globals())
del module
จากนั้นทำตามรหัสของคุณ:
import Foo
ตอนนี้คุณสามารถเข้าถึงโมดูลด้วย
Foo.bar
Foo.eggs
Foo.spam
ฯลฯfrom Foo import *
ไม่ใช่ความคิดที่ดีด้วยเหตุผลหลายประการรวมถึง clashes ชื่อและทำให้มันยากที่จะวิเคราะห์รหัส
__import__
แฮ็คฉันคิดว่ามันจะเป็นการดีกว่าถ้าจะเพิ่มชื่อ__all__
และวางไว้from . import *
ที่ด้านล่างสุดของสคริปต์
__import__
ไม่ได้มีไว้สำหรับการใช้งานทั่วไปใช้โดยinterpreter
ใช้importlib.import_module()
แทน
from . import eggs
ฯลฯ__init__.py
ก่อนที่ Python จะนำเข้าได้ มีเพียงimport eggs
ฉันเท่านั้นที่ได้รับModuleNotFoundError: No module named 'eggs'
เมื่อพยายามเข้าไปimport Foo
ในmain.py
ไดเรกทอรีด้านบน
เมื่อเพิ่มคำตอบของ Mihail ฉันเชื่อว่าวิธีที่ไม่แฮ็ก
__init__.py
ไฟล์ว่างใต้Foo/
import pkgutil
import sys
def load_all_modules_from_dir(dirname):
for importer, package_name, _ in pkgutil.iter_modules([dirname]):
full_package_name = '%s.%s' % (dirname, package_name)
if full_package_name not in sys.modules:
module = importer.find_module(package_name
).load_module(full_package_name)
print module
load_all_modules_from_dir('Foo')
คุณจะได้รับ:
<module 'Foo.bar' from '/home/.../Foo/bar.pyc'>
<module 'Foo.spam' from '/home/.../Foo/spam.pyc'>
RuntimeWarning
ข้อความนอกจากนี้ยังสามารถหลีกเลี่ยงได้โดยไม่ได้ใช้ full_package_name importer.find_module(package_name).load_module(package_name)
ที่ทั้งหมด:
RuntimeWarning
ข้อผิดพลาดยังสามารถหลีกเลี่ยงได้ (ในทางที่น่าเกลียดเนื้อหา) โดยการนำเข้าแม่ (AKA dirname) วิธีหนึ่งในการทำเช่นนั้นคือ - if dirname not in sys.modules: pkgutil.find_loader(dirname).load_module(dirname)
. แน่นอนว่าใช้งานได้หากdirname
เป็นเส้นทางสัมพัทธ์ส่วนประกอบเดียว ไม่มีการทับ โดยส่วนตัวแล้วฉันชอบวิธีการของ @ Artfunkel ในการใช้ base package_name แทน
สำหรับมือใหม่ที่ไม่สามารถไปทำงานที่ต้องการมือของพวกเขาได้
สร้างโฟลเดอร์ / home / el / foo และสร้างไฟล์main.py
ภายใต้ / home / el / foo ใส่รหัสนี้ใน:
from hellokitty import *
spam.spamfunc()
ham.hamfunc()
ทำไดเรกทอรี /home/el/foo/hellokitty
สร้างไฟล์__init__.py
ภายใต้/home/el/foo/hellokitty
และใส่รหัสนี้ใน:
__all__ = ["spam", "ham"]
สร้างไฟล์หลามสองไฟล์: spam.py
และham.py
ใต้/home/el/foo/hellokitty
กำหนดฟังก์ชั่นภายใน spam.py:
def spamfunc():
print("Spammity spam")
กำหนดฟังก์ชั่นภายใน ham.py:
def hamfunc():
print("Upgrade from baloney")
เรียกใช้:
el@apollo:/home/el/foo$ python main.py
spammity spam
Upgrade from baloney
import *
ก็ถือว่าเป็นวิธีการเขียนโค้ดที่ไม่ดี คุณจะทำสิ่งนี้โดยปราศจากสิ่งนั้นได้อย่างไร
ฉันเบื่อกับปัญหานี้ด้วยตัวเองดังนั้นฉันจึงเขียนแพคเกจชื่อ automodinit เพื่อแก้ไข คุณสามารถรับได้จากhttp://pypi.python.org/pypi/automodinit/ http://pypi.python.org/pypi/automodinit/
การใช้งานเป็นเช่นนี้:
automodinit
แพ็คเกจไว้ในการsetup.py
อ้างอิงของคุณ__all__ = ["ฉันจะได้รับการเขียนใหม่"] # อย่าแก้ไขบรรทัดด้านบนหรือบรรทัดนี้! นำเข้าเครื่องอัตโนมัติ automodinit.automodinit (__ name__, __file__, globals ()) del automodinit # อะไรก็ได้ที่คุณต้องการสามารถไปได้หลังจากที่นี่มันจะไม่ถูกแก้ไข
แค่นั้นแหละ! นับจากนี้ไปการนำเข้าโมดูลจะตั้งค่า __all__ เป็นรายการของไฟล์. py [co] ในโมดูลและจะนำเข้าแต่ละไฟล์เหล่านั้นราวกับว่าคุณได้พิมพ์:
for x in __all__: import x
ดังนั้นผลกระทบของ "จาก M import *" จึงตรงกับ "import M"
automodinit
มีความสุขในการทำงานจากภายในคลัง ZIP และดังนั้นจึงปลอดภัย ZIP
เนียล
ฉันรู้ว่าฉันกำลังอัปเดตโพสต์ค่อนข้างเก่าและฉันลองใช้automodinit
แต่พบว่ากระบวนการติดตั้งใช้งานไม่ได้กับ python3 ดังนั้นตามคำตอบของ Luca ฉันจึงได้คำตอบที่ง่ายขึ้นซึ่งอาจใช้ไม่ได้กับ. zip สำหรับปัญหานี้ดังนั้นฉันจึงคิดว่าฉันควรแชร์ที่นี่:
ภายใน__init__.py
โมดูลจากyourpackage
:
#!/usr/bin/env python
import os, pkgutil
__all__ = list(module for _, module, _ in pkgutil.iter_modules([os.path.dirname(__file__)]))
และภายในแพ็คเกจอื่นด้านล่างyourpackage
:
from yourpackage import *
จากนั้นคุณจะมีโมดูลทั้งหมดที่อยู่ในแพ็คเกจที่โหลดและถ้าคุณเขียนโมดูลใหม่มันจะถูกนำเข้าโดยอัตโนมัติเช่นกัน แน่นอนว่าใช้สิ่งต่าง ๆ ด้วยความระมัดระวังด้วยพลังอันยิ่งใหญ่มาพร้อมความรับผิดชอบที่ยิ่งใหญ่
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
for imp, module, ispackage in pkgutil.walk_packages(path=__path__, prefix=__name__+'.'):
__import__(module)
ฉันยังพบปัญหานี้และนี่เป็นวิธีแก้ปัญหาของฉัน:
import os
def loadImports(path):
files = os.listdir(path)
imps = []
for i in range(len(files)):
name = files[i].split('.')
if len(name) > 1:
if name[1] == 'py' and name[0] != '__init__':
name = name[0]
imps.append(name)
file = open(path+'__init__.py','w')
toWrite = '__all__ = '+str(imps)
file.write(toWrite)
file.close()
ฟังก์ชั่นนี้สร้างไฟล์ (ในโฟลเดอร์ที่ให้ไว้) __init__.py
ซึ่งมีชื่อซึ่งมี__all__
ตัวแปรที่เก็บทุกโมดูลในโฟลเดอร์
ตัวอย่างเช่นฉันมีชื่อโฟลเดอร์Test
ซึ่งประกอบด้วย:
Foo.py
Bar.py
ดังนั้นในสคริปต์ฉันต้องการให้โมดูลถูกนำเข้าไปยังฉันจะเขียน:
loadImports('Test/')
from Test import *
สิ่งนี้จะนำเข้าทุกอย่างจากTest
และ__init__.py
ไฟล์Test
จะมี:
__all__ = ['Foo','Bar']
ตัวอย่างของ Anurag พร้อมการแก้ไขสองประการ:
import os, glob
modules = glob.glob(os.path.join(os.path.dirname(__file__), "*.py"))
__all__ = [os.path.basename(f)[:-3] for f in modules if not f.endswith("__init__.py")]
ตอบ Anurag Uniyalพร้อมการปรับปรุงที่แนะนำ!
#!/usr/bin/python
# -*- encoding: utf-8 -*-
import os
import glob
all_list = list()
for f in glob.glob(os.path.dirname(__file__)+"/*.py"):
if os.path.isfile(f) and not os.path.basename(f).startswith('_'):
all_list.append(os.path.basename(f)[:-3])
__all__ = all_list
เห็นว่าคุณกำหนด__init__.py
โมดูล - แพคเกจ doc กล่าวว่า__all__
__init__.py
ไฟล์จะต้องทำให้งูหลามรักษาไดเรกทอรีที่เป็นแพคเกจที่มี; สิ่งนี้ถูกทำเพื่อป้องกันไดเร็กทอรีที่มีชื่อทั่วไปเช่นสตริงจากการซ่อนโมดูลที่ถูกต้องโดยไม่ตั้งใจซึ่งเกิดขึ้นในภายหลังบนพา ธ การค้นหาโมดูล ในกรณีที่ง่ายที่สุด__init__.py
อาจเป็นไฟล์ว่างเปล่า แต่ก็ยังสามารถรันโค้ดการเริ่มต้นสำหรับแพ็คเกจหรือตั้งค่า__all__
ตัวแปรอธิบายไว้ในภายหลัง...
ทางออกเดียวสำหรับผู้สร้างแพ็คเกจเพื่อจัดทำดัชนีที่ชัดเจนของแพ็คเกจ คำสั่งการนำเข้าใช้การประชุมดังต่อไปนี้: หาก
__init__.py
รหัสของแพ็คเกจกำหนดรายการชื่อ__all__
จะถูกนำไปเป็นรายการชื่อโมดูลที่ควรนำเข้าเมื่อพบแพ็คเกจจากการนำเข้า * ขึ้นอยู่กับผู้สร้างแพ็คเกจเพื่อให้รายการนี้เป็นปัจจุบันเมื่อมีการออกแพ็คเกจใหม่ ผู้เขียนแพ็คเกจอาจตัดสินใจที่จะไม่สนับสนุนหากพวกเขาไม่เห็นการใช้งานเพื่อนำเข้า * จากแพ็คเกจของพวกเขา ตัวอย่างเช่นไฟล์sounds/effects/__init__.py
อาจมีรหัสต่อไปนี้:
__all__ = ["echo", "surround", "reverse"]
นี่หมายความว่า
from sound.effects import *
จะนำเข้าชุดข้อมูลย่อยที่มีชื่อสามชุด
นี่เป็นวิธีที่ดีที่สุดที่ฉันค้นพบ:
from os.path import dirname, join, isdir, abspath, basename
from glob import glob
pwd = dirname(__file__)
for x in glob(join(pwd, '*.py')):
if not x.startswith('__'):
__import__(basename(x)[:-3], globals(), locals())
ใช้importlib
สิ่งเดียวที่คุณต้องเพิ่มคือ
from importlib import import_module
from pathlib import Path
__all__ = [
import_module(f".{f.stem}", __package__)
for f in Path(__file__).parent.glob("*.py")
if "__" not in f.stem
]
del import_module, Path
error: Type of __all__ must be "Sequence[str]", not "List[Module]"
mypy: การกำหนด__all__
ไม่จำเป็นถ้าimport_module
ใช้วิธีการตามนี้
ดูโมดูล pkgutil จากไลบรารีมาตรฐาน มันจะช่วยให้คุณทำสิ่งที่คุณต้องการตราบใดที่คุณมี__init__.py
ไฟล์ในไดเรกทอรี __init__.py
ไฟล์สามารถเป็นที่ว่างเปล่า
ฉันได้สร้างโมดูลสำหรับสิ่งนั้นซึ่งไม่พึ่งพา__init__.py
(หรือไฟล์เสริมอื่น ๆ ) และทำให้ฉันพิมพ์เพียงสองบรรทัดต่อไปนี้:
import importdir
importdir.do("Foo", globals())
รู้สึกอิสระที่จะนำกลับมาใช้ใหม่หรือมีส่วนร่วม: http://gitlab.com/aurelien-lourot/importdir
เพียงแค่นำเข้าพวกเขาโดยimportlibและเพิ่มลงใน__all__
( add
การกระทำเป็นตัวเลือก) ในการ__init__.py
บรรจุในแพ็คเกจ
/Foo
bar.py
spam.py
eggs.py
__init__.py
# __init__.py
import os
import importlib
pyfile_extes = ['py', ]
__all__ = [importlib.import_module('.%s' % filename, __package__) for filename in [os.path.splitext(i)[0] for i in os.listdir(os.path.dirname(__file__)) if os.path.splitext(i)[1] in pyfile_extes] if not filename.startswith('__')]
del os, importlib, pyfile_extes
py
เมื่อfrom . import *
ไม่เพียงพอที่ดีนี้คือการปรับปรุงมากกว่าคำตอบโดยเท็ด โดยเฉพาะการใช้__all__
ไม่จำเป็นต้องใช้วิธีการนี้
"""Import all modules that exist in the current directory."""
# Ref https://stackoverflow.com/a/60861023/
from importlib import import_module
from pathlib import Path
for f in Path(__file__).parent.glob("*.py"):
module_name = f.stem
if (not module_name.startswith("_")) and (module_name not in globals()):
import_module(f".{module_name}", __package__)
del f, module_name
del import_module, Path
โปรดทราบว่าmodule_name not in globals()
มีวัตถุประสงค์เพื่อหลีกเลี่ยงการนำเข้าโมดูลอีกครั้งหากนำเข้ามาแล้วเนื่องจากอาจเสี่ยงต่อการนำเข้าแบบวนซ้ำ