193 votes

Comment charger dynamiquement une classe Python ?

Étant donné une chaîne de caractères d'une classe Python, par exemple my_package.my_module.MyClass Quelle est la meilleure façon de le charger ?

En d'autres termes, je cherche un équivalent Class.forName() en Java, fonction en Python. Il doit fonctionner sur Google App Engine.

Il s'agirait de préférence d'une fonction qui accepte le FQN de la classe sous la forme d'une chaîne de caractères et qui renvoie une référence à la classe :

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

0 votes

Je dois également être en mesure d'affecter la référence de la classe à une variable.

1 votes

Cela semble être un double de : stackoverflow.com/questions/452969/

0 votes

Vous avez raison, c'est un doublon, merci de l'avoir trouvé.

220voto

Jason Baker Points 56682

D'après la documentation python, voici la fonction que vous voulez :

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

La raison en est simple __import__ ne fonctionne pas, car toute importation de quelque chose au-delà du premier point d'une chaîne de paquets est un attribut du module que vous importez. Ainsi, quelque chose comme ceci ne fonctionnera pas :

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

Vous devriez 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()

EDITAR : Je me suis un peu trompé sur ce point. Ce que vous voulez faire en gros, c'est ça :

from my_package.my_module import my_class

La fonction ci-dessus n'est nécessaire que si vous avez une vide de la liste. Ainsi, l'appel approprié serait comme ceci :

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

0 votes

J'ai essayé my_import('my_package.my_module.my_class') mais je n'ai pas trouvé de module my_class, ce qui est logique puisque c'est une classe et non un module. Cependant, si je peux utiliser gettattr pour obtenir la classe après l'appel à my_import

0 votes

C'est bizarre. Tout ce qui dépasse le premier point est appelé en utilisant getattr. Il ne devrait pas y avoir de différence.

0 votes

Merci, je pense que c'est la meilleure solution. Maintenant, j'ai seulement besoin de la meilleure façon de diviser la chaîne 'mon_pakcage.mon_module.ma_classe' en nom de module, nom de classe, mais je pense que je peux le faire :)

154voto

chadrik Points 81

Si vous n'avez pas envie de le faire vous-même, il existe une fonction disponible dans l'application 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 énumérées ici est que locate trouvera cualquier objet python au niveau du chemin pointé fourni, et pas seulement un objet directement dans un module, par exemple my_package.my_module.MyClass.attr .

Si vous êtes curieux de savoir quelle est 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 pydoc.safeimport fonction. Voici la documentation à ce sujet :

"""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)."""

2 votes

J'ai upvoted cette réponse. BTW, voici le code qui a également safeimport car il semble étrange d'importer pydoc juste pour cela : github.com/python/cpython/blob/

0 votes

J'ai également voté en faveur de cette réponse, qui est la meilleure de toutes les réponses pertinentes.

1 votes

Cela semble être capable de gérer qualname (objet ne se trouvant pas au sommet de l'espace de noms du module) correctement aussi.

136voto

Adam Spence Points 416
import importlib

module = importlib.import_module('my_package.my_module')
my_class = getattr(module, 'MyClass')
my_instance = my_class()

10 votes

Une fois que vous avez importé le module dynamiquement, vous avez accès à la classe via le module

0 votes

Je viens de modifier ma réponse pour être plus concis. C'est la meilleure façon de charger une classe en Python.

0 votes

B-B-B-Benny et les Spence ! ;)

29voto

Stanislav Points 1505
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 votes

C'est la solution propre ! Vous pourriez envisager d'utiliser : (modulename, classname) = cl.rsplit('.', 2)

0 votes

C'est génial) J'avais créé le paquet putils avec différents utils, ainsi que l'importation de classes. Si vous voulez, vous pouvez l'utiliser à partir de ce paquet.

4 votes

@vdboor rsplit('.', 1) ?

-2voto

oefe Points 9122
module = __import__("my_package/my_module")
the_class = getattr(module, "MyClass")
obj = the_class()

6 votes

Notez que cela fonctionne en raison d'un bogue dans le programme import fonction. Les chemins d'accès aux fichiers doivent no être utilisé dans le import et ne fonctionnera pas dans python 2.6 et plus : docs.python.org/whatsnew/2.6.html#porting-to-python-2-6

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X