2009-05-07 50 views
16

Tôi đang cố gắng phân lớp str, nhưng gặp một số khó khăn do tính bất biến của nó.Mở rộng nội dung của 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'> 

Đối với phương pháp di truyền mà không yêu cầu bất kỳ chức năng mới, chẳng hạn như derived.lower(), là có một cách đơn giản, pythonic để trả lại một đối tượng kiểu DerivedClass (thay vì str)? Hoặc tôi bị mắc kẹt bằng tay ghi đè mỗi str.method(), như tôi đã làm với 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

** Xem thêm: ** http://stackoverflow.com/questions/tagged/python+monkeypatching – dreftymac

+0

** Xem thêm: ** http://stackoverflow.com/questions/tagged/python+method-missing – dreftymac

Trả lời

5

Bạn có thể làm điều này bằng cách ghi đè __getattribute__ như Zr40 gợi ý, nhưng bạn sẽ cần phải có thuộc tính trả lại hàm gọi. Mẫu dưới đây sẽ cung cấp cho bạn những gì bạn muốn; nó sử dụng functools.partial wrapper để làm cho cuộc sống dễ dàng hơn, mặc dù bạn có thể thực hiện điều đó mà không một phần nếu bạn thích:

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

Bạn có thể có thể làm điều này bằng cách ghi đè __getattribute__.

+0

Ý bạn là str .__ getattribute__? Và DerivedClass .__ dict__ sẽ cho bạn biết tên nào nằm trong lớp dẫn xuất. –

+0

Điều này dường như ném một TypeError. print derived.lower() LoạiError: đối tượng 'DerivedClass' không được gọi – user102975

+0

Có, tôi tin rằng nó phải là str .__ getattribute__. Vẫn nhận được TypeError. – user102975

5

Bạn vừa gần gũi, nhưng kiểm tra cho mỗi không mở rộng tốt với trọng nhiều phương pháp.

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) 

(Cải tiến đề xuất bởi jarret hardie trong ý kiến.)

+0

Có cần phải tự kiểm tra .__ dict __. Keys() cho tên không? Cuộc gọi đến str .__ getattribute __ (tự, tên) dường như gọi các phương thức như mong đợi (ghi đè hoặc không) và 'call_me_later' trả về các cá thể của lớp con nếu thích hợp. Tôi giả định có thể gọi (func) là bắt bất kỳ nỗ lực nào để truy cập vào các thành viên dữ liệu. Tôi đã sửa đổi các đóng góp một chút và chỉnh sửa câu hỏi. Vì lợi ích của sự đơn giản, vì tôi chưa quen với nó, một phần không được sử dụng. Suy nghĩ? Cảm ơn một lần nữa :) – user102975

+0

@trigue - Đặt câu lệnh in trong __getattribute__. Bạn sẽ thấy rằng nó được gọi mỗi lần. – tghw

7

Sử dụng tốt cho một trang trí lớp - xấp xỉ (mã chưa được kiểm tra):

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

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))) 
Các vấn đề liên quan