【问题标题】:Python: Can I have a subclass return instances of its own type for operators defined in a superclass?Python:我可以让子类为超类中定义的运算符返回其自己类型的实例吗?
【发布时间】:2013-11-22 01:49:31
【问题描述】:

在 Python (2.7) 中,我想创建一个有理数类,它模仿 Fraction 类的行为(在模块分数中)但覆盖 __repr__ 方法以匹配 __str__ 的结果。最初的想法只是为了我自己的修补,只是为了让 IDLE 输出看起来更友好。但现在我更感兴趣的是理解潜在的继承/类型问题——我认为这可能是普遍的兴趣——而不是为这个特定的用例找到一个解决方法,这无疑是微不足道的。

问题是,我也想继承所有数值运算符(方法 __add__、__sub__ 等)的功能,但结果是我的子类的实例而不是 Fraction 的实例。这就是我想要的,但是却发生了这种情况:

class Q(Fraction):
    def __repr__(self):
        return str(self)

>>> Q(1,2)
1/2
>>> Q(1,2) + Q(1,3)
Fraction(5, 6)

这是因为 Fraction 中定义的运算符返回 Fraction 实例。当然,我可以单独覆盖所有这些魔术方法,调用父类进行数学运算,然后强制转换为我的类型,但我觉得应该有一种方法可以通用地处理这种重复的情况(即不写“def”20次)。

我也考虑过使用 __getattribute__ 来拦截方法调用,但这看起来很不优雅,非常脆弱,并且在比这个稍微复杂一点的情况下几乎肯定会失败。 (我知道 __getattr__ 是首选,但似乎不会捕获我感兴趣的方法调用,因为它们是在基类中定义的!)

鉴于我不是基类的作者,有没有比单独覆盖每个方法更好的方法?

【问题讨论】:

  • 不幸的是,您必须定义所有魔术方法。 (__geattribute__ 不起作用,它不会捕获对魔术方法的魔术调用。)但是,您可以使用here 描述的方法自动创建此类方法。基本上,您编写一个元类,它会自动定义调用基类魔术方法并将结果包装在派生类中的魔术方法。
  • 所以主要问题是您的基类Fraction 没有很好地为继承而设计。如果是这样,它会使用类似type(self)(...) 的东西来创建它返回的对象(或它显示的repr-strings)。不幸的是,这意味着它会让你的生活更加艰难。

标签: python inheritance


【解决方案1】:

这有点工作,但您可以包装它并创建一个委托人。实际上,我做了与您所做的类似的事情,以创建一个默认以十六进制打印的 int 。一个更好的例子来自我自己的一个子类 int 的类,以允许位切片读取(写入显然不起作用,因为 int 是不可变的,所以这个特定的代码并没有走远......)。示例代码可能很多,但它显示了如何使用它:

# I stole this decorator from another stackoverflow recipe :) 
def returnthisclassfrom(specials):
  specialnames = ['__%s__' % s for s in specials.split()]
  def wrapit(cls, method):
    return lambda *a: cls(method(*a))
  def dowrap(cls):
    for n in specialnames:
      method = getattr(cls, n)
      setattr(cls, n, wrapit(cls, method))
    return cls
  return dowrap

def int_getslice(self, i, j):
    # NON-pythonic, will return everything inclusive i.e. x[5:3] returns 3 bits, not 2.
    # Because that's what users normally expect.
    # If you're a purist, modify below.
    if i > 1000000 or j > 1000000:
        raise ValueError, 'BitSize limited to 1 million bits'
    lo = min(i,j)
    hi = max(i,j)
    mask = (1<<((hi-lo)+1))-1

    return (self>>lo) & mask

def int_getitem(self, i):
    # Safety limit
    if i > 1000000:
        raise ValueError, 'BitSize limited to 1 million bits'
    return (self>>i)&1

def int_iter(self):
    # since getitem makes it iterable, override
    raise AttributeError, 'int object is not iterable'

@returnthisclassfrom('abs add and div floordiv lshift mod mul neg or pow radd rand rdiv rdivmod rfloordiv rlshift rmod rmul ror rpow rrshift rshift rsub rxor rtruediv sub truediv xor trunc')
class BitSliceInt(long):
  __getslice__ = int_getslice
  __getitem__ = int_getitem
  __iter__ = int_iter

【讨论】:

  • 这段代码运行得很好,尽管完全公开,但它确实导致了至少一个运算符 (__rpow__) 的无限循环,这可能与分数模块处理反向运算符的复杂方式有关。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多