2009-05-07 5 views
16

Sto provando a sottoclasse str, ma avendo qualche difficoltà a causa della sua immutabilità.Estendere il built-in di Python Str

class DerivedClass(str): 

    def __new__(cls, string): 
     ob = super(DerivedClass, cls).__new__(cls, string) 
     return ob 

    def upper(self): 
     #overridden, new functionality. Return ob of type DerivedClass. Great. 
     caps = super(DerivedClass, self).upper() 
     return DerivedClass(caps + '123') 

derived = DerivedClass('a') 

print derived.upper() #'A123' 
print type(derived.upper()) #<class '__main__.DerivedClass'> 
print derived.lower() #'a' 
print type(derived.lower()) #<type 'str'> 

Per i metodi ereditati che non richiedono alcuna nuova funzionalità, come derived.lower(), c'è un modo semplice divinatorio per restituire un oggetto di tipo DerivedClass (anziché str)? O sono bloccato manualmente ignorando ogni str.method(), come ho fatto con derived.upper()?

Edit:

#Any massive flaws in the following? 

class DerivedClass(str): 
    def __new__(cls, string): 
     ob = super(DerivedClass, cls).__new__(cls, string) 
     return ob 

    def upper(self): 
     caps = super(DerivedClass, self).upper() 
     return DerivedClass(caps + '123') 

    def __getattribute__(self, name): 
     att = super(DerivedClass, self).__getattribute__(name) 

     if not callable(att): 
      return att 

     def call_me_later(*args, **kwargs): 
      result = att(*args, **kwargs) 
      if isinstance(result, basestring): 
       return DerivedClass(result) 
      return result 
     return call_me_later 
+0

** Vedi anche: ** http://stackoverflow.com/questions/tagged/python+monkeypatching – dreftymac

+0

** Vedi anche: ** http://stackoverflow.com/questions/tagged/python+method-missing – dreftymac

risposta

5

È possibile farlo sovrascrivendo __getattribute__ come suggerisce ZR40, ma è necessario avere getattribute restituire una funzione callable. L'esempio qui sotto dovrebbe darti quello che vuoi; utilizza il functools.partial wrapper per rendere la vita più facile, anche se si potrebbe attuarla senza parziale se vi piace:

from functools import partial 

class DerivedClass(str): 

    def __new__(cls, string): 
     ob = super(DerivedClass, cls).__new__(cls, string) 
     return ob 

    def upper(self): 
     #overridden, new functionality. Return ob of type DerivedClass. Great. 
     caps = super(DerivedClass, self).upper() 
     return DerivedClass(caps + '123') 

    def __getattribute__(self, name): 
     func = str.__getattribute__(self, name) 
     if name == 'upper': 
      return func 

     if not callable(func): 
      return func 

     def call_me_later(*args, **kwargs): 
      result = func(*args, **kwargs) 
      # Some str functions return lists, ints, etc 
      if isinstance(result, basestring: 
       return DerivedClass(result) 
      return result 

     return partial(call_me_later) 
-2

Potreste essere in grado di fare questo sovrascrivendo __getattribute__.

+0

Non intendi str .__ getattribute__? E DerivedClass .__ dict__ ti direbbe quali nomi sono nella classe derivata. –

+0

Sembra lanciare un errore TypeError. print derived.lower() TypeError: l'oggetto 'DerivedClass' non è richiamabile – user102975

+0

Sì, credo che dovrebbe essere str .__ getattribute__. Ancora ottieni l'errore TypeError. – user102975

5

Siete entrambi vicino, ma il controllo per ogni non si estende ben al override molti metodi.

from functools import partial 

class DerivedClass(str): 
    def __new__(cls, string): 
     ob = super(DerivedClass, cls).__new__(cls, string) 
     return ob 

    def upper(self): 
     caps = super(DerivedClass, self).upper() 
     return DerivedClass(caps + '123') 

    def __getattribute__(self, name): 
     if name in ['__dict__', '__members__', '__methods__', '__class__']: 
      return object.__getattribute__(self, name) 
     func = str.__getattribute__(self, name) 
     if name in self.__dict__.keys() or not callable(func): 
      return func 

     def call_me_later(*args, **kwargs): 
      result = func(*args, **kwargs) 
      # Some str functions return lists, ints, etc 
      if isinstance(result, basestring): 
       return DerivedClass(result) 
      return result 

     return partial(call_me_later) 

(miglioramenti suggeriti dalla jarret hardie nei commenti.)

+0

È necessario controllare self .__ dict __. Keys() per nome? La chiamata a str .__ getattribute __ (self, name) sembra chiamare i metodi come previsto (sovrascrivendo o meno) e 'call_me_later' restituisce le istanze della sottoclasse, se appropriato. Presumo callable (func) è di catturare qualsiasi tentativo di accedere ai membri dei dati. Ho modificato leggermente i contributi e modificato la domanda. Per semplicità, dal momento che non lo conosco ancora, non viene usato il partial. Pensieri? Grazie ancora :) – user102975

+0

@trigue - Inserisci una dichiarazione di stampa in __getattribute__. Vedrai che viene chiamato ogni volta. – tghw

7

Buon uso per una classe decorator - grosso modo (codice non testato):

@do_overrides 
class Myst(str): 
    def upper(self): 
    ...&c... 

e

def do_overrides(cls): 
    done = set(dir(cls)) 
    base = cls.__bases__[0] 
    def wrap(f): 
    def wrapper(*a, **k): 
     r = f(*a, **k) 
     if isinstance(r, base): 
     r = cls(r) 
     return r 
    for m in dir(base): 
    if m in done or not callable(m): 
     continue 
    setattr(cls, m, wrap(getattr(base, m)))