5.1 使用ABC可调用对象来进行设计
在Python中有两种创建可调用对象的简单方式,如下所示。
- 使用def语句创建一个函数。
- 通过创建继承自collections.abc.Callable类的实例。
也可以将一个变量赋值为lambda表达式。一个lambda表达式是一个小的匿名函数,其中只包含了一个表达式语句。我们不倾向于将lambda表达式保存在变量中,因为当使用了一个类似函数的、并未使用def语句定义的可调用对象,这种做法就会带来困惑。
import collections.abc
class Power1( collections.abc.Callable ):
def call( self, x, n ):
p= 1
for i in range(n):
p *= x
return p
pow1= Power1()
以上的可调用对象包含了如下3个部分。
- 类继承自abc.Callable。
- 定义了call()方法。
- 创建了类的实例pow1()``。
算法的确不是很高效,我们稍后会优化这一点。
一个完整的类定义显然是不必要的。为了逐步完成优化,比起把一个函数重构为可调用对象,从一个可调用对象作为开始更容易入手一些。
像使用其他函数那样,现在可以使用刚刚定义的pow1()函数了。如下是如何在Python命令行中使用pow1()函数的示例。
>>> pow1( 2, 0 )
1
>>> pow1( 2, 1 )
2
>>> pow1( 2, 2 )
4
>>> pow1( 2, 10 )
1024
我们使用了各种各样的参数值来构造可调用对象。如果仅仅是为了创建abc.Callable子类的对象,这样做是不必要的。然而,这样做可以帮助调试。考虑如下的定义。
class Power2( collections.abc.Callable ):
def _call( self, x, n ):
p= 1
for i in range(n):
p *= x
return p
以上的类定义有一个错误并且不符合可调用基类的定义规则。
是否找到了这个错误?如果没有,在本章最后会揭晓。
当我们试图使用这个类创建实例时,会产生如下错误。
>>> pow2= Power2()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Power2 with abstract
methods call
也许无法一下子发现错误的根源,但是可以通过调试来把它找出来。如果没有继承自collections.abc.Callable基类,将非常难调试。
这里就是调试的难点,我们会跳过power3的代码。和power2一样,唯一不同的是在没有继承自collections.abc.Callable的前提下就开始了类定义:class power3。
当试图把power3作为一个类来使用时,由于此类并没有满足可调用的条件并且没有继承自abc.Callable,因此将会出现如下错误。
>>> pow3= Power3()
>>> pow3( 2, 5 )
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'Power3' object is not callable
关于power3的类定义哪里出现了问题,以上错误只提供了少量的信息。power2的错误信息对找到问题的根源更有用一些。
