2008-11-19 34 views
363

Tôi đang viết một ứng dụng Python mà mất như một lệnh làm tham số, ví dụ:động mô-đun nhập khẩu bằng Python

$ python myapp.py command1 

tôi muốn ứng dụng được mở rộng, có nghĩa là, để có thể thêm các mô đun mới triển khai các lệnh mới mà không phải thay đổi nguồn ứng dụng chính. Cây trông giống như sau:

myapp/ 
    __init__.py 
    commands/ 
     __init__.py 
     command1.py 
     command2.py 
    foo.py 
    bar.py 

Vì vậy, tôi muốn ứng dụng tìm các mô-đun lệnh sẵn có khi chạy và thực hiện mô-đun thích hợp.

Hiện nay điều này được thực hiện một cái gì đó như:

command = sys.argv[1] 
try: 
    command_module = __import__("myapp.commands.%s" % command, fromlist=["myapp.commands"]) 
except ImportError: 
    # Display error message 

command_module.run() 

này hoạt động tốt, tôi chỉ tự hỏi nếu có thể là một cách thành ngữ hơn để thực hiện những gì chúng tôi đang làm với mã này.

Lưu ý rằng tôi đặc biệt không muốn sử dụng trứng hoặc điểm mở rộng. Đây không phải là một dự án mã nguồn mở và tôi không mong đợi có được "bổ sung". Vấn đề là đơn giản hóa mã ứng dụng chính và loại bỏ nhu cầu sửa đổi nó mỗi lần thêm một mô-đun lệnh mới.

+0

Gì fromlist = [ "myapp.commands"] làm gì? –

+0

@ PieterMüller: trong một loại trình bao python: '' dir (__ import __) ''. Danh sách từ phải là danh sách các tên để mô phỏng "từ nhập tên ...". – mawimawi

+2

[Nhập mô-đun từ biến chuỗi] (http://stackoverflow.com/questions/8718885/import-module-from-string-variable) –

Trả lời

228

Với Python cũ hơn 2,7/3,1, đó là khá nhiều cách bạn làm điều đó. Đối với các phiên bản mới hơn, hãy xem importlib.import_modulefor 2.7+for 3.1+.

Bạn có thể sử dụng exec nếu bạn muốn.

Lưu ý bạn có thể nhập một danh sách các module bằng cách làm này:

>>> moduleNames = ['sys', 'os', 're', 'unittest'] 
>>> moduleNames 
['sys', 'os', 're', 'unittest'] 
>>> modules = map(__import__, moduleNames) 

Ripped thẳng từ Dive Into Python.

+5

differece để exec là gì? – user1767754

+0

Bạn có thể sử dụng '__init__' của mô-đun đó như thế nào? –

+2

Một vấn đề với giải pháp này cho OP là bẫy các ngoại lệ cho một hoặc hai mô-đun "lệnh" xấu khiến toàn bộ lệnh ứng dụng của nó không thành công trên một ngoại lệ. Cá nhân tôi muốn lặp lại trên mỗi __import__ riêng lẻ bọc trong một thử: mods = __ import __() \ nexcept ImportError là lỗi: báo cáo (lỗi) để cho phép các lệnh khác tiếp tục hoạt động trong khi lệnh xấu được sửa. – DevPlayer

9

Bạn có thể sử dụng exec:

exec "import myapp.commands.%s" % command 
+0

Làm thế nào để có được một xử lý cho các mô-đun thông qua exec, vì vậy mà tôi có thể gọi (trong ví dụ này) phương thức .run()? –

+5

Sau đó bạn có thể thực hiện getattr (myapp.commands, lệnh) để truy cập vào mô-đun. –

+1

... hoặc thêm 'như command_module' đến hết tuyên bố nhập khẩu và sau đó làm 'command_module.run()' – oxfn

117

Như đã đề cập các module imp cung cấp cho bạn tải chức năng:

imp.load_source(name, path) 
imp.load_compiled(name, path) 

Tôi đã sử dụng này trước khi thực hiện một cái gì đó tương tự.

Trong trường hợp của tôi, tôi đã xác định một lớp cụ thể với các phương thức được xác định đã được yêu cầu. Khi tôi nạp module tôi sẽ kiểm tra nếu lớp là trong các mô-đun, và sau đó tạo ra một thể hiện của lớp đó, một cái gì đó như thế này:

import imp 
import os 

def load_from_file(filepath): 
    class_inst = None 
    expected_class = 'MyClass' 

    mod_name,file_ext = os.path.splitext(os.path.split(filepath)[-1]) 

    if file_ext.lower() == '.py': 
     py_mod = imp.load_source(mod_name, filepath) 

    elif file_ext.lower() == '.pyc': 
     py_mod = imp.load_compiled(mod_name, filepath) 

    if hasattr(py_mod, expected_class): 
     class_inst = getattr(py_mod, expected_class)() 

    return class_inst 
+1

Giải pháp tốt và đơn giản. Tôi đang viết một cái tương tự: http://stamat.wordpress.com/dynamic-module-import-in-python/ Nhưng của bạn có một số sai sót: Điều gì về ngoại lệ? IOError và ImportError? Tại sao không kiểm tra phiên bản đã biên dịch trước và sau đó cho phiên bản nguồn. Mong đợi một lớp làm giảm khả năng sử dụng lại trong trường hợp của bạn. – stamat

+2

Trong dòng nơi bạn xây dựng MyClass trong mô-đun đích, bạn sẽ thêm một tham chiếu dư thừa vào tên lớp. Nó đã được lưu trữ trong 'expected_class', do đó bạn có thể làm' class_inst = getattr (py_mod, expected_name)() 'để thay thế. – Amoss

+1

* Lưu ý rằng nếu một tệp được biên dịch phù hợp với byte (có hậu tố .pyc hoặc .pyo) tồn tại, nó sẽ được sử dụng thay vì phân tích cú pháp tệp nguồn đã cho *. [https://docs.python.org/2/library/imp.html#imp.load_source](https://docs.python.org/2/library/imp.html#imp.load_source) – cdosborn

0

Nghe có vẻ giống như những gì bạn thực sự muốn là một kiến ​​trúc plugin.

Bạn nên xem xét chức năng entry points được cung cấp bởi gói thiết lập. Nó cung cấp một cách tuyệt vời để khám phá các plugin được tải cho ứng dụng của bạn.

+6

Tôi đã đề cập cụ thể muốn sử dụng điểm vào và kiến ​​trúc kiểu plugin. Lý do thiết kế có nhiều khả năng bảo trì và mô đun mã hơn là cho phép các plugin tùy ý được đưa vào. –

-6

Sau đây làm việc cho tôi:

import sys, glob 
sys.path.append('/home/marc/python/importtest/modus') 
fl = glob.glob('modus/*.py') 
modulist = [] 
adapters=[] 
for i in range(len(fl)): 
    fl[i] = fl[i].split('/')[1] 
    fl[i] = fl[i][0:(len(fl[i])-3)] 
    modulist.append(getattr(__import__(fl[i]),fl[i])) 
    adapters.append(modulist[i]()) 

Nó nạp module từ thư mục 'modus'. Các mô-đun có một lớp duy nhất có cùng tên với tên mô-đun. Ví dụ. tệp modus/modu1.py chứa:

class modu1(): 
    def __init__(self): 
     self.x=1 
     print self.x 

Kết quả là danh sách các lớp "bộ điều hợp" được nạp động.

+12

Xin vui lòng không chôn câu trả lời mà không cung cấp một lý do trong các ý kiến. Nó không giúp được ai. – Rebs

+0

Tôi muốn biết tại sao đây là nội dung xấu. – DevPlayer

+0

Giống như tôi, vì tôi mới sử dụng Python và muốn biết, tại sao điều này là xấu. – Kosmos

13

Nếu bạn muốn nó trong người dân địa phương của bạn:

>>> mod = 'sys' 
>>> locals()['my_module'] = __import__(mod) 
>>> my_module.version 
'2.6.6 (r266:84297, Aug 24 2010, 18:46:32) [MSC v.1500 32 bit (Intel)]' 

cùng sẽ làm việc với globals()

206

Cách khuyến khích cho Python 2.7 và sau đó là sử dụng importlib mô-đun:

my_module = importlib.import_module('os.path') 
+2

Được đề xuất bởi nguồn hoặc cơ quan nào? – michuelnik

+42

Tài liệu tư vấn [chống lại] (https://docs.python.org/2/library/functions.html#__import__) bằng cách sử dụng hàm '__import__' có lợi cho mô-đun được đề cập ở trên. –

+5

Tính năng này hoạt động tốt để nhập 'os.path'; làm thế nào về 'từ os.path nhập khẩu *'? –

3

tương tự dưới dạng giải pháp của @monkut nhưng có thể sử dụng lại và có khả năng chịu lỗi được mô tả ở đây http://stamat.wordpress.com/dynamic-module-import-in-python/:

import os 
import imp 

def importFromURI(uri, absl): 
    mod = None 
    if not absl: 
     uri = os.path.normpath(os.path.join(os.path.dirname(__file__), uri)) 
    path, fname = os.path.split(uri) 
    mname, ext = os.path.splitext(fname) 

    if os.path.exists(os.path.join(path,mname)+'.pyc'): 
     try: 
      return imp.load_compiled(mname, uri) 
     except: 
      pass 
    if os.path.exists(os.path.join(path,mname)+'.py'): 
     try: 
      return imp.load_source(mname, uri) 
     except: 
      pass 

    return mod 
0

Các mảnh dưới đây làm việc cho tôi:

>>>import imp; 
>>>fp, pathname, description = imp.find_module("/home/test_module"); 
>>>test_module = imp.load_module("test_module", fp, pathname, description); 
>>>print test_module.print_hello(); 

nếu bạn muốn nhập khẩu trong vỏ kịch bản:

python -c '<above entire code in one line>'