Étant donné la chaîne d'une classe Python, par ex. my_package.my_module.MyClass
, quel est le meilleur moyen de le charger?
En d'autres termes, je recherche une Class.forName()
équivalente en Java, une fonction en Python. Il doit fonctionner sur Google App Engine.
De préférence, il s’agit d’une fonction qui accepte le FQN de la classe en tant que chaîne et renvoie une référence à la classe:
my_class = load_class('my_package.my_module.MyClass')
my_instance = my_class()
Dans la documentation Python, voici la fonction que vous souhaitez:
def my_import(name):
components = name.split('.')
mod = __import__(components[0])
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
La raison pour laquelle un simple __import__
ne fonctionnera pas est que toute importation au-delà du premier point d'une chaîne de package est un attribut du module que vous importez. Ainsi, quelque chose comme ceci ne fonctionnera pas:
__import__('foo.bar.baz.qux')
Vous devez appeler la fonction ci-dessus comme suit:
my_import('foo.bar.baz.qux')
Ou dans le cas de votre exemple:
klass = my_import('my_package.my_module.my_class')
some_object = klass()
EDIT: J'étais un peu désemparé. Ce que vous voulez fondamentalement faire est la suivante:
from my_package.my_module import my_class
La fonction ci-dessus n'est nécessaire que si vous avez une liste de sélection empty. Ainsi, l'appel approprié serait comme ceci:
mod = __import__('my_package.my_module', fromlist=['my_class'])
klass = getattr(mod, 'my_class')
Si vous ne voulez pas lancer le vôtre, il existe une fonction disponible dans le module pydoc
qui fait exactement cela:
from pydoc import locate
my_class = locate('my_package.my_module.MyClass')
L’avantage de cette approche par rapport aux autres options répertoriées ici est que locate
trouvera un objet n’importe quel python dans le chemin en pointillé fourni, et pas seulement un objet directement dans un module. par exemple. my_package.my_module.MyClass.attr
.
Si vous êtes curieux de connaître leur recette, voici la fonction:
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
Il s'appuie sur la fonction pydoc.safeimport
. Voici la documentation pour cela:
"""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)
Si vous utilisez Django, vous pouvez l'utiliser. Oui, je suis conscient que OP n'a pas demandé Django, mais j'ai rencontré cette question à la recherche d'une solution Django, je n'en ai pas trouvé et je l'ai mise ici pour le prochain garçon/fille qui la recherche.
# 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')
N'oubliez pas que si vous souhaitez importer quelque chose qui n'a pas de .
, tel que re
ou argparse
, utilisez:
re = __import__('re')
OK, pour moi c'est comme cela que ça a fonctionné (j'utilise Python 2.7):
a = __import__('file_to_import', globals(), locals(), ['*'], -1)
b = a.MyClass()
Alors, b est une instance de la classe 'MyClass'
Si vous possédez déjà une instance de votre classe souhaitée, vous pouvez utiliser la fonction 'type' pour extraire son type de classe et l'utiliser pour créer une nouvelle instance:
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()
Voici pour partager quelque chose que j'ai trouvé sur __import__
et importlib
en essayant de résoudre ce problème.
J'utilise Python 3.7.3.
Quand j'essaie d'obtenir la classe d
dans le module a.b.c
,
mod = __import__('a.b.c')
La variable mod
fait référence à l'espace de noms supérieur a
.
Donc, pour arriver à la classe d
, je dois
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
Si nous essayons de faire
mod = __import__('a.b.c')
d = getattr(mod, 'd')
nous essayons en fait de rechercher a.d
.
En utilisant importlib
, je suppose que la bibliothèque a fait la récursive getattr
pour nous. Ainsi, lorsque nous utilisons importlib.import_module
, nous obtenons en fait une poignée sur le module le plus profond.
mod = importlib.import_module('a.b.c') #mod is module c
d = getattr(mod, 'd') #this is a.b.c.d