【问题标题】:Extending Python's builtin Str扩展 Python 的内置 Str
【发布时间】:2010-10-24 13:01:40
【问题描述】:

我正在尝试继承 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)?还是像derived.upper() 那样手动覆盖每个str.method()?

编辑:

#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

【问题讨论】:

标签: python oop inheritance overriding immutability


【解决方案1】:

类装饰器的好用处——大致(未经测试的代码):

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

【讨论】:

    【解决方案2】:

    您可以按照 Zr40 的建议通过覆盖 __getattribute__ 来做到这一点,但您需要让 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)
    

    【讨论】:

      【解决方案3】:

      你们都很接近,但检查每个都不能很好地扩展到覆盖许多方法。

      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 在 cmets 中提出的改进建议。)

      【讨论】:

      • 是否有必要检查 self.__dict__.keys() 的名称?对 str.__getattribute__(self, name) 的调用似乎按预期调用方法(是否覆盖),如果合适,'call_me_later' 返回子类的实例。我假设 callable(func) 是为了捕捉任何访问数据成员的尝试。我稍微修改了贡献,并编辑了问题。为了简单起见,由于我还不熟悉它,因此不使用 partial。想法?再次感谢:)
      • @trigue - 在 getattribute 中添加打印语句。你会看到它每次都被调用。
      【解决方案4】:

      您可以通过覆盖 __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))
      

      【讨论】:

      • 你不是说 str.__getattribute__ 吗? DerivedClass.__dict__ 会告诉您派生类中有哪些名称。
      • 这似乎引发了 TypeError。 print derived.lower() TypeError: 'DerivedClass' object is not callable
      • 是的,我认为应该是 str.__getattribute__。仍然得到 TypeError。
      • 您收到 TypeError 是因为 getattribute 返回对属性的引用(在本例中为 lower 函数)......它不调用该函数。这个函数返回的是一个DerivedClass实例,而不是lower函数,由于DerivedClass没有实现call,所以不能调用它。
      猜你喜欢
      • 2013-08-26
      • 2011-09-09
      • 2010-09-26
      • 2011-10-08
      • 1970-01-01
      • 2015-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多