การสร้างอินสแตนซ์แบบไดนามิกจากชื่อสตริงของคลาสในโมดูลที่นำเข้าแบบไดนามิกหรือไม่


180

ในไพ ธ อนฉันต้องสร้างอินสแตนซ์ของคลาสโดยรู้ชื่อของมันในสตริง แต่คลาสนี้ 'อยู่' ในโมดูลที่อิมพอร์ตแบบไดนามิก ตัวอย่างดังต่อไปนี้:

สคริปต์ระดับโหลดเดอร์:

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

สคริปต์โมดูลโหลดบางส่วนแบบไดนามิก:

class myName:
  # etc...

ฉันใช้การจัดเรียงนี้เพื่อทำให้โมดูลโหลดแบบไดนามิกใด ๆ ที่จะใช้โดยคลาสโหลดเดอร์หลังจากพฤติกรรมที่กำหนดไว้ล่วงหน้าบางอย่างใน Dyn-load-modules ...

คำตอบ:


251

คุณสามารถใช้getattr

getattr(module, class_name)

เพื่อเข้าชั้นเรียน รหัสที่สมบูรณ์มากขึ้น:

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

ตามที่ระบุไว้ด้านล่างเราอาจใช้ importlib

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

3
module = __import__(module, fromlist=[name])ทำงานให้ฉันเท่านั้น
umpirsky

16
หากใครมีปัญหากับวิธีการนำเข้าสเวนกล่าวข้างต้นผมพบว่ารหัสของฉันทำงานดีกว่าการใช้วิธีการต่อไปนี้แทนimportlib.import_module สามารถใช้งานได้เช่น: module = importlib.import_module (module_name)
jpennell

@jpennell คุณควรโพสต์นั้นเป็นคำตอบบ่อยครั้งจะมีประโยชน์มากกว่าที่จะสามารถใช้สตริงที่ส่งคืนโดยตรงobj.__module__
Anentropic

importlib.import_moduleจะโหลดไฟล์. py ลงใน pyc ถ้าจำเป็นรวมถึงจัดการกับ module.name.pathing.to.get.to.the คลาสที่สมบูรณ์ __import__จะไม่ทำอย่างใดอย่างหนึ่งในสิ่งเหล่านี้ในสภาพแวดล้อม django (ไม่ได้ทดสอบนอกนี้)
James

123

TL; DR

อิมพอร์ตโมดูลรูทด้วยimportlib.import_moduleและโหลดคลาสด้วยชื่อโดยใช้getattrฟังก์ชัน:

# 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()

คำอธิบาย

คุณอาจไม่ต้องการใช้__import__เพื่อนำเข้าโมดูลตามชื่อแบบไดนามิกเนื่องจากไม่อนุญาตให้คุณนำเข้า submodules:

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

นี่คือสิ่งที่หลาม doc พูดเกี่ยวกับ__import__:

หมายเหตุ: นี่เป็นฟังก์ชั่นขั้นสูงที่ไม่จำเป็นในการเขียนโปรแกรม Python ทุกวันซึ่งแตกต่างจาก importlib.import_module ()

ให้ใช้importlibโมดูลมาตรฐานเพื่อนำเข้าโมดูลตามชื่อแบบไดนามิกแทน เมื่อgetattrคุณสามารถยกตัวอย่างคลาสโดยใช้ชื่อ:

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

คุณสามารถเขียน:

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

รหัสนี้ใช้ได้ใน python ≥ 2.7 (รวมถึง python 3)


1
บางทีฉันอาจไม่เข้าใจคำตอบของคุณ แต่ฉันได้ใช้การนำเข้าเพื่อนำเข้า submodules: __import __ ("module." + submodule_name_string)
Javier Novoa C.

โค้ดต่อไปนี้ส่งผลให้เกิด AttributeError: mod = __import__("os.path"); mod.joinในขณะที่โค้ดต่อไปนี้ไม่ได้:mod = importlib.import_module("os.path"); mod.join
Régis B.

โอ้ฉันเห็นว่าคุณพูดถูก แต่ฉันได้ทำสิ่งต่อไปนี้เพื่อให้ได้วิธี os.path.join: getattr (sys.modules ["os.path"], "เข้าร่วม")
Javier Novoa C.

ฮ่า! และฉันเห็นว่าเมื่อใช้สิ่งนั้น ' นำเข้า ' นั้นไม่จำเป็น XD
Javier Novoa C.

2
ตัวเลือกที่ทำเครื่องหมายว่าเป็นคำตอบไม่ได้ผลสำหรับฉัน (โดยใช้ submodules) แต่คำตอบนี้ใช้ได้ ฉันคิดว่ามันควรจะเป็นคำตอบเพราะมันเป็นคำตอบทั่วไปสำหรับการโหลดโมดูลแบบไดนามิก
rkachach

14

ใช้getattrเพื่อรับแอตทริบิวต์จากชื่อในสตริง ในคำอื่น ๆ รับอินสแตนซ์เป็น

instance = getattr(modul, class_name)()

10

ตัวอย่างคัดลอกวาง:

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

หากคุณต้องการfrom foo.bar import foo2ให้โหลดประโยคนี้แบบไดนามิกคุณควรทำสิ่งนี้

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

instance = foo2()

4

หนึ่งสามารถใช้pydoc.locateฟังก์ชั่น

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

2

ฉันไม่สามารถไปถึงได้ในกรณีการใช้งานของฉันจากตัวอย่างด้านบน แต่อาห์หมัดทำให้ฉันใกล้เคียงที่สุด (ขอบคุณ) สำหรับผู้ที่อ่านบทความนี้ในอนาคตนี่คือรหัสที่เหมาะกับฉัน

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.