So laden Sie eine Python-Klasse dynamisch


167

my_package.my_module.MyClassWas ist bei einer Zeichenfolge einer Python-Klasse der beste Weg, um sie zu laden?

Mit anderen Worten, ich suche ein Äquivalent Class.forName()in Java, eine Funktion in Python. Es muss mit Google App Engine funktionieren.

Vorzugsweise ist dies eine Funktion, die den FQN der Klasse als Zeichenfolge akzeptiert und einen Verweis auf die Klasse zurückgibt:

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

Ich muss in der Lage sein, die Klassenreferenz auch einer Variablen zuzuweisen.
pjesi

1
Dies scheint ein Duplikat von zu sein: stackoverflow.com/questions/452969/…
cdleary

Sie haben Recht, es ist ein Duplikat, danke, dass Sie es gefunden haben
pjesi

1
Ich musste noch nie eine solche Klasse in Python laden. Sie wissen, wo sich das Modul befindet. Warum also nicht einfach das Modul laden und dann seine Klassen verwenden, wie Python es möchte
Adam Spence,

22
Wenn Sie dies noch nie getan haben, haben Sie noch nicht genügend interessante Programme geschrieben. Übe weiter.
John Tyree

Antworten:


194

In der Python-Dokumentation finden Sie folgende 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 einfaches __import__nicht funktioniert, ist, dass jeder Import von etwas nach dem ersten Punkt in einer Paketzeichenfolge ein Attribut des Moduls ist, das Sie importieren. So etwas funktioniert nicht:

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

Sie müssten die obige Funktion folgendermaßen aufrufen:

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

Oder im Fall Ihres Beispiels:

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

EDIT : Ich war ein bisschen daneben. Was Sie im Grunde tun möchten, ist Folgendes:

from my_package.my_module import my_class

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

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

Ich habe my_import ('my_package.my_module.my_class') ausprobiert, aber kein Modul gefunden my_class, was sinnvoll ist, da es sich um eine Klasse und nicht um ein Modul handelt. Wie auch immer, wenn ich gettattr verwenden kann, um die Klasse nach dem Aufruf von my_import
pjesi

Das ist seltsam. Alles nach dem ersten Punkt wird mit getattr aufgerufen. Es sollte keinen Unterschied geben.
Jason Baker

Danke, ich denke das ist der beste Weg. Jetzt brauche ich nur noch den besten Weg, um die Zeichenfolge 'my_pakcage.my_module.my_class' in mod_name, klass_name aufzuteilen, aber ich denke, ich kann das herausfinden :)
pjesi

2
Die Python-Dokumentation (im Code) des Imports besagt, dass importlib verwendet werden soll.
Also


125

Wenn Sie nicht selbst rollen möchten, steht im pydocModul eine Funktion zur Verfügung , die genau dies ausführt:

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

Der Vorteil dieses Ansatzes gegenüber den anderen hier aufgeführten ist , dass locatefinden alle an dem vorgesehenen gepunkteten Pfad Python - Objekt, nicht nur ein Objekt direkt innerhalb eines Moduls. zB my_package.my_module.MyClass.attr.

Wenn Sie neugierig sind, was ihr Rezept ist, ist hier 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 hängt von der pydoc.safeimportFunktion ab. 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)."""

1
Ich habe diese Antwort positiv bewertet. Übrigens, hier ist der Code, der auch safeimport hat, da es seltsam erscheint, pydoc nur dafür zu importieren: github.com/python/cpython/blob/…
brianray

Ich habe auch diese Antwort positiv bewertet, dies ist die beste aller relevanten Antworten.
Sunding Wei

Dies scheint auch in der Lage zu sein qualname(Objekt nicht oben im Modul-Namespace) korrekt zu handhaben .
MisterMiyagi

Ja, Sie können tunlocate('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
Sobald Sie das Modul dynamisch importiert haben, haben Sie über das Modul Zugriff auf die Klasse
Adam Spence

Ich habe gerade meine Antwort bearbeitet, um sie präziser zu gestalten. Dies ist der beste Weg, um eine Klasse in Python zu laden.
Adam Spence

BBB-Benny und die Spence! ;)
dKen

Toll! Es ist sogar Teil der Standardbibliothek ab 2.7.
Apteryx

Dies ist der richtige Weg, um auf ein Modul / eine Klasse zuzugreifen. In den Dokumenten wird dies hier angegeben: docs.python.org/3/library/importlib.html#importlib.__import__
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
Das ist die saubere Lösung! Sie könnten in Betracht ziehen:(modulename, classname) = cl.rsplit('.', 2)
vdboor

Es ist großartig) Ich hatte ein Putils-Paket mit verschiedenen Utils erstellt und dort auch Klassenimporte durchgeführt. Wenn Sie möchten, können Sie es aus diesem Paket verwenden.
Stan

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

Ich habe es geschafft, {} anstelle von Globals / Einheimischen zu übergeben und es funktioniert immer noch gut
Valtron

17

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, um nach einer Django-Lösung zu suchen, habe keine gefunden und sie hier für den nächsten Jungen / das nächste Mädchen, das danach sucht, abgelegt.

# 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 keine hat ., wie reoder argparseverwendet:

re = __import__('re')

5

Hier ist etwas zu teilen, was ich gefunden habe __import__und importlibwährend ich versucht habe, dieses Problem zu lösen.

Ich benutze Python 3.7.3.

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

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

Die modVariablen beziehen sich auf den oberen Namespace a.

Um in die Klasse zu kommen d, 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 zu suchen a.d.

Bei der Verwendung importlibhat die Bibliothek vermutlich die Rekursion getattrfür uns durchgeführt. Wenn wir also verwenden importlib.import_module, 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

1

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'


0

Wenn Sie bereits eine Instanz Ihrer gewünschten Klasse haben, können Sie mit der Funktion 'type' den Klassentyp extrahieren und damit 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()


-2

In Google App Engine gibt es eine webapp2Funktion namens import_string. Weitere Informationen finden Sie hier: https://webapp-improved.appspot.com/api/webapp2.html

So,

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

Dies wird beispielsweise verwendet, webapp2.Routewenn Sie entweder einen Handler oder eine Zeichenfolge verwenden können.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.