第2章 抽象数据类型和python类
2.1 抽象数据类型
ADT(Abstract Data Type)是一种思想,也是一种组织程序的技术,主要包括:
1、围绕着一类数据定义程序模块。
2、模块的接口和实现分离。
3、在需要实现时,选择一套合适的机制,采用合理的技术,实现这种ADT的功能,包括具体的数据表示和操作。
2.2 Python中的类
本节主要讲python利用class实现抽象数据类型。
class Rational_number():
def __init__(self,num,den=1):
self.num=num
self.den=den
def plus(self,another):
den=self.den*another.den
num=self.den*another.num+self.num*another.den
# 求最大公约数,保证可以被约到最简形式
max=self.greatest_common_divisor(num,den)
den=den/max
num=num/max
# int()确保输出的是整数
return Rational_number(int(num),int(den))
# 求两个数的最大公约数
def greatest_common_divisor(self,a,b):
if a
下面对这段代码做说明:
类定义的作用是支持创建抽象的数据类型,在建立这种抽象时人们不希望暴露其实现的内部细节。如对于有理数类,不希望暴露这种对象内部是用两个整数表示分子和分母。需要为此而隐藏一些信息。
在python中,以单下划线开头的属性名和函数名是内部使用的名字,在类外不能使用,另外,python对类定义里面以两个下划线开头(不能是结尾)的名字做了特殊处理,不能在类定义之外访问。(类的私有成员函数或者变量)
考虑上面代码中求最大公约数的方法,该函数并不依赖与有理数的对象,因此第一个参数无需使用self,即它不需要作为类的实例方法出现,我们将其定义为类的非实例方法(静态方法)。描述时需要在函数定义的头部行之前加上修饰符@staticmethod。静态方法就是类里面的普通函数,也是该类的局部函数,调用时用类名.方法名(参数)。
还有一个问题需要考虑,在有理数类的初始化方法中没有检查参数,首先要检查是否参数均为正数,然后分母是否为0,如果不满足,则抛出异常。而且分子分母可能为正或负,需要将符号加到公约化简后的分子上去。(实验发现,如果去掉了@staticmethod没有什么影响。)
class Rational_number():
def __init__(self,num,den=1):
if not isinstance(num,int) or not isinstance(den,int):
raise TypeError
if den==0:
raise ZeroDivisionError
sign=1
if num<0:
num,sign=-num,-sign
if den<0:
den,sign=-den,-sign
g=Rational_number._greatest_common_divisor(num,den)
# 在python3.0中,"/"表示浮点数除法,返回浮点结果;"//"表示整数除法,返回整数结果。
self._num=sign*(num//g)
self._den=den//g
# 求两个数的最大公约数
@staticmethod
def _greatest_common_divisor(a,b):
if b==0:
a,b=b,a
while a!=0:
a,b=b%a,a
return b
下面考虑类内其他的方法。由于两个属性_den和_num均被设置为内部属性,不应该在类外引用他们,但在实际计算中,我们有时需要提取分子和分母,为此需要有一对解析的操作(也是实例方法):
def get_num(self):
return self._num
def get_den(self):
return self._den
考虑有理数的运算,人们希望用(+,-,*,/)等运算符来描述计算过程,写出更加自然的表达式,在python中,为所有的算术运算符规定了特殊的方法名。+运算符对应了__add__,*运算符对应了__mul__,%运算符对应了__mod__,/运算符对应了__truediv__,-运算符对应了__sub__。
def __add__(self,another):
den=self._den*another._den
num=self._den*another._num+self._num*another._den
# 求最大公约数,保证可以被约到最简形式
max=Rational_number._greatest_common_divisor(num,den)
den=den//max
num=num//max
return Rational_number(num,den)
在调用时,只需要用a+b即可。
有理数经常要比较大小和不等,这些类进行比较时,也提供了特殊的方法名,如下面的定义:
# 定义相等
def __eq__(self, other):
return self._den*other._num==self._num*other._den
# 定义大于
def __gt__(self, other):
return self._den * other._num < self._num * other._den
# 定义小于
def __lt__(self, other):
return self._den * other._num > self._num * other._den
为了便于输出,人们经常在类定义里面定义一个将该类对象转换到字符串的方法,为了保证系统的str函数可以正常使用,这个方法要采用特殊的名字__str__。内置函数str将调用它。
下面是完整的程序,此时的有理数类以及和python系统内部的类型没有什么差别了,地位和用法相同,python标准库的一些类型就是如此定义的。
class Rational_number():
def __init__(self,num,den=1):
if not isinstance(num,int) or not isinstance(den,int):
raise TypeError
if den==0:
raise ZeroDivisionError
sign=1
if num<0:
num,sign=-num,-sign
if den<0:
den,sign=-den,-sign
g=Rational_number._greatest_common_divisor(num,den)
# 在python3.0中,"/"表示浮点数除法,返回浮点结果;"//"表示整数除法,返回整数结果。
self._num=sign*(num//g)
self._den=den//g
def __add__(self,another):
den=self._den*another._den
num=self._den*another._num+self._num*another._den
# 求最大公约数,保证可以被约到最简形式
max=Rational_number._greatest_common_divisor(num,den)
den=den//max
num=num//max
return Rational_number(num,den)
# 定义相等
def __eq__(self, other):
return self._den*other._num==self._num*other._den
# 定义大于
def __gt__(self, other):
return self._den * other._num < self._num * other._den
# 定义小于
def __lt__(self, other):
return self._den * other._num > self._num * other._den
# 求两个数的最大公约数
@staticmethod
def _greatest_common_divisor(a,b):
if b==0:
a,b=b,a
while a!=0:
a,b=b%a,a
return b
def get_num(self):
return self._num
def get_den(self):
return self._den
def __str__(self):
return str(self._num)+"/"+str(self._den)
def print_number(self):
# str(实数)可以将实数转化为字符串
print(self._num,"/",self._den)
a=Rational_number(3,4)
b=Rational_number(5,2)
(a+b).print_number()
print((a+b).__str__())
t=type(a)
print(t)
print(a>b)