2013-08-02 10 views
7

Esiste una classe base Base e una sottoclasse Special.Convertire un oggetto BaseClass in un oggetto SubClass in modo idiomatico?

class Base(object): 
    def __init__(self, name): 
     self.name = name 
    def greet(self): 
     return 'Hello %s' % self.name 

class Special(Base): 
    def __init__(self, name): 
     super(Special, self).__init__(name) 
    def rhyme(self): 
     return 'Hi %s! How are you? Fine, thanks. What about you?' % self.name 

Come posso trasformare un'istanza di Base in un'istanza Special? Attualmente ho un classmethod definito su Special che appena reassignes __dict__:

class Special(Base): 
    ... 
    @classmethod 
    def from_base(cls, baseobj): 
     special = Special() 
     special.__dict__ = baseobj.__dict__ 
     return special 

È questo idiomatica? Se no, cosa sarebbe?

P.S. Uno scenario di esempio: la classe base è un'implementazione predefinita. In natura, probabilmente troverai oggetti della classe base. Ora in alcuni progetti, la classe base è stata sottoclasse e sono stati aggiunti metodi speciali alla sottoclasse. Ora per lo più lavori ancora con oggetti di classe base, ma di volta in volta ti consigliamo di "aggiornare" alla classe speciale, perché avrai bisogno di accedere ad alcuni metodi.

+0

Perché non è possibile creare un'istanza di applicare una forza di 'Special'? –

risposta

6

È possibile ottenere ciò definendo un costruttore alternativo e riassegnando l'attributo __class__ dell'istanza.

class Base(object): 
    def __init__(self, name): 
     self.name = name 

    def greet(self): 
     return 'Hello %s' % self.name 

    @classmethod 
    def alt_constructor(cls, *args, **kwargs): 
     obj = cls(*args, **kwargs) 
     obj.__class__ = Special 
     return obj 


class Special(Base): 
    def __init__(self, name): 
     super(Special, self).__init__(name) 

    def rhyme(self): 
     return 'Hi %s! How are you? Fine, thanks. What about you?' % self.name 


>>> s = Base.alt_constructor("test") 
>>> print s.rhyme() 
Hi test! How are you? Fine, thanks. What about you? 

EDIT:

spostato il costruttore Special-Base.

Se non è possibile modificare la classe Base, è possibile aggiungere un metodo di classe a Special che modificherà la classe di qualsiasi oggetto passato ad esso.

class Base(object): 
    def __init__(self, name): 
     self.name = name 

    def greet(self): 
     return 'Hello %s' % self.name 


class Special(Base): 
    def __init__(self, name): 
     super(Special, self).__init__(name) 

    def rhyme(self): 
     return 'Hi %s! How are you? Fine, thanks. What about you?' % self.name 

    @classmethod 
    def convert_to_special(cls, obj): 
     obj.__class__ = Special 

>>> b = Base("test") 
>>> print type(b) 
<class '__main__.Base'> 

>>> Special.convert_to_special(b) 
>>> print type(b) 
<class '__main__.Special'> 

Una soluzione più completa sarebbe quella di creare un mixin che può essere aggiunto a qualsiasi classe.

class ConverterMixin(object): 

    @classmethod 
    def convert_to_class(cls, obj): 
     obj.__class__ = cls 


class Special(ConverterMixin, Base): 
    def __init__(self, name): 
     super(Special, self).__init__(name) 

    def rhyme(self): 
     return 'Hi %s! How are you? Fine, thanks. What about you?' % self.name 

>>> b = Base("test") 
>>> print type(b) 
<class '__main__.Base'> 

>>> Special.convert_to_class(b) 
>>> print type(b) 
<class '__main__.Special'> 
+0

@miku Hai ragione, nella mia risposta originale ho aggiunto un costruttore alternativo a 'Base', cambiando il modo in cui la classe si è comportata. Ho aggiornato la mia risposta con quello che credo stiate cercando. – FastTurtle

+0

Grazie per il tuo aggiornamento. Usare '__class__' è un ordine di grandezza più veloce, il che non è sorprendente, poiché in non creerà un nuovo oggetto. – miku

1

in realtà è possibile, ma non penso che si dovrebbe. invece di typecasting python ha la tipizzazione anatra per risolvere questo tipo di situazione.

in ogni caso, ecco il codice:

>>> base = Base("I'm a base!") 
>>> hasattr(base, 'rhyme') 
False 
>>> base.__class__ = Special 
>>> hasattr(base, 'rhyme') 
True 
>>> base.rhyme() 
"Hi I'm a base!! How are you? Fine, thanks. What about you?"