Cách tải động một lớp Python


167

Đưa ra một chuỗi của một lớp Python, ví dụ my_package.my_module.MyClass, cách tốt nhất có thể để tải nó là gì?

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

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

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

Tôi cần có khả năng gán tham chiếu lớp cho một biến.
pjesi

1
Điều này dường như là một bản sao của: stackoverflow.com/questions/452969/ từ
cdleary

Bạn nói đúng đó là một bản sao, cảm ơn vì đã tìm thấy nó
pjesi

1
Tôi chưa bao giờ cần phải tải một lớp như thế này trong Python. Bạn biết mô-đun ở đâu vậy tại sao không tải mô-đun và sau đó sử dụng các lớp như Python muốn bạn, đơn giản hơn nhiều
Adam Spence

22
Nếu bạn chưa bao giờ cần phải làm điều này thì bạn chưa viết chương trình đủ thú vị. Tiếp tục tập luyện.
John Tyree

Câu trả lời:


194

Từ 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à vì bất kỳ nhập bất kỳ thứ gì qua dấu chấm đầu tiên trong chuỗi gói là một thuộc tính của mô-đun bạn đang nhập. Do đó, một cái gì đó như thế này sẽ không hoạt động:

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

Bạn sẽ phải gọi các chức năng trên như vậy:

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 tắt về điều này. Những gì bạn về cơ bản muốn làm là đây:

from my_package.my_module import my_class

Các chức năng trên chỉ cần thiết nếu bạn có một danh sách trống . Do đó, cuộc gọi thích hợp sẽ như thế này:

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

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

Thật ki quặc. Tất cả mọi thứ qua dấu 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.
Jason Baker

Cảm ơn tôi nghĩ rằng đây là cách tốt nhất. Bây giờ tôi chỉ cần cách tốt nhất để phân tách chuỗi 'my_pakcage.my_module.my_group' thành mod_name, klass_name nhưng tôi đoán tôi có thể tìm ra điều đó :)
pjesi

2
tài liệu python (về mã) của nhập khẩu nói rằng sử dụng importlib. vì vậy nên kiểm tra câu trả lời của Adam Spence
naoko

1
Liên kết @naoko là đề cập đến: docs.python.org/3/library/importlib.html#importlib.__import__
Noel Evans

125

Nếu bạn không muốn tự lăn, có một chức năng có sẵn trong pydocmô-đun thực hiện chính xác điều này:

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

Ưu điểm của phương pháp này so với các phương pháp khác được liệt kê ở đây là locatesẽ tìm thấy bất kỳ đối tượng python nào tại đường dẫn chấm được cung cấp, không chỉ là 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ò công thức của họ là gì, thì đâ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.safeimportchức năng. Dưới đây là các 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 nêu lên câu trả lời này. BTW, đây là mã mà cũng có safeimport vì nó có vẻ kỳ lạ để pydoc nhập khẩu chỉ cho việc này: github.com/python/cpython/blob/...
brianray

Tôi cũng đánh giá cao câu trả lời này, đây là câu trả lời hay nhất trong tất cả các câu trả lời có liên quan.
Sunding Wei

Điều này dường như cũng có thể xử lý chính xác qualname(đối tượng không nằm trên không gian tên mô-đun).
MisterMiyagi

vâng, bạn có thể làmlocate('my_package.my_module.MyClass.attr.method.etc')
chadrik

109
import importlib

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

9
một khi bạn đã nhập mô-đun một cách linh hoạt, bạn có quyền truy cập vào lớp thông qua mô-đun
Adam Spence

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 trong Python.
Adam Spence

BBB-Benny và Spence! ;)
dKen

Tuyệt quá! Nó thậm chí là một phần của thư viện tiêu chuẩn từ 2.7 trở lên.
Apterx

Đây là cách chính xác để truy cập một mô-đun / lớp. Các tài liệu nêu rõ điều này tại đây: docs.python.org/3/l Library / imllib.html
Noel Evans

29
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ể cân nhắc sử dụng:(modulename, classname) = cl.rsplit('.', 2)
vdboor

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

4
@vdboor rsplit('.', 1)?
Carles Barrobés

Tôi đã cố gắng vượt qua {} thay vì toàn cầu / người dân địa phương và nó vẫn hoạt động tốt
valtron

17

Nếu bạn đang sử dụng Django, bạn có thể sử dụng nó. Vâng, tôi biết OP đã không yêu cầu django, nhưng tôi đã chạy qua câu hỏi này để tìm giải pháp Django, không tìm thấy giải pháp nào và đặt nó ở đây cho cậu bé / gal tiếp theo tìm kiếm nó.

# It's available for v1.7+
# https://github.com/django/django/blob/stable/1.7.x/django/utils/module_loading.py
from django.utils.module_loading import import_string

Klass = import_string('path.to.module.Klass')
func = import_string('path.to.module.func')
var = import_string('path.to.module.var')

Hãy ghi nhớ, nếu bạn muốn nhập một cái gì đó không có ., thích rehoặc argparsesử dụng:

re = __import__('re')

5

Đây là để chia sẻ một cái gì đó tôi tìm thấy trên __import__importlib trong khi cố gắng giải quyết vấn đề này.

Tôi đang sử dụng Python 3.7.3.

Khi tôi cố gắng đến lớp dtrong mô-đun a.b.c,

mod = __import__('a.b.c')

Các modbiến tham khảo các namespace đầu a.

Vì vậy, để đến lớp d, tôi cần phải

mod = getattr(mod, 'b') #mod is now module b
mod = getattr(mod, 'c') #mod is now module c
mod = getattr(mod, 'd') #mod is now class d

Nếu chúng ta cố gắng làm

mod = __import__('a.b.c')
d = getattr(mod, 'd')

chúng tôi đang thực sự cố gắng tìm kiếm a.d.

Khi sử dụng importlib, tôi cho rằng thư viện đã thực hiện đệ quy getattrcho chúng tôi. Vì vậy, khi chúng tôi sử dụng importlib.import_module, chúng tôi thực sự có được một mô-đun sâu nhất.

mod = importlib.import_module('a.b.c') #mod is module c
d = getattr(mod, 'd') #this is a.b.c.d

1

OK, đối với tôi đó là cách nó hoạt động (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'


0

Nếu bạn đã có một thể hiện của lớp mong muốn, bạn có thể sử dụng hàm 'type' để trích xuất loại lớp của nó và sử dụng điều này để xây dựng một thể hiện mới:

class Something(object):
    def __init__(self, name):
        self.name = name
    def display(self):
        print(self.name)

one = Something("one")
one.display()
cls = type(one)
two = cls("two")
two.display()

-2
module = __import__("my_package/my_module")
the_class = getattr(module, "MyClass")
obj = the_class()

5
Lưu ý rằng điều này hoạt động vì một lỗi trong chức năng nhập khẩu . Đường dẫn tệp không nên được sử dụng trong chức năng nhập và sẽ không hoạt động trong python 2.6 trở lên: docs.python.org/whatsnew/2.6.html#porting-to-python-2-6
Jason Baker

-2

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

Vì thế,

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

Ví dụ, điều này được sử dụng ở webapp2.Routenơi bạn có thể sử dụng trình xử lý hoặc chuỗi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.