2010-04-24 24 views
8

Ecco un esempio molto semplice di quello che sto cercando di muoversi:classe __init__ (non istanza __init__)

class Test(object): 
    some_dict = {Test: True} 

Il problema è che non riesco a fare riferimento alla prova mentre è ancora in fase di definizione

Normalmente, vorrei solo fare questo:

class Test(object): 
    some_dict = {} 
    def __init__(self): 
     if self.__class__.some_dict == {}: 
      self.__class__.some_dict = {Test: True} 

Ma non ho mai creato un'istanza di questa classe. In realtà è solo un contenitore per contenere un gruppo di funzioni e dati correlati (ho alcune di queste classi e faccio riferimento a loro, quindi è è necessario per Test per essere la sua classe)

Quindi il mio la domanda è, come potrei fare riferimento a Test mentre è in fase di definizione, o c'è qualcosa di simile a __init__ che viene chiamato non appena viene definita la classe? Se possibile, voglio che self.some_dict = {Test: True} rimanga all'interno della definizione della classe. Questo è l'unico modo che conosco come fare questo finora:

class Test(object): 
    @classmethod 
    def class_init(cls): 
     cls.some_dict = {Test: True} 
Test.class_init() 
+0

"Il problema è che non posso fare riferimento a Test mentre è ancora in fase di definizione". Così? Quale tipo di riferimento hai bisogno ** durante la definizione **? La risposta standard è None: la definizione della classe è stabile, coerente e invariante. ** Il comportamento ** può cambiare. La definizione non dovrebbe cambiare. Che cosa stai cercando di fare con questa attività "a definizione variabile"? –

+0

La definizione non sta cambiando. Ho solo bisogno di un riferimento alla classe e, per motivi organizzativi, preferirei che io impostassi quel riferimento quando la classe è in via di definizione. – Ponkadoodle

+0

Capisco cosa stai cercando di fare, ma non capisco perché stai cercando di farlo in questo modo e quello che stai cercando di realizzare. Puoi elaborare un po '? Sembra più di un architettonico poi un "come posso convincere Python a ...?" problema per me. – Aea

risposta

12

La classe in effetti non esiste mentre viene definita. Il modo in cui l'istruzione class funziona è che il corpo dell'istruzione viene eseguito, come blocco di codice, in uno spazio dei nomi separato. Alla fine dell'esecuzione, tale spazio dei nomi viene passato al metaclasse (come type) e il metaclasse crea la classe utilizzando lo spazio dei nomi come spazio attributi.

Dalla descrizione, è non suono necessario per Test per essere una classe. Sembra che dovrebbe essere un modulo . some_dict è un globale - anche se si tratta di un attributo di classe, c'è solo un attributo di questo tipo nel tuo programma, quindi non è migliore di avere un globale - e qualsiasi metodo di classe che hai nella classe può essere solo funzioni.

Se si vuole veramente che sia una classe, avete tre opzioni: impostare il dict dopo aver definito la classe:

class Test: 
    some_dict = {} 
Test.some_dict[Test] = True 

utilizzare una classe decoratrice (in Python 2.6 o successiva):

def set_some_dict(cls): 
    cls.some_dict[cls] = True 

@set_some_dict 
class Test: 
    some_dict = {} 

Oppure utilizzare un metaclasse:

class SomeDictSetterType(type): 
    def __init__(self, name, bases, attrs): 
     self.some_dict[self] = True 
     super(SomeDictSetterType, self).__init__(name, bases, attrs) 

class Test(object): 
    __metaclass__ = SomeDictSetterType 
    some_dict = {} 
+0

+1 per la risposta dettagliata –

+0

* potrebbe * essere un modulo, ma preferirei che fosse una classe. Se fosse in un modulo separato, dovrei fare un po 'di riorganizzazione per evitare importazioni circolari. Inoltre, avrei bisogno di avere 20 schede aperte durante la modifica del mio progetto se tutte quelle classi fossero in file diversi. Comunque, grazie per avermi parlato di Metaclasses. – Ponkadoodle

+0

il decoratore è un approccio interessante –

4

si potrebbe aggiungere l'attributo some_dict dopo la definizione della classe principale.

class Test(object): 
    pass 
Test.some_dict = {Test: True} 
+0

Questo è il modo per farlo. – PaulMcG

+0

concordato: questa è la risposta corretta. –

0

È possibile anche utilizzare un metaclasse (una funzione qui, ma ci sono altri modi) :

def Meta(name, bases, ns): 
    klass = type(name, bases, ns) 
    setattr(klass, 'some_dict', { klass: True }) 
    return klass 

class Test(object): 
    __metaclass__ = Meta 

print Test.some_dict 
0

Il primo esempio di Thomas è molto buono, ma ecco un modo più pitonico di fare la stessa cosa.

class Test: 
    x = {} 
    @classmethod 
    def init(cls): 
     # do whatever setup you need here 
     cls.x[cls] = True 
Test.init() 
1

Ho cercato di utilizzare le classi in questo modo, in passato, e diventa brutto abbastanza rapidamente (per esempio, tutti i metodi dovranno essere metodi di classe o di metodi statici, e probabilmente rendersi conto alla fine che vuoi definire determinati metodi speciali, per i quali dovrai iniziare ad usare metaclassi).Potrebbe rendere le cose molto più semplici se si usano solo istanze di classe - non ci sono davvero aspetti negativi.

A (strano-looking) alternativa a ciò che altri hanno suggerito: si potrebbe usare __new__:

class Test(object): 
    def __new__(cls): 
     cls.some_dict = {cls: True} 

Test() 

si potrebbe anche avere __new__ restituire un riferimento alla classe e utilizzare un decoratore di chiamarlo:

def instantiate(cls): 
    return cls() 

@instantiate 
class Test(object): 
    def __new__(cls): 
     cls.some_dict = {cls: True} 
     return cls 
Problemi correlati