webentwicklung-frage-antwort-db.com.de

So laden Sie eine Python-Klasse dynamisch

Bei einer Zeichenfolge einer Python-Klasse, z. my_package.my_module.MyClass, wie kann ich ihn am besten laden?

Mit anderen Worten, ich suche nach einer äquivalenten Class.forName() in Java, Funktion in Python. Es muss mit Google App Engine funktionieren.

Vorzugsweise wäre dies eine Funktion, die den FQN der Klasse als String akzeptiert und einen Verweis auf die Klasse zurückgibt:

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

In der Python-Dokumentation finden Sie die gewünschte Funktion:

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

Der Grund, warum ein einfacher __import__ nicht funktioniert, ist, dass der Import von etwas, das über den ersten Punkt in einer Paketzeichenfolge hinausgeht, ein Attribut des Moduls ist, das Sie importieren. So etwas funktioniert nicht:

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

Sie müssten die obige Funktion wie folgt aufrufen:

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

Oder bei Ihrem Beispiel:

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

EDIT: Ich war ein bisschen dran. Was Sie grundsätzlich wollen, ist folgendes:

from my_package.my_module import my_class

Die obige Funktion ist nur erforderlich, wenn Sie eine empty fromlist haben. Der entsprechende Aufruf wäre also so:

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

Wenn Sie keine eigene Rolle ausführen möchten, steht im Modul pydoc eine Funktion zur Verfügung, die genau dies tut:

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

Der Vorteil dieses Ansatzes gegenüber den anderen hier aufgelisteten Methoden besteht darin, dass locate am angegebenen gestrichelten Pfad any python-Objekt findet, nicht nur ein Objekt direkt in einem Modul. z.B. my_package.my_module.MyClass.attr.

Wenn Sie neugierig sind, was das Rezept ist, hier ist die Funktion:

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

Es beruht auf der pydoc.safeimport-Funktion. Hier sind die Dokumente dafür:

"""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)."""
93
chadrik
import importlib

module = importlib.import_module('my_package.my_module')
my_class = getattr(module, 'MyClass')
my_instance = my_class()
68
Adam Spence
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)
28
Stan

Wenn Sie Django verwenden, können Sie dies verwenden. Ja, ich bin mir bewusst, dass OP nicht nach Django gefragt hat, aber ich bin auf diese Frage gestoßen, auf der Suche nach einer Django-Lösung, habe keine gefunden und habe sie hier für den nächsten Jungen/die nächste Frau abgelegt, die danach sucht.

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

Denken Sie daran, wenn Sie etwas importieren möchten, das keinen . hat, wie re oder argparse, verwenden Sie:

re = __import__('re')
2
Javier Buzzi

OK, für mich hat das so funktioniert (ich benutze Python 2.7):

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

Dann ist b eine Instanz der Klasse 'MyClass'.

1
lfvv

Wenn Sie bereits über eine Instanz der gewünschten Klasse verfügen, können Sie mit der Funktion 'type' den Klassentyp extrahieren und eine neue Instanz erstellen:

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()
0
Jason DeMorrow

Hier ist etwas, das ich bei __import__ und importlib gefunden habe, als ich versuchte, dieses Problem zu lösen.

Ich benutze Python 3.7.3.

Wenn ich versuche, zur Klasse d im Modul a.b.c zu gelangen,

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

Die Variable mod verweist auf den oberen Namespace a.

Also, um zur Klasse d zu gelangen, muss ich

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

Wenn wir es versuchen

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

wir versuchen tatsächlich, nach a.d zu suchen.

Bei Verwendung von importlib hat die Bibliothek die rekursive getattr für uns erstellt. Wenn wir also importlib.import_module verwenden, bekommen wir tatsächlich das tiefste Modul in den Griff.

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