ใครช่วยให้ฉันมีวิธีที่ดีในการนำเข้าไดเรกทอรีทั้งหมดของโมดูล?
ฉันมีโครงสร้างเช่นนี้:
/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 baloneyimport *ก็ถือว่าเป็นวิธีการเขียนโค้ดที่ไม่ดี คุณจะทำสิ่งนี้โดยปราศจากสิ่งนั้นได้อย่างไร
ฉันเบื่อกับปัญหานี้ด้วยตัวเองดังนั้นฉันจึงเขียนแพคเกจชื่อ 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()มีวัตถุประสงค์เพื่อหลีกเลี่ยงการนำเข้าโมดูลอีกครั้งหากนำเข้ามาแล้วเนื่องจากอาจเสี่ยงต่อการนำเข้าแบบวนซ้ำ