0

Python 为数字类型提供了一组抽象基类。这些以 开头Number,其中Complex是一个子类,依此类推,一直到RealRationalIntegral。由于每个都是最后一个的子类,因此每个都支持序列中在它之前的类的特殊功能。例如,您可以编写(1).numerator以获取 Python integer 的分子,该分子是1使用整数文字创建的1,被视为有理数。

链接页面说明:当然,数字可能有更多的 ABC,如果排除了添加这些的可能性,这将是一个糟糕的层次结构。您可以在 Complex 和 Real 之间添加 MyFoo:

class MyFoo(Complex): ...
MyFoo.register(Real)

这具有添加复数的新子类的效果,这样类型的对象Real将作为新类的实例进行测试 - 因此在“中间”ComplexReal某种意义上添加了新类。然而,这并没有解决新类可能引入numerator其子类未提供的功能(例如由属性举例说明的功能)的可能性。

例如,假设您要添加一个类,其实例表示形式为a + b√2whereab是有理数的数字。您可能会在内部将这些数字表示为一对Fractions(fraction.Fraction来自 Python 标准库的实例)。显然,这类数是 的子类Real,我们希望将Rational其视为它的子类(因为每个有理数都是我们的新类型的数b == 0)。所以我们会这样做:

class FractionWithRoot2Part (Real): ...
FractionWithRoot2Part.register(Rational)

我们可能希望向新类添加属性(比如)返回数字ab. 这些属性可能被称为RationalPartCoefficientOfRoot2。然而,这很尴尬,因为现有的类型数量Rational将不具有这些属性。如果我们写(1).RationalPart,那么我们将得到一个AttributeError. 示范:

Python 3.3.1 (v3.3.1:d9893d13c628, Apr  6 2013, 20:25:12) [MSC v.1600 32 bit (In
tel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from abc import *
>>> class c1 (metaclass = ABCMeta):
...     def x (self): return 5
...
>>> class c2: pass
...
>>> c1.register(c2)
<class '__main__.c2'>
>>> a1 = c1()
>>> a2 = c2()
>>> a1.x()
5
>>> a2.x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'c2' object has no attribute 'x'

因此,我们并没有真正引入一个新的类型,它“介于”现有的两个类型之间,因为子类关系的“底部”类型不支持“中间”类的行为。

解决这个问题的普遍接受的方法是什么?一种可能性是提供一个函数(不是任何类的方法),它可以处理任何类型的输入并智能地行动;像这样的东西:

def RationalPart (number):
    if isinstance(number, FractionWithRoot2Part):
        try:
            return number.RationalPart
        except AttributeError:
            # number is presumably of type Rational
            return number
    else:
        raise TypeError('This is not supported you dummy!')

还有比这更好的方法吗?

4

1 回答 1

0

您不能使用 ABC 以这种方式修改现有类(事实上,您根本无法安全地以这种方式修改现有类)。ABC 只是一种用于自定义一个类是否作为另一个的子类进行测试(并且它的实例作为另一个的实例进行测试)的机制,而不是用于实际更改子类实现的机制。当文档谈到在“中间”定义一个新类时,这就是它的意思;它只是在子类/实例检查方面介于两者之间,而不是实际继承。这在这里描述:

ABC 引入了虚拟子类,这些类不继承自一个类但仍被isinstance()和识别issubclass()

请注意它所说的:虚拟子类实际上并不从您的 ABC 继承,它们只是像这样做一样进行测试。这就是 ABC 的设计方式。这里建议使用它们的方法:

ABC 可以直接子类化,然后充当混合类。

因此,您不能使用 ABC 修改现有的 Rational 类。做你想做的事情的方法是创建一个继承自 Rational 并使用你的 ABC 作为 mixin 的新类。然后使用那个类而不是常规的 Rational。

事实上,你甚至可能不需要在这里使用 ABC。使用 ABC 的唯一优点是,如果有人明确测试,它会使您的新的类似有理数的数字看起来像有理数;但是只要您从 Rational 和添加您想要的行为的新类继承,新类无论如何都会Rational 一样工作。

当你说

然而,这很尴尬,因为现有数量的 Rational 类型将不具有这些属性。

你已经瞄准了情况的本质。这可能看起来很尴尬,但如果有人可以进来,并以 ABC 结束运行,通过在继承层次结构中在现有类上方粘贴一个新超类来开始修改现有类的行为,那也将非常尴尬。这不是它的工作原理。没有向任何类的现有实例添加新行为的安全方法;唯一安全的事情是向新类添加新行为,并告诉人们使用新类而不是旧类。

于 2014-03-29T22:47:50.727 回答