2010-07-29 7 views
26

Tôi có một cấu trúc thư mục như sau:Làm cách nào để nhập tất cả các mô-đun con?

| main.py 
| scripts 
|--| __init__.py 
    | script1.py 
    | script2.py 
    | script3.py 

Từ main.py, module scripts được nhập khẩu. Tôi đã thử sử dụng pkgutils.walk_packages kết hợp với __all__, nhưng khi sử dụng, tôi chỉ có thể nhập tất cả các mô-đun con dưới trực tiếp main sử dụng from scripts import *. Tôi muốn nhận tất cả chúng dưới scripts. Điều gì sẽ là cách sạch nhất để nhập tất cả các mô-đun con của scripts để tôi có thể truy cập scripts.script1 từ main?

EDIT: Tôi xin lỗi vì tôi hơi mơ hồ. Tôi muốn nhập các mô-đun con vào thời gian chạy mà không chỉ rõ chúng trong __init__.py. Tôi có thể sử dụng pkgutils.walk_packages để có được tên submodule (trừ khi ai đó biết một cách tốt hơn), nhưng tôi không chắc chắn cách sạch nhất để sử dụng những tên này (hoặc có thể là ImpImporters rằng walk_packages trả về?) Để nhập chúng.

+1

sẽ pkgutil việc nếu bạn triển khai ứng dụng như một quả trứng nén? bạn có thể xem "nhập pkg_resources", chỉ trong trường hợp –

Trả lời

25

Edit: Dưới đây là một cách để đệ quy nhập khẩu tất cả mọi thứ trong thời gian chạy ...

Nó sử dụng exec, do đó gần như chắc chắn một cách tốt hơn, nhưng nó làm việc (ngay cả đối với tùy tiện lồng gói con, tôi suy nghĩ).

(Nội dung __init__.py trong thư mục gói đầu)

import pkgutil 

__all__ = [] 
for loader, module_name, is_pkg in pkgutil.walk_packages(__path__): 
    __all__.append(module_name) 
    module = loader.find_module(module_name).load_module(module_name) 
    exec('%s = module' % module_name) 

tôi không sử dụng __import__(__path__+'.'+module_name) đây, vì nó rất khó để đúng cách đệ quy nhập gói sử dụng nó. Nếu bạn không có các gói con lồng nhau, và muốn tránh các exec/eval, mặc dù, đó là một cách để làm điều đó.

Có thể là cách tốt hơn, nhưng đây là cách tốt nhất tôi có thể làm.

gốc trả lời (Đối với bối cảnh, bỏ qua othwerwise tôi hiểu lầm câu hỏi ban đầu.):

gì nhìn scripts/__init__.py của bạn như thế nào? Nó phải là một cái gì đó như:

import script1 
import script2 
import script3 
__all__ = ['script1', 'script2', 'script3'] 

Bạn thậm chí có thể làm mà không cần xác định __all__, nhưng mọi thứ (pydoc, nếu không có gì khác) sẽ làm việc sạch hơn nếu bạn định nghĩa nó, ngay cả khi nó chỉ là một danh sách những gì bạn nhập khẩu.

+1

Xin lỗi vì không giải thích đầy đủ. Tôi đã chỉnh sửa bài đăng chính. Tôi đang tìm cách nhập các mô-đun con một cách linh động vào thời gian chạy. – linkmaster03

+0

@ linkmaster03 - Xin lỗi vì sự hiểu lầm. Xem những thay đổi ở trên ... Nó xấu xí, nhưng nó hoạt động. Có lẽ là một cách tốt hơn, mặc dù. –

+0

Cảm ơn! :) Tôi thấy rằng bằng cách sử dụng '__import__' và không cần phải nhập các tập lệnh bằng tên của chúng thực sự làm việc ra độc đáo, bởi vì tôi chỉ có thể lặp qua các mô-đun mà không cần tên của chúng. – linkmaster03

1

Tôi đã viết một thư viện cá nhân nhỏ và thêm các mô-đun mới tất cả các thời gian vì vậy tôi đã viết một kịch bản shell để tìm kiếm các kịch bản và tạo ra các __init__.py của. Kịch bản lệnh được thực hiện ngay bên ngoài thư mục chính cho gói của tôi, pylux.

Tôi biết nó có lẽ không phải là câu trả lời bạn đang tìm kiếm, nhưng nó đã phục vụ mục đích của nó cho tôi và nó có thể hữu ích cho người khác nữa.

#!/bin/bash 

echo 'Traversing folder hierarchy...' 

CWD=`pwd` 


for directory in `find pylux -type d -exec echo {} \;`; 
do 
    cd $directory 
    #echo Entering $directory 
    echo -n "" > __init__.py 

    for subdirectory in `find . -type d -maxdepth 1 -mindepth 1`; 
    do 
     subdirectory=`echo $subdirectory | cut -b 3-` 
     #echo -n ' ' ...$subdirectory 
     #echo -e '\t->\t' import $subdirectory 
     echo import $subdirectory >> __init__.py 
    done 

    for pyfile in *.py ; 
    do 
     if [ $pyfile = $(echo __init__.py) ]; then 
      continue 
     fi 
     #echo -n ' ' ...$pyfile 
     #echo -e '\t->\t' import `echo $pyfile | cut -d . -f 1` 
     echo import `echo $pyfile | cut -d . -f 1` >> __init__.py 
    done 
    cd $CWD 

done 


for directory in `find pylux -type d -exec echo {} \;`; 
do 
    echo $directory/__init__.py: 
    cat $directory/__init__.py | awk '{ print "\t"$0 }' 
done 
+1

Không thể viết cùng một logic trong '__init __. Py'? Sử dụng 'os.listdir' và' __import__'? – cji

+0

Hmm. Tôi cho là vậy, nhưng tôi phải xem xét nó nhiều hơn một chút. – physicsmichael

2

Tôi đã mệt mỏi vì vấn đề này, vì vậy tôi đã viết một gói có tên là automodinit để khắc phục sự cố. Bạn có thể lấy nó từ http://pypi.python.org/pypi/automodinit/.Cách sử dụng như sau:

  1. Bao gồm gói tự động vào phụ thuộc setup.py của bạn.
  2. Thêm dòng sau vào đầu file __init__.py:
__all__ = ["I will get rewritten"] 
# Don't modify the line above, or this line! 
import automodinit 
automodinit.automodinit(__name__, __file__, globals()) 
del automodinit 
# Anything else you want can go after here, it won't get modified. 

Vậy là xong! Từ giờ trở đi nhập khẩu một mô-đun sẽ thiết lập __all__ để một danh sách các py [đồng] file trong mô-đun và cũng sẽ nhập khẩu mỗi của các tập tin như thể bạn đã gõ:

for x in __all__: import x 

Do đó ảnh hưởng của from M import * khớp chính xác import M.

automodinit là hạnh phúc chạy từ bên trong lưu trữ ZIP và do đó ZIP an toàn.

1

Tôi đã chơi xung quanh với Joe Kington's Answer và đã xây dựng giải pháp sử dụng globalsget/setattr và do đó không cần eval. Một sửa đổi nhỏ là thay vì trực tiếp sử dụng các gói __path__ cho walk_packages, tôi sử dụng thư mục mẹ của gói và sau đó chỉ nhập mô-đun bắt đầu bằng __name__ + ".". Điều này đã được thực hiện để nhận được tất cả các gói con một cách đáng tin cậy từ walk_packages - trong trường hợp sử dụng của tôi, tôi đã có một gói con có tên là test khiến pkgutil lặp lại gói test từ thư viện của python; hơn nữa, việc sử dụng __path__ sẽ không được chuyển vào các thư mục con của gói. Tất cả những vấn đề này đã được quan sát bằng cách sử dụng jython và python2.5, mã dưới đây chỉ được thử nghiệm trong jython cho đến nay. Cũng cần lưu ý rằng câu hỏi OPs chỉ nói về việc nhập khẩu tất cả mô-đun từ một gói, mã này đệ quy nhập tất cả các gói quá.

from pkgutil import walk_packages 
from os import path 

__all__ = [] 
__pkg_prefix = "%s." % __name__ 
__pkg_path = path.abspath(__path__[0]).rsplit("/", 1)[0] #parent directory 

for loader, modname, _ in walk_packages([__pkg_path]): 
    if modname.startswith(__pkg_prefix): 
     #load the module/package 
     module = loader.find_module(modname).load_module(modname) 
     modname = modname[len(__pkg_prefix):] #strip package prefix from name 
     #append all toplevel modules and packages to __all__ 
     if not "." in modname: 
      __all__.append(modname) 
      globals()[modname] = module 
     #set everything else as an attribute of their parent package 
     else: 
      #get the toplevel package from globals() 
      pkg_name, rest = modname.split(".", 1) 
      pkg = globals()[pkg_name] 
      #recursively get the modules parent package via getattr 
      while "." in rest: 
       subpkg, rest = rest.split(".", 1) 
       pkg = getattr(pkg, subpkg) 
      #set the module (or package) as an attribute of its parent package 
      setattr(pkg, rest, module) 

Như một sự cải thiện trong tương lai tôi sẽ cố gắng để làm điều này năng động với một cái móc __getattr__ trên bao bì, vì vậy các module thực tế chỉ được nhập khẩu khi chúng được truy cập ...

0

này hoạt động độc đáo cho tôi bằng Python 3.3. Lưu ý rằng thao tác này chỉ hoạt động đối với các mô-đun con trong các tệp trong cùng thư mục với số __init__.py. Với một số công việc tuy nhiên nó có thể được tăng cường để hỗ trợ submodules trong thư mục quá.

from glob import iglob 
from os.path import basename, relpath, sep, splitext 

def import_submodules(__path__to_here): 
    """Imports all submodules. 
    Import this function in __init__.py and put this line to it: 
    __all__ = import_submodules(__path__)""" 
    result = [] 
    for smfile in iglob(relpath(__path__to_here[0]) + "/*.py"): 
     submodule = splitext(basename(smfile))[0] 
     importstr = ".".join(smfile.split(sep)[:-1]) 
     if not submodule.startswith("_"): 
      __import__(importstr + "." + submodule) 
      result.append(submodule) 
    return result 
+1

điều này sẽ không hoạt động trong trứng và gói zip – kolypto

9

Đơn giản chỉ cần làm việc, và cho phép nhập khẩu tương đối bên trong gói:

def import_submodules(package_name): 
    """ Import all submodules of a module, recursively 

    :param package_name: Package name 
    :type package_name: str 
    :rtype: dict[types.ModuleType] 
    """ 
    package = sys.modules[package_name] 
    return { 
     name: importlib.import_module(package_name + '.' + name) 
     for loader, name, is_pkg in pkgutil.walk_packages(package.__path__) 
    } 

Cách sử dụng:

__all__ = import_submodules(__name__).keys() 
17

này được dựa trên the answer that kolypto provided, nhưng câu trả lời của ông không thực hiện nhập khẩu đệ quy các gói, trong khi điều này có. Mặc dù không được yêu cầu bởi câu hỏi chính, tôi tin rằng nhập khẩu đệ quy được áp dụng và có thể rất hữu ích trong nhiều tình huống tương tự. Tôi, đối với một, tìm thấy câu hỏi này khi tìm kiếm về chủ đề này.

Đây là một cách tốt, sạch sẽ để thực hiện nhập các mô-đun của gói con, và cũng phải di động, và nó sử dụng chuẩn lib cho python 2.7+/3.x.

import importlib 
import pkgutil 


def import_submodules(package, recursive=True): 
    """ Import all submodules of a module, recursively, including subpackages 

    :param package: package (name or actual module) 
    :type package: str | module 
    :rtype: dict[str, types.ModuleType] 
    """ 
    if isinstance(package, str): 
     package = importlib.import_module(package) 
    results = {} 
    for loader, name, is_pkg in pkgutil.walk_packages(package.__path__): 
     full_name = package.__name__ + '.' + name 
     results[full_name] = importlib.import_module(full_name) 
     if recursive and is_pkg: 
      results.update(import_submodules(full_name)) 
    return results 

Cách sử dụng:

# from main.py, as per the OP's project structure 
import scripts 
import_submodules(scripts) 

# Alternatively, from scripts.__init__.py 
import_submodules(__name__) 
2

Không gần như sạch sẽ như tôi muốn, nhưng không ai trong số các phương pháp sạch hơn làm việc cho tôi. Điều này đạt được các hành vi quy định: Cơ cấu

Directory:

| pkg 
|--| __init__.py 
    | main.py 
    | scripts 
    |--| __init__.py 
     | script1.py 
     | script2.py 
     | script3.py 

đâu pkg/scripts/__init__.py là trống rỗng, và pkg/__init__.py chứa:

import importlib as _importlib 
import pkgutil as _pkgutil 
__all__ = [_mod[1].split(".")[-1] for _mod in 
      filter(lambda _mod: _mod[1].count(".") == 1 and not 
           _mod[2] and __name__ in _mod[1], 
        [_mod for _mod in _pkgutil.walk_packages("." + __name__)])] 
__sub_mods__ = [".".join(_mod[1].split(".")[1:]) for _mod in 
       filter(lambda _mod: _mod[1].count(".") > 1 and not 
            _mod[2] and __name__ in _mod[1], 
         [_mod for _mod in 
         _pkgutil.walk_packages("." + __name__)])] 
from . import * 
for _module in __sub_mods__: 
    _importlib.import_module("." + _module, package=__name__) 

Mặc dù nó lộn xộn, nó phải là cầm tay. Tôi đã sử dụng mã này cho một số gói khác nhau.

+0

: - | Chỉ cần tò mò - những gì về các phương pháp sạch hơn đã không làm việc? –

+0

Tbh, tôi không nhớ .. xin lỗi! Giới hạn có thể không còn tồn tại/áp dụng nữa, vì vậy nó có thể đáng để kiểm tra. Tôi không chắc chắn nếu nó địa chỉ các câu hỏi, nhưng ngày nay tôi sử dụng '__all__ = [_mod [1] cho _mod trong _pkgutil.iter_modules (__ path__) nếu không _mod [2]]'. Tôi khá chắc chắn rằng các chức năng 'iter_modules' không tồn tại trở lại sau đó. – user2561747

0

Trong Python 3, bạn có thể đặt đoạn mã sau vào tập tin scripts.__init__.py của bạn:

import os 
import os.path as op 

__all__ = [ 
    op.splitext(f)[0] # remove .py extension 
    for f in os.listdir(BASE_DIR) # list contents of current dir 
    if not f.startswith('_') and 
    ((op.isfile(op.join(BASE_DIR, f)) and f.endswith('.py')) or 
    (op.isdir(op.join(BASE_DIR, f)) and op.isfile(op.join(BASE_DIR, f, '__init__.py')))) 
] 

from . import * # to make `scripts.script1` work after `import script` 

Đối với thông tin thêm về nhập khẩu Python, tôi khuyên bạn nên nói chuyện David Beazley tại PyCon 2015: https://youtu.be/0oTh1CXRaQ0