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()
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')
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)."""
import importlib
module = importlib.import_module('my_package.my_module')
my_class = getattr(module, 'MyClass')
my_instance = my_class()
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)
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')
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'.
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()
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