Dinamik olarak içe aktarılan modülde bir sınıfın dize adından dinamik örnekleme?


180

Python, adını bir dize içinde bilmek, belirli bir sınıf başlatmak zorunda, ancak bu sınıf dinamik olarak içe aktarılan bir modülde 'yaşıyor'. Bir örnek şöyledir:

loader sınıfı komut dosyası:

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

bazı dinamik olarak yüklenmiş modül betiği:

class myName:
  # etc...

Bu düzenlemeyi, dyn yüklü modüllerde önceden tanımlanmış belirli davranışları izleyerek loader sınıfı tarafından kullanılacak dinamik olarak yüklenmiş herhangi bir modülü yapmak için kullanıyorum ...

Yanıtlar:


251

Getattr kullanabilirsiniz

getattr(module, class_name)

sınıfa erişmek için. Daha eksiksiz kod:

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

Aşağıda belirtildiği gibi importlib kullanabiliriz

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

3
module = __import__(module, fromlist=[name])sadece benim için çalıştı.
umpirsky

16
Herkes Sven yukarıda belirtilen alma yöntemi ile sorun yaşıyorsanız, benim kod importlib.import_module yerine aşağıdaki yöntemi kullanarak daha iyi çalıştı bulundu . Gibi kullanılabilir: module = importlib.import_module (module_name)
jpennell

@jpennell yanıt olarak göndermelisiniz, döndürülen dizeyi doğrudan kullanabilmek genellikle daha yararlıdırobj.__module__
Anentropic

importlib.import_module.py dosyasını gerekirse bir pyc'ye yükleyecek ve tüm module.name.pathing.to.get.to.the sınıfını işleyecektir. __import__django ortamında (bunun dışında test edilmedi) bu iki durumdan hiçbirini yapmayacak
James

123

tl; Dr.

Kök modülünü içe aktarın ve işlevi importlib.import_modulekullanarak sınıfı adına göre yükleyin 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()

açıklamalar

__import__Alt modülleri içe aktarmanıza izin vermediğinden, muhtemelen bir modülü adıyla dinamik olarak içe aktarmak için kullanmak istemezsiniz :

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

İşte piton doc sözleriyle ifade ediyor __import__:

Not: Bu importlib.import_module () 'den farklı olarak günlük Python programlamasında gerekli olmayan gelişmiş bir fonksiyondur.

Bunun yerine, importlibbir modülü adıyla dinamik olarak içe aktarmak için standart modülü kullanın. Daha getattrsonra bir sınıfı adıyla başlatabilirsiniz:

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

Ayrıca şunu da yazabilirsiniz:

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

Bu kod python ≥ 2.7 için geçerlidir (python 3 dahil).


1
belki cevabınızı anlamıyorum, ancak alt modülleri içe aktarmak için içe aktarma kullandım : __import __ ("modül." + alt modül_adı_string)
Javier Novoa C.

Aşağıdaki kod bir AttributeError ile sonuçlanır: mod = __import__("os.path"); mod.joinaşağıdakiler gerçekleşmez:mod = importlib.import_module("os.path"); mod.join
Régis B.

oh görüyorum, haklısın ama os.path.join yöntemini almak için aşağıdakileri yaptım: getattr (sys.modules ["os.path"], "join")
Javier Novoa C.

Ha! ve görüyorum ki, o zaman ' ithalat ' gereksiz XD
Javier Novoa C.

2
Cevap olarak işaretlenen seçenek benim için işe yaramadı (alt modülleri kullanarak) ancak bu cevap işe yarıyor. Modülleri dinamik olarak yüklemek için daha genel bir çözüm olduğu için bunun cevap olması gerektiğini düşünüyorum.
rkachach

14

getattrBir dizedeki addan bir öznitelik almak için kullanın . Başka bir deyişle, örneği aşağıdaki gibi alın:

instance = getattr(modul, class_name)()

10

Snippet'i kopyala yapıştır:

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

Bu cümlenin from foo.bar import foo2dinamik olarak yüklenmesini istiyorsanız, bunu yapmalısınız

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

instance = foo2()

4

Sadece pydoc.locateişlevi kullanabilirsiniz .

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

2

Yukarıdaki örneklerden kullanım durumumda tam olarak oraya varamadım, ama Ahmad beni en yakına getirdi (teşekkür ederim). Gelecekte bunu okuyanlar için, işte benim için çalışan kod.

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.