写在前面
利用class定义(类定义)实现抽象数据类型.
本节从例子出发介绍class定义的使用、其结构和主要设施;下一节将进一步讨论python中基于class的编程技术,成为面向对象技术
.
python把内置对象都看作是类.
0.有理数类
class Rationa10:
def __init__(self, num, den=1):
# self.fname形式的写法表示本类实例对象的属性 fname是属性名
self.num = num #分子
self.den = den #分母
'''用于有理数相加'''
def plus(self, other):
# other传另一个实例对象进去
num = self.num * other.den + self.den * other.num
den = self.den * other.den
return Rationa10(num, den)
def print(self):
print(str(self.num) + '/' + str(self.den))
if __name__ == "__main__":
r1 = Rationa10(3, 5)
r2 = r1.plus(Rationa10(7, 15))
r2.print()
#输出结果:80/75
- 在类里面定义的函数叫做这个类的方法.在这里定义了三个方法,都是操作类实例对象的方法(
实例方法
),最明显的特征是函数的第一个参数是self
-
__init__()
是初始化方法,在创建实例化对象的时候会自动调用 -
r1.plus(Rationa10(7, 15))
r1为plus方法的调用对象,方法中的self将约束于该对象,实参表达式Rationa10(7, 15)
它将作为plus方法的第二个参数约束到形参another.简单来说,在这里,r1 = self ;Rationa10(7, 15) = another
1.类定义进阶
类定义的重要作用之一就是支持创建抽象的数据类型
我们将表示有理数类进行优化,它可以实现以下功能:
- 约去其分字分母的最大公约数进行有理数的化简及异常处理
- 有理数+ 、-、*、//、==、>、<
- print输出str类型的实例化对象
class Rationa10:
@staticmethod
def _gcd(m, n):
if n == 0:
m, n = n, m
while m != 0:
m, n = n % m, m
return n
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 = Rationa10._gcd(num, den)
self._num = sign * (num//g) #分子
self._den = den // g #分母
def num(self): return self._num
def den(self): return self._den
def __add__(self, another): #用于有理数相加 +
num = (self._num * another.den()
+ self._den * another.num())
den = self._den * another.den()
return Rationa10(num, den)
def __sub__(self, another): #用于有理数相加 -
num = (self._num * another.den()
+ self._den * another.num())
den = self._den * another.den
return Rationa10(num, den)
def __mul__(self, another): #用于有理数相乘 *
return Rationa10(self._num * another.num(),
self._den * another.den())
def __floordiv__(self, another): #用于有理数相除 //
if another.num == 0:
raise ZeroDivisionError
return Rationa10(self._num * another.den(),
self._den * another.num())
def __eq__(self, another): #比较运算符 ==
return self._num * another.den() == self._den * another.num()
def __lt__(self, another): #比较运算符 <
return self._num * another.den() < self._den * another.num()
def __gt__(self, another): #比较运算符 >
return self._num * another.den() > self._den * another.num()
def __str__(self): #把该类对象转换到字符串
return str(self._num) + '/' + str(self._den)
def print(self):
print(self._num, '/', self._den)
if __name__ == "__main__":
#初始化方法的默认参数保证用整数直接创建有理数
five = Rationa10(5)
print(five)
print(type(five))
x = Rationa10(3, 5)
#实例化对象x调用该类中的实例方法print()
x.print()
#由于有理数类定义了str转换函数,可以直接用标准函数print输出
print("Two thirds are", Rationa10(2, 3))
#使用类中定义的算术运算符和条件运算符
y = five + x * Rationa10(5, 17)
print(y)
if y < Rationa10(123, 11):
print("small")
结果输出如下:
5/1
<class '__main__.Rationa10'>
3 / 5
Two thirds are 2/3
88/17
small
代码解析:
-
'_'(一个下划线)
:人们约定,在一个类定义里,由下划线_开头的属性名(和函数名)都当作内部使用的名字,不应该在这个类之外使用.(虽然使用了也不会报错,但约定必定是约定!遵守!) -
'__'(两个下划线)
:python对类定义里以两个下划线开头(但不以两个下划线结尾)的名字做了特殊处理,使得在类定义之外不能直接用这个名字访问.一意孤行要访问的话会报错. -
@staticmethod
:静态方法的修饰符。对于静态方法,不应该有self参数,可以通过其所在类的名字出发通过圆点形式调用,也可以从该类的对象出发通过圆点的形式调用. -
异常处理
:有理数的分母不能为0;有理数的分子和分母都应该为整数;初始化的实参可能有正有负,这里采用分子的正负表示有理数的正负;进行两个有理数整除操作的时候,作为分母的那方有理数不能为0; -
__str__
:为了便于输出,在类里定义一个把该类的对象转换到字符串的方法 -
魔法方法
:/:__truediv__
;%:__mod__
;!=:__ne__
;<=:__le__
;>=:__ge__
2.python采用的ADT描述形式
为了更好的用python面向对象的技术和类结构定义各种数据类型,使用的ADT描述将模仿python类定义的形式,也认为ADT描述的是一个类型
ADT描述形式python
- ADT的基本创建函数将以self作为第一个参数,表示被创建的对象,其它参数表示为正确创建对象时候需要提供的其它信息
- 在ADT描述中的每个操作也都以self作为第一个参数,表示被操作的对象
- 定义二元运算时也采用同样的形式,其参数表将包括self和另一个同类型对象,操作返回的是运算生成的结果对象
- 写ADT定义时,有时还是采用写参数类型的形式,用于说明操作对具体参数的类型要求,在很多情况下,这样写可以省略一些文字说明