2009-05-07 11 views
16

Próbuję podklasę str, ale mam pewne trudności ze względu na jego niezmienność.Rozszerzanie wbudowanego w Pythona 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'> 

Dla odziedziczonych metod, które nie wymagają żadnych nowych funkcji, takich jak derived.lower(), to istnieje prosty, pythonic sposobem powrotu obiekt typu DerivedClass (zamiast str)? Czy utknąłem ręcznie przesłonięcie każdego str.method(), tak jak ja z 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

** Zobacz też: ** http://stackoverflow.com/questions/tagged/python+monkeypatching – dreftymac

+0

** Zobacz też: ** http://stackoverflow.com/questions/tagged/python+method-missing – dreftymac

Odpowiedz

5

Można to zrobić poprzez nadpisanie __getattribute__ jak sugeruje Zr40, ale trzeba mieć getAttribute zwrócić wpłacone funkcji. Poniższa próbka powinna dać ci to, czego chcesz; używa functools.partial opakowanie, by ułatwić życie, choć można wdrożyć go bez częściowy jeśli chcesz:

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

Możesz być w stanie to zrobić poprzez nadpisanie __getattribute__.

+0

Nie masz na myśli str .__ getattribute__? I DerivedClass .__ dict__ powie Ci, które nazwy należą do klasy pochodnej. –

+0

To wydaje się wywoływać TypeError. print derived.lower() TypeError: Obiekt "DerivedClass" nie jest wywoływalny – user102975

+0

Tak, wierzę, że powinien to być str .__ getattribute__. Nadal dostaję TypeError. – user102975

5

Jesteś zarówno blisko, ale sprawdzając każdy dobrze nie rozciągają się nadrzędnymi wiele sposobów.

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) 

(Poprawki zaproponowane przez jarret hardie w komentarzach.)

+0

Czy konieczne jest sprawdzenie self .__ dict __. Keys() dla nazwy? Wywołanie funkcji str .__ getattribute __ (self, name) wydaje się wywoływać metody zgodnie z oczekiwaniami (przesłonięte lub nie), a "call_me_later" zwraca instancje podklasy, jeśli jest to właściwe. Zakładam, że calllable (func) to przechwycenie wszelkich prób dostępu do elementów danych. Lekko zmodyfikowałem składki i zredagowałem pytanie. Ze względu na prostotę, ponieważ nie jestem jeszcze zaznajomiony z tym, częściowe nie jest używane. Myśli? Jeszcze raz dziękuję :) – user102975

+0

@trigue - Umieść instrukcję drukowania w __getattribute__. Zobaczysz, że jest wywoływana za każdym razem. – tghw

7

Dobre wykorzystanie do klasy dekoratora - z grubsza (kod niesprawdzone):

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

i

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