Domanda

Ho la necessità di prendere un argomento stringa e creare un oggetto della classe denominata in quella stringa in Python. In Java, utilizzerei Class.forName (). NewInstance () . Esiste un equivalente in Python?


Grazie per le risposte. Per rispondere a coloro che vogliono sapere cosa sto facendo: voglio usare un argomento della riga di comando come nome della classe e istanziarlo. In realtà sto programmando in Jython e istanziando le classi Java, da qui la Java-ness della domanda. getattr () funziona alla grande. Grazie mille.

È stato utile?

Soluzione

La riflessione in Python è molto più semplice e molto più flessibile di quanto non sia in Java.

Consiglio di leggere questo tutorial

Non esiste alcuna funzione diretta (che io conosca) che prende un nome di classe completo e restituisce la classe, tuttavia hai tutti i pezzi necessari per costruirlo e puoi collegarli insieme.

Un piccolo consiglio però: non provare a programmare in stile Java quando sei in Python.

Se puoi spiegare cosa stai cercando di fare, forse possiamo aiutarti a trovare un modo più pitonico di farlo.

Ecco una funzione che fa quello che vuoi:

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

È possibile utilizzare il valore restituito di questa funzione come se fosse la classe stessa.

Ecco un esempio di utilizzo:

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

Come funziona?

Stiamo usando __import__ per importare il modulo che contiene la classe, il che ci ha richiesto di estrarre prima il nome del modulo dal nome completo. Quindi importiamo il modulo:

m = __import__( module )

In questo caso, m farà riferimento solo al modulo di livello superiore,

Ad esempio, se la tua classe vive nel modulo foo.baz , allora m sarà il modulo foo
Possiamo facilmente ottenere un riferimento a foo.baz usando getattr (m, 'baz')

Per passare dal modulo di livello superiore alla classe, utilizzare ricorsivamente gettatr nelle parti del nome della classe

Ad esempio, se il nome della tua classe è foo.baz.bar.Model , allora facciamo questo:

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

Questo è ciò che sta accadendo in questo ciclo:

for comp in parts[1:]:
    m = getattr(m, comp)    

Alla fine del ciclo, m sarà un riferimento alla classe. Ciò significa che m è in realtà la classe stessa, ad esempio puoi fare:

a = m() #instantiate a new instance of the class    
b = m( arg1, arg2 ) # pass arguments to the constructor

Altri suggerimenti

Supponendo che la classe sia nel tuo ambito:

globals()['classname'](args, to, constructor)

In caso contrario:

getattr(someModule, 'classname')(args, to, constructor)

Modifica: Nota, non puoi dare un nome come 'foo.bar' a getattr. Dovrai dividerlo per. e chiama getattr () su ogni pezzo da sinistra a destra. Questo gestirà ciò:

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

Uso

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: 

Ancora un'altra implementazione.

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)

Sembra che ti stai avvicinando a questo punto dal centro invece che dall'inizio. Cosa stai davvero cercando di fare? Trovare la classe associata a una determinata stringa è un mezzo per raggiungere un fine.

Se chiarisci il tuo problema, che potrebbe richiedere il tuo refactoring mentale, potrebbe presentarsi una soluzione migliore.

Ad esempio: stai tentando di caricare un oggetto salvato in base al nome del suo tipo e a una serie di parametri? Python scrive questo disfacimento e dovresti guardare il modulo pickle . E anche se il processo di decompressione fa esattamente quello che descrivi, non devi preoccuparti di come funziona internamente:

>>> 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

Questo si trova nella libreria standard di Python, come unittest.TestLoader.loadTestsFromName. Sfortunatamente il metodo continua a svolgere ulteriori attività correlate ai test, ma questo primo ha sembra riutilizzabile. L'ho modificato per rimuovere la funzionalità relativa al 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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top