2011-10-06 15 views
10

Voglio creare dinamicamente le classi in runtime in python.Qual è il vantaggio nell'usare `exec` su` type() `durante la creazione delle classi in fase di runtime?

Ad esempio, voglio replicare il codice qui sotto

>>> class RefObj(object): 
...  def __init__(self, ParentClassName): 
...   print "Created RefObj with ties to %s" % ParentClassName 
... class Foo1(object): 
...  ref_obj = RefObj("Foo1") 
... class Foo2(object): 
...  ref_obj = RefObj("Foo2") 
... 
Created RefObj with ties to Foo1 
Created RefObj with ties to Foo2 
>>> 

... ma voglio classi foo1, foo2, Foo da creare dinamicamente (es: durante l'esecuzione anziché sul primo passaggio compilare).

Un modo per raggiungere questo obiettivo è con type(), in questo modo:

>>> class RefObj(object): 
...  def __init__(self, ParentClassName): 
...   print "Created RefObj with ties to %s" % ParentClassName 
... def make_foo_class(index): 
...  name = "Foo%s" % index 
...  return type(name, (object,), dict(ref_obj = RefObj(name))) 
... 
>>> Foo1 = make_foo_class(1) 
Created RefObj with ties to Foo1 
>>> Foo2 = make_foo_class(2) 
Created RefObj with ties to Foo2 
>>> type(Foo1()), type(Foo2()) 
(<class 'Foo1'>, <class 'Foo2'>) 

posso anche raggiungere con exec, in questo modo:

>>> class RefObj(object): 
...  def __init__(self, ParentClassName): 
...   print "Created RefObj with ties to %s" % ParentClassName 
... def make_foo_object(index): 
...  class_template = """class Foo%(index)d(object): 
...   ref_obj = RefObj("Foo%(index)d") 
...   """ % dict(index = index) 
...  global RefObj 
...  namespace = dict(RefObj = RefObj) 
...  exec class_template in namespace 
...  return namespace["Foo%d" % index] 
... 
>>> Foo1 = make_foo_object(1) 
Created RefObj with ties to Foo1 
>>> Foo2 = make_foo_object(2) 
Created RefObj with ties to Foo2 
>>> type(Foo1()), type(Foo2()) 
(<class 'Foo1'>, <class 'Foo2'>) 

L'uso di exec non sta bene con me (come mi aspetto non con molte persone che leggono questa domanda) ma exec è esattamente come la classe collections.namedtuple() di python is implemented (vedi this line). Molto rilevante anche la difesa di questo uso di exechere, dal creatore della classe (Raymond Hettinger). In questa difesa, si afferma che "È una caratteristica chiave per le tuple denominate che sono esattamente equivalenti a una classe scritta a mano", che si potrebbe implicare che l'uso di type() non è buono come utilizzare exec ...

C'è differenza? Perché utilizzare exec rispetto a type()?

mi aspetto la risposta potrebbe essere che in entrambe le direzioni sono le stesse ed è semplicemente che l'attuazione namedtuple ha un sacco di variabili namedtuple pepati attraverso di essa, e facendo questo con la generazione dinamica di chiusure per tutti i metodi di fatto il codice di ottenere ingombrante , ma voglio sapere se c'è qualcosa di più in questo.

Per quanto riguarda il mio disagio con exec, riconosco che se non vi è alcun modo per le parti non fidate di iniettare codice nefasto in esso, dovrebbe andare bene ... è solo garantendo che questo mi rende nervoso.

+1

C'è un po 'di buona discussione sul problema a http://blog.ccpgames.com/kristjan/2011/05/28/namedtuple-and-exec/ pure, e c'era un altro post di blog su di esso Non vedo ora. Non hai presentato alcuna ragione per cui 'type' è problematico nella tua situazione, quindi non so perché ti preoccuperai di' exec' perché sai che 'type' funziona. (Oltre alla curiosità ovviamente). – agf

+0

@agf - ottimo collegamento, grazie! Inizialmente non avevo un problema con 'type', dal momento che entrambi gli approcci funzionano. Ero solo curioso riguardo le differenze e cercavo di capire il motivo per cui "exec" veniva usato in "namedtuple". Gli argomenti di firma di classe/funzione presentati sono eccellenti, sebbene ... Ho spesso problemi di decorazione che richiedono l'uso del pacchetto [decoratore] (http://pypi.python.org/pypi/decorator). – Russ

+0

Potresti dare una spiegazione alla necessità di creare classi dinamiche o è solo curiosità? – dhill

risposta

2

Non c'è alcuno svantaggio nell'usare type() su exec. Penso che la difesa di Raymond sia un po 'difensiva. Devi scegliere la tecnica che trovi più leggibile e comprensibile. Entrambi i modi creano codice confuso.

Dovresti provare davvero a evitare il codice che crea le classi in primo luogo, sarebbe meglio.

+0

@agfL scusa, non sono sicuro di cosa ti stai riferendo. –

+3

Uno degli argomenti per 'exec' per la libreria' decorator' era che era più semplice conservare la firma di funzioni/metodi decorati. Penso che fosse anche un argomento per 'namedtuple' che con' exec' le classi erano esattamente le stesse una volta costruite come sarebbero se fossero codificate a mano, mentre con 'type' erano diverse. – agf

+0

Vorrei assolutamente evitare di creare classi al volo, ma ho un caso in cui ho bisogno di creare attributi di classe basati su parametri che non posso garantire di essere in ambito prima della creazione della classe. Le classi dinamiche sembrano il modo più semplice al momento. – Russ

4

Perché non creare semplicemente una classe in una funzione?

def foo_factory(index): 
    name = 'Foo%d' % index 

    class Foo(object): 
     ref_obj = RefObj(name) 

    Foo.__name__ = name 
    return Foo 
+0

Quando c'è effettivamente qualcosa _dynamic_ su 'Foo' - non vuoi crearlo praticamente sempre lo stesso. – agf

+0

Naturalmente, questo è solo uno scheletro. – yak

+0

Mostrare come implementare 'namedtuple' o decoratori che conservano la firma in questo modo. Dire che "in nessun modo meno flessibile" non è vero. – agf

7

Si consiglia di type su exec qui.

Infatti, la dichiarazione class è solo zucchero sintattico per una chiamata a type: Il corpo della classe viene eseguita all'interno del proprio spazio dei nomi, che viene poi trasferito al metaclasse, che per impostazione predefinita type se non viene specificato metaclasse personalizzato.

Questo approccio è meno errorprone poiché non è necessario analizzare il codice in fase di esecuzione e potrebbe anche essere un po 'più veloce.

+0

+1 per praticità. Non merita il -1. – agf

Problemi correlati