Khởi tạo động từ tên chuỗi của một lớp trong mô đun nhập động?


179

Trong python, tôi phải khởi tạo một số lớp nhất định, biết tên của nó trong một chuỗi, nhưng lớp này 'sống' trong một mô-đun được nhập động. Một ví dụ sau:

tập lệnh lớp nạp:

import sys
class loader:
  def __init__(self, module_name, class_name): # both args are strings
    try:
      __import__(module_name)
      modul = sys.modules[module_name]
      instance = modul.class_name() # obviously this doesn't works, here is my main problem!
    except ImportError:
       # manage import error

kịch bản mô-đun được tải động

class myName:
  # etc...

Tôi sử dụng sự sắp xếp này để làm cho bất kỳ mô-đun được tải động nào được sử dụng bởi lớp trình nạp theo các hành vi được xác định trước trong các mô-đun được tải dyn ...

Câu trả lời:


251

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

getattr(module, class_name)

để truy cập lớp. Mã đầy đủ hơn:

module = __import__(module_name)
class_ = getattr(module, class_name)
instance = class_()

Như đã đề cập dưới đây , chúng tôi có thể sử dụng importlib

import importlib
module = importlib.import_module(module_name)
class_ = getattr(module, class_name)
instance = class_()

3
module = __import__(module, fromlist=[name])chỉ làm việc cho tôi
umpirsky

16
Nếu bất cứ ai gặp rắc rối với phương thức nhập mà Sven đã đề cập ở trên, tôi thấy mã của tôi hoạt động tốt hơn bằng cách sử dụng phương thức sau thay vì importlib.import_module . Có thể được sử dụng như: module = importlib.import_module (module_name)
jpennell

@jpennell bạn nên đăng nó như một câu trả lời, thường hữu ích hơn khi có thể sử dụng trực tiếp chuỗi được trả về bởiobj.__module__
Anentropic

importlib.import_modulesẽ tải tệp .py vào pyc nếu cần cũng như xử lý mô-đun hoàn chỉnh.name.pathing.to.get.to.the. __import__sẽ không làm một trong những điều này, trong môi trường django (không được thử nghiệm bên ngoài điều này)
James

122

tl; dr

Nhập mô-đun gốc với importlib.import_modulevà tải lớp theo tên của nó bằng getattrhàm:

# Standard import
import importlib
# Load "module.submodule.MyClass"
MyClass = getattr(importlib.import_module("module.submodule"), "MyClass")
# Instantiate the class (pass arguments to the constructor, if needed)
instance = MyClass()

giải thích

Bạn có thể không muốn sử dụng __import__để nhập động một mô-đun theo tên, vì nó không cho phép bạn nhập các mô hình con:

>>> mod = __import__("os.path")
>>> mod.join
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'join'

Đây là những gì tài liệu python nói về __import__:

Lưu ý: Đây là một chức năng nâng cao không cần thiết trong lập trình Python hàng ngày, không giống như importlib.import_module ().

Thay vào đó, sử dụng importlibmô-đun chuẩn để nhập động mô-đun theo tên. Sau đó, getattrbạn có thể khởi tạo một lớp theo tên của nó:

import importlib
my_module = importlib.import_module("module.submodule")
MyClass = getattr(my_module, "MyClass")
instance = MyClass()

Bạn cũng có thể viết:

import importlib
module_name, class_name = "module.submodule.MyClass".rsplit(".", 1)
MyClass = getattr(importlib.import_module(module_name), class_name)
instance = MyClass()

Mã này hợp lệ trong python ≥ 2.7 (bao gồm cả python 3).


1
có thể tôi không hiểu câu trả lời của bạn, nhưng tôi đã sử dụng nhập để nhập các mô hình con: __import __ ("mô-đun." + Subodule_name_opes)
Javier Novoa C.

Đoạn mã sau dẫn đến AttributionError: mod = __import__("os.path"); mod.jointrong khi đoạn mã sau không:mod = importlib.import_module("os.path"); mod.join
Régis B.

ồ tôi hiểu rồi, bạn nói đúng nhưng tôi đã làm như sau để có được phương thức os.path.join: getattr (sys.modules ["os.path"], "tham gia")
Javier Novoa C.

ha! và tôi thấy rằng khi sử dụng điều đó, thì ' nhập ' là không cần thiết XD
Javier Novoa C.

2
Tùy chọn được đánh dấu là câu trả lời không hiệu quả với tôi (sử dụng mô hình con) tuy nhiên câu trả lời này có. Tôi nghĩ rằng nó nên là câu trả lời vì đó là giải pháp chung hơn cho các mô-đun tải động.
rkachach

14

Sử dụng getattrđể có được một thuộc tính từ một tên trong một chuỗi. Nói cách khác, lấy ví dụ như

instance = getattr(modul, class_name)()

10

Sao chép-dán đoạn trích:

import importlib
def str_to_class(module_name, class_name):
    """Return a class instance from a string reference"""
    try:
        module_ = importlib.import_module(module_name)
        try:
            class_ = getattr(module_, class_name)()
        except AttributeError:
            logging.error('Class does not exist')
    except ImportError:
        logging.error('Module does not exist')
    return class_ or None

5

Nếu bạn muốn câu này from foo.bar import foo2được tải động, bạn nên làm điều này

foo = __import__("foo")
bar = getattr(foo,"bar")
foo2 = getattr(bar,"foo2")

instance = foo2()

4

Một cách đơn giản có thể sử dụng pydoc.locatechức năng.

from pydoc import locate
my_class = locate("module.submodule.myclass")
instance = my_class()

2

Tôi hoàn toàn không thể đến đó trong trường hợp sử dụng của mình từ các ví dụ trên, nhưng Ahmad đã cho tôi gần nhất (cảm ơn bạn). Đối với những người đọc điều này trong tương lai, đây là mã làm việc cho tôi.

def get_class(fully_qualified_path, module_name, class_name, *instantiation):
    """
    Returns an instantiated class for the given string descriptors
    :param fully_qualified_path: The path to the module eg("Utilities.Printer")
    :param module_name: The module name eg("Printer")
    :param class_name: The class name eg("ScreenPrinter")
    :param instantiation: Any fields required to instantiate the class
    :return: An instance of the class
    """
    p = __import__(fully_qualified_path)
    m = getattr(p, module_name)
    c = getattr(m, class_name)
    instance = c(*instantiation)
    return instance
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.