扩展Python的内置Str

时间:2009-05-07 15:50:30

标签: python oop inheritance override immutability

我正在尝试继承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'>  

对于不需要任何新功能的继承方法,例如derived.lower(),有一种简单的pythonic方法可以返回DerivedClass类型的对象(而不是str) ?或者我手动覆盖每个str.method(),就像我对derived.upper()所做的一样?

编辑:

#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

4 个答案:

答案 0 :(得分:7)

很好用于类装饰器 - 大致(未经测试的代码):

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

答案 1 :(得分:5)

你可以通过覆盖__getattribute__来做到这一点,正如Zr40建议的那样,但你需要让getattribute返回一个可调用的函数。下面的示例应该给你你想要的东西;它使用functools.partial包装器来使生活更轻松,但如果您愿意,可以不加局部地实现它:

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 :(得分:5)

你们两个都很接近,但是检查每一个并不能很好地覆盖很多方法。

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)

(评论中jarret hardie建议的改进。)

答案 3 :(得分:-2)

您可以通过覆盖__getattribute__来完成此操作。

def __getattribute__(self, name):
    # Simple hardcoded check for upper.
    # I'm sure there are better ways to get the list of defined methods in
    # your class and see if name is contained in it.
    if name == 'upper':
        return object.__getattribute__(self, name)

    return DerivedClass(object.__getattribute__(self, name))