2009-02-13 27 views
104

Cho một chuỗi lớp Python, ví dụ: my_package.my_module.MyClass, cách tốt nhất có thể để tải nó là gì?Cách tải động một lớp Python

Nói cách khác, tôi đang tìm kiếm một số tương đương Class.forName() trong Java, hoạt động bằng Python. Nó cần phải hoạt động trên Google App Engine.

Tốt đây sẽ là một chức năng chấp nhận các FQN của lớp như là một chuỗi, và trả về một tham chiếu đến các lớp:

my_class = load_class('my_package.my_module.MyClass') 
my_instance = my_class() 
+0

tôi cần để có thể gán lớp tham chiếu đến một biến là tốt. – pjesi

+1

Điều này có vẻ là một bản sao của: http://stackoverflow.com/questions/452969/does-python-have-an-equivalent-to-java-class-forname – cdleary

+0

Bạn nói đúng là một bản sao, cảm ơn vì đã tìm kiếm nó – pjesi

Trả lời

139

Từ các tài liệu python, đây là chức năng bạn muốn:

def my_import(name): 
    components = name.split('.') 
    mod = __import__(components[0]) 
    for comp in components[1:]: 
     mod = getattr(mod, comp) 
    return mod 

lý do đơn giản __import__ sẽ không hoạt động là bởi vì bất kỳ nhập khẩu bất cứ điều gì quá khứ chấm đầu tiên trong một chuỗi gói là một thuộc tính của mô-đun bạn im porting. Do đó, một cái gì đó như thế này sẽ không làm việc:

__import__('foo.bar.baz.qux') 

Bạn sẽ phải gọi hàm trên như sau:

my_import('foo.bar.baz.qux') 

Hoặc trong trường hợp ví dụ của bạn:

klass = my_import('my_package.my_module.my_class') 
some_object = klass() 

EDIT: Tôi có một chút về điều này. Những gì bạn đang về cơ bản muốn làm điều này là:

from my_package.my_module import my_class 

Chức năng ở trên là chỉ cần thiết nếu bạn có một trống fromlist. Như vậy, gọi thích hợp sẽ là như thế này:

mod = __import__('my_package.my_module', fromlist=['my_class']) 
klass = getattr(mod, 'my_class') 
+0

Tôi đã thử my_import ('my_package.my_module.my_class') nhưng không tìm thấy module nào my_class, điều này có ý nghĩa vì nó là một lớp không phải là một mô-đun. Làm thế nào nếu tôi có thể sử dụng gettattr để nhận được lớp sau khi cuộc gọi đến my_import – pjesi

+0

Điều đó thật kỳ quặc. Mọi thứ qua chấm đầu tiên được gọi là sử dụng getattr. Không nên có bất kỳ sự khác biệt nào. –

+1

Đã tìm ra. Xem bài chỉnh sửa bài đăng. –

-3
module = __import__("my_package/my_module") 
the_class = getattr(module, "MyClass") 
obj = the_class() 
+5

Lưu ý rằng thao tác này do lỗi trong hàm __import__. Đường dẫn tệp * không * sẽ được sử dụng trong hàm __import__ và sẽ không hoạt động ở trăn 2.6 trở lên: http://docs.python.org/whatsnew/2.6.html#porting-to-python-2-6 –

+0

sẽ hoạt động với "." thay vì gạch chéo "/" – Nick

25
def import_class(cl): 
    d = cl.rfind(".") 
    classname = cl[d+1:len(cl)] 
    m = __import__(cl[0:d], globals(), locals(), [classname]) 
    return getattr(m, classname) 
+5

Đây là giải pháp sạch! Bạn có thể xem xét sử dụng: '(modulename, classname) = cl.rsplit ('.', 2)' – vdboor

+0

Thật tuyệt vời) Tôi đã tạo ra các gói putils với các utils khác nhau, cũng là lớp nhập vào đó. Nếu bạn muốn, bạn có thể sử dụng nó từ gói đó. – Stan

+4

@vdboor 'rsplit ('.', 1)'? –

47
import importlib 

module = importlib.import_module('my_package.my_module') 
my_class = getattr(module, 'MyClass') 
my_instance = my_class() 
+0

không hoạt động. 'import_module' nhập khẩu một mô-đun không phải lớp. – Xuan

+7

khi bạn đã nhập mô-đun tự động bạn có quyền truy cập vào lớp học qua mô-đun –

+0

Chỉ cần chỉnh sửa câu trả lời của tôi ngắn gọn hơn. Đây là cách tốt nhất để tải một lớp bằng Python. –

72

Nếu bạn không muốn cuộn của riêng bạn, có một chức năng có sẵn trong các mô-đun pydoc thực hiện chính xác này:

from pydoc import locate 
my_class = locate('my_package.my_module.MyClass') 

Lợi thế của cách tiếp cận này so với các phương pháp khác được liệt kê ở đây là locate sẽ tìm thấy bất kỳ đối tượng python nào ở đường chấm chấm được cung cấp, chứ không phải j ust một đối tượng trực tiếp trong một mô-đun. ví dụ. my_package.my_module.MyClass.attr.

Nếu bạn tò mò những gì công thức của họ là, đây là chức năng:

def locate(path, forceload=0): 
    """Locate an object by name or dotted path, importing as necessary.""" 
    parts = [part for part in split(path, '.') if part] 
    module, n = None, 0 
    while n < len(parts): 
     nextmodule = safeimport(join(parts[:n+1], '.'), forceload) 
     if nextmodule: module, n = nextmodule, n + 1 
     else: break 
    if module: 
     object = module 
    else: 
     object = __builtin__ 
    for part in parts[n:]: 
     try: 
      object = getattr(object, part) 
     except AttributeError: 
      return None 
    return object 

Nó dựa vào pydoc.safeimport chức năng. Dưới đây là những tài liệu cho rằng:

"""Import a module; handle errors; return None if the module isn't found. 

If the module *is* found but an exception occurs, it's wrapped in an 
ErrorDuringImport exception and reraised. Unlike __import__, if a 
package path is specified, the module at the end of the path is returned, 
not the package at the beginning. If the optional 'forceload' argument 
is 1, we reload the module from disk (unless it's a dynamic extension).""" 
+1

Tôi đã bỏ phiếu tán thành này. BTW, đây là mã mà cũng có safeimport vì nó có vẻ kỳ lạ để nhập khẩu pydoc chỉ cho điều này: https://github.com/python/cpython/blob/aa8ea3a6be22c92e774df90c6a6ee697915ca8ec/Lib/pydoc.py#L288 – brianray

+0

Tôi upvoted câu trả lời này quá , đây là câu trả lời phù hợp nhất. –

+0

bạn cần thêm một hàm init trống __init __ (self, get_response = None): pass –

-2

Trong Google App Engine có được một hàm webapp2 gọi import_string.Để biết thêm xem tại đây: https://webapp-improved.appspot.com/api/webapp2.html

Vì vậy,

import webapp2 
my_class = webapp2.import_string('my_package.my_module.MyClass') 

Ví dụ này được sử dụng trong webapp2.Route nơi bạn có thể sử dụng một handler hoặc một chuỗi.

1

OK, đối với tôi đó là cách nó làm việc (Tôi đang sử dụng Python 2.7):

a = __import__('file_to_import', globals(), locals(), ['*'], -1) 
b = a.MyClass() 

Sau đó, b là một thể hiện của lớp 'MyClass'

Các vấn đề liên quan