2009-05-07 15 views
16

Estoy tratando de subclase str, pero teniendo algunas dificultades debido a su inmutabilidad.Extendiendo el String incorporado de Python

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

Para métodos heredados que no requieren ninguna funcionalidad nueva, como derived.lower(), hay una manera simple, Pythonic para devolver un objeto de tipo DerivedClass (en lugar de str)? ¿O estoy atascado manualmente anulando cada str.method(), como hice con derived.upper()?

Editar:

#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

** Ver también: ** http://stackoverflow.com/questions/tagged/python+monkeypatching – dreftymac

+0

** Vea también: ** http://stackoverflow.com/questions/tagged/python+method-missing – dreftymac

Respuesta

5

Usted puede hacer esto por razones imperiosas __getattribute__ como sugiere ZR40, pero se necesita tener una función getAttribute volver exigible. El ejemplo a continuación debería darle lo que desea; que utiliza el envoltorio functools.partial para hacer la vida más fácil, aunque se puede aplicar sin parcial si te gusta:

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

Usted puede ser capaz de hacer esto reemplazando __getattribute__.

+0

¿No quiere decir str .__ getattribute__? Y DerivedClass .__ dict__ te diría qué nombres están en la clase derivada. –

+0

Esto parece lanzar un TypeError. print derived.lower() TypeError: el objeto 'DerivedClass' no se puede llamar – user102975

+0

Sí, creo que debería ser str .__ getattribute__. Sigue obteniendo el TypeError. – user102975

5

Ambos están cerca, pero la comprobación de cada uno no se extiende así a anular muchos métodos.

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) 

(mejoras sugeridas por jarret hardie en los comentarios.)

+0

¿Es necesario marcar self .__ dict __. Keys() para el nombre? La llamada a str .__ getattribute __ (self, name) parece llamar a los métodos como se esperaba (anulado o no) y 'call_me_later' devuelve instancias de la subclase si corresponde. Supongo que debe llamarse (func) para detectar cualquier intento de acceder a los miembros de datos. Modifiqué ligeramente las contribuciones y edité la pregunta. En aras de la simplicidad, dado que aún no estoy familiarizado con él, no se usa parcial. ¿Pensamientos? Gracias de nuevo :) – user102975

+0

@trigue - Ponga una declaración de impresión en __getattribute__. Verás que se llama siempre. – tghw

7

Buen uso de un decorador de clase - más o menos (código no probado):

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

y

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