4.2 基类和多态
在这个部分中,我们来看看特别糟糕的多态实现。在Python编程实践中,有一些特殊的情况,参数值的检查应该被独立看待。
设计很好的多态通常会符合Liskov替换原则。基于这个原则,多态类之间可以互相替换而且每一个多态类都包含相同的属性。如果想查看更多这方面的信息,请参见http://en.wikipedia.org/wiki/Liskov_substitution_principle。
过度使用isinstance()来区分参数的类型在带来不必要的复杂性的同时会降低程序的效率。程序中持续在进行实例的比较,但是通常只有在软件维护阶段才会发现错误。比起在代码中使用冗长的类型检查,单元测试能够更有效地发现代码中的错误。
包含许多isinstance()方法的函数可以被认为是糟糕的多态实现的标志。相比于在类的外部处理类型相关的操作,通常更好的方式是扩展或者封装,让它们更合理地实现多态性并且将类型相关的处理封装起来。
isinstance()方法的一个很好的用处是用来创建诊断信息,使用在assert语句中是其中一种简单的用法。
assertisinstance( some_argument, collections.abc.Container ),
"{0!r} not a Container".format(some_argument)
当有问题发生时,这段代码会抛出一个AssertionError异常。这样做的优点是用很短的代码就达到了我们的目的。但是,这样的用法有两个缺点:assert语句有可能不会抛出异常,所以显式地抛出一个TypeError异常可能会更好一些。下面是一个更好的例子。
if not isinstance(some_argument, collections.abc.Container):
raiseTypeError( "{0!r} not a Container".format(some_argument)
)
上面的代码的优点是它显式地抛出了正确的异常,缺点就是代码太长了。
更具有Python风格的做法总结如下:
“请求原谅比请求许可更好。”
这句的意思是我们应该尽量减少直接测试参数(请求许可)来确定它们的类型是否正确。参数类型检查没有任何实际的好处。取而代之的是,我们应该合理地处理异常(请求原谅)。
如果使用了不正确的类型却仍然通过了单元测试这种情况不太可能发生,最好的方式是结合使用诊断信息和异常。
下面是我们经常会使用的策略。
try:
found = value in some_argument
exceptTypeError:
if not isinstance(some_argument, collections.abc.Container):
warnings.warn( "{0!r} not a Container".format(some_argument) )
raise
isinstance()方法假定some_argument是一个正确的collections.abc. Container实例,并将结果返回给in运算符。
在一些特殊情况中,例如某些人更改了程序,将一个错误的类作为some_argument参数,我们的程序会输出一条诊断信息,然后以抛出一个TypeError异常的方式将程序终止。
