J'ai besoin de prendre un argument de chaîne et de créer un objet de la classe nommée dans cette chaîne en Python. En Java, j'utiliserais Class.forName().newInstance()
. Existe-t-il un équivalent en Python?
Merci pour les réponses. Pour répondre à ceux qui veulent savoir ce que je fais: je veux utiliser un argument de ligne de commande comme nom de classe et l'instancier. Je suis en train de programmer en Jython et d'instancier des classes Java, d'où la Java-ness de la question. getattr()
fonctionne très bien. Merci beaucoup.
La réflexion en python est beaucoup plus facile et beaucoup plus flexible qu'en Java.
Je recommande de lire ceci tutoriel
Il n'y a pas de fonction directe (à ma connaissance) qui prenne un nom de classe complet et renvoie la classe, mais vous avez toutes les pièces nécessaires pour le construire et vous pouvez les connecter ensemble.
Un conseil cependant: n'essayez pas de programmer dans le style Java lorsque vous êtes en python.
Si vous pouvez expliquer ce que vous essayez de faire, nous pouvons peut-être vous aider à trouver une façon plus Pythonique de le faire.
Voici une fonction qui fait ce que vous voulez:
def get_class( kls ):
parts = kls.split('.')
module = ".".join(parts[:-1])
m = __import__( module )
for comp in parts[1:]:
m = getattr(m, comp)
return m
Vous pouvez utiliser la valeur de retour de cette fonction comme s'il s'agissait de la classe elle-même.
Voici un exemple d'utilisation:
>>> D = get_class("datetime.datetime")
>>> D
<type 'datetime.datetime'>
>>> D.now()
datetime.datetime(2009, 1, 17, 2, 15, 58, 883000)
>>> a = D( 2010, 4, 22 )
>>> a
datetime.datetime(2010, 4, 22, 0, 0)
>>>
Comment ça marche?
Nous utilisons __import__
Pour importer le module qui contient la classe, ce qui nous obligeait à extraire d'abord le nom du module du nom complet. Ensuite, nous importons le module:
m = __import__( module )
Dans ce cas, m
ne fera référence qu'au module de niveau supérieur,
Par exemple, si votre classe vit dans le module foo.baz
, Alors m
sera le module foo
Nous pouvons facilement obtenir une référence à foo.baz
En utilisant getattr( m, 'baz' )
Pour passer du module de niveau supérieur à la classe, vous devez utiliser récursivement gettatr
sur les parties du nom de la classe
Dites par exemple, si le nom de votre classe est foo.baz.bar.Model
Alors nous faisons ceci:
m = __import__( "foo.baz.bar" ) #m is package foo
m = getattr( m, "baz" ) #m is package baz
m = getattr( m, "bar" ) #m is module bar
m = getattr( m, "Model" ) #m is class Model
C'est ce qui se passe dans cette boucle:
for comp in parts[1:]:
m = getattr(m, comp)
À la fin de la boucle, m
sera une référence à la classe. Cela signifie que m
est en fait la classe elle-même, vous pouvez faire par exemple:
a = m() #instantiate a new instance of the class
b = m( arg1, arg2 ) # pass arguments to the constructor
En supposant que la classe est dans votre portée:
globals()['classname'](args, to, constructor)
Autrement:
getattr(someModule, 'classname')(args, to, constructor)
Edit: Remarque, vous ne pouvez pas donner un nom comme 'foo.bar' à getattr. Vous devrez le diviser par. et appelez getattr () sur chaque pièce de gauche à droite. Cela gérera cela:
module, rest = 'foo.bar.baz'.split('.', 1)
fooBar = reduce(lambda a, b: getattr(a, b), rest.split('.'), globals()[module])
someVar = fooBar(args, to, constructor)
def import_class_from_string(path):
from importlib import import_module
module_path, _, class_name = path.rpartition('.')
mod = import_module(module_path)
klass = getattr(mod, class_name)
return klass
In [59]: raise import_class_from_string('google.appengine.runtime.apiproxy_errors.DeadlineExceededError')()
---------------------------------------------------------------------------
DeadlineExceededError Traceback (most recent call last)
<ipython-input-59-b4e59d809b2f> in <module>()
----> 1 raise import_class_from_string('google.appengine.runtime.apiproxy_errors.DeadlineExceededError')()
DeadlineExceededError:
Encore une autre implémentation.
def import_class(class_string):
"""Returns class object specified by a string.
Args:
class_string: The string representing a class.
Raises:
ValueError if module part of the class is not specified.
"""
module_name, _, class_name = class_string.rpartition('.')
if module_name == '':
raise ValueError('Class name must contain module part.')
return getattr(
__import__(module_name, globals(), locals(), [class_name], -1),
class_name)
Il semble que vous vous en approchez du milieu au lieu du début. Qu'essayez-vous vraiment de faire? Trouver la classe associée à une chaîne donnée est un moyen de parvenir à une fin.
Si vous clarifiez votre problème, ce qui pourrait nécessiter votre propre refactoring mental, une meilleure solution pourrait se présenter.
Par exemple: essayez-vous de charger un objet enregistré en fonction de son nom de type et d'un ensemble de paramètres? Python épelle ce décapage et vous devriez regarder le module pickle . Et même si le processus de décapage fait exactement ce que vous décrivez, vous n'avez pas à vous soucier de la façon dont cela fonctionne en interne:
>>> class A(object):
... def __init__(self, v):
... self.v = v
... def __reduce__(self):
... return (self.__class__, (self.v,))
>>> a = A("example")
>>> import pickle
>>> b = pickle.loads(pickle.dumps(a))
>>> a.v, b.v
('example', 'example')
>>> a is b
False
Cela se trouve dans la bibliothèque standard python, comme unittest.TestLoader.loadTestsFromName. Malheureusement, la méthode continue à faire des activités supplémentaires liées aux tests, mais ce premier ha semble réutilisable. J'ai l'a modifié pour supprimer la fonctionnalité liée au test:
def get_object(name):
"""Retrieve a python object, given its dotted.name."""
parts = name.split('.')
parts_copy = parts[:]
while parts_copy:
try:
module = __import__('.'.join(parts_copy))
break
except ImportError:
del parts_copy[-1]
if not parts_copy: raise
parts = parts[1:]
obj = module
for part in parts:
parent, obj = obj, getattr(obj, part)
return obj