在Python类中有些方法名、属性名的前后都添加了双下划线,这种方法、属性通常都属于Python的特殊方法和特殊属性,可以通过重写这些方法或直接调用这些方法来实现特殊的功能。
一、常见的特殊方法
(一)重写__repr__方法
__repr__()是Python类中的一个特殊方法,由于object类已提供了该方法,而所有的Python类都是object类的子类,因此所有的Python对象都具有__repr__()方法。
__repr__()是一个非常特殊的方法,它是一个“自我描述”的方法,该方法通常用于实现这样一个功能:当程序直接打印该对象时,系统将会输出该对象的“自我描述”信息。
object类提供的__repr__()方法总是返回该对象实现类的“类名+object at+内存地址"值。
class Apple:
# 实现构造器
def __init__(self, color, weight):
self.color = color
self.weight = weight
# 重写__repr__()方法,用于实现Apple对象的自我描述
def __repr__(self):
return "Apple[color=" + self.color +\
",weight=" + str(self.weight) + "]"
a = Apple('红色', 5.68)
# 打印Apple对象
print(a) # Apple[color=红色,weight=5.68]
(二)析构方法:__del__
与__init__()方法对应的是__del__()方法,该方法用于销毁Python对象,在任何Python对象将要被系统回收之时,系统都会自动调用该对象的__del__()方法。
当程序不再需要一个Python对象时,系统必须把该对象所占用的内存空间释放出来,这个过程被称为垃圾回收(GC, Garbage Collector)。
当一个对象被垃圾回收时,Python就会自动调用对象的__del__方法。
如果父类提供了__del__()方法,则系统重写__del__()方法时,必须显示调用父类的__del__()方法,这样才能保证合理地回收父类实例的部分属性。
(三)__dir__方法
对象的__dir__()方法用于列出该对象内部的所有属性(包括方法)名,该方法将会返回包含所有属性(方法)名的序列。
当程序对某个对象执行dir(object)函数时,实际上就是将该对象的__dir__()方法返回值进行排序,然后包装成列表。
(四)__dict__属性
__dict__属性用于查看对象内部存储的所有属性名和属性值组成的字典,通常程序直接使用该属性即可。程序使用__dict__属性即可查看对象的所有内部状态,也可通过字典语法来访问或修改指定属性的值。
class Item:
def __init__(self, name, price):
self.name = name
self.price = price
im = Item('鼠标', 28.9)
print(im.__dict__)
# 通过__dict__访问name属性
print(im.__dict__['name']) # 鼠标
# 通过__dict__访问price属性
print(im.__dict__['price']) # 28.9
im.__dict__['name'] = '键盘'
im.__dict__['price'] = 32.8
print(im.name) # 键盘
print(im.price) # 32.8
(五)__getattr__、__setattr__等
当程序操作(包括访问、设置、删除)对象的属性时,Python系统同样会执行该对象特定的方法。这些方法共涉及如下几个:
● __getattribute__(self,name):当程序访问对象的name属性时被自动调用。
● __getattr__(self, name):当程序访问对象的name属性切该属性不存在时被自动调用。
● __setattr__(self, name, value):当程序对对象的name属性赋值时被自动调用。
● __delattr__(self, name):当程序删除对象的name属性时被自动调用。
通过重写上面的方法,可以为Python类“合成”属性——当属性不存在时,程序会委托给上面的__getattr__、__setattr__、__delattr__方法来实现。
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def __setattr__(self, name, value):
print('----设置%s属性----' % name)
if name == 'size':
self.width, self.height = value
else:
self.__dict__[name] = value
def __getattr__(self, name):
print('----读取%s属性----' % name)
if name == 'size':
return self.width, self.height
else:
return AttributeError
def __delattr__(self, name):
print('----删除%s属性----' % name)
if name == 'size':
self.__dict__['width'] = 0
self.__dict__['height'] = 0
rect = Rectangle(3, 4)
print(rect.size)
rect.size = 6, 8
print(rect.width)
del rect.size
print(rect.size)
运行结果:
----设置width属性----
----设置height属性----
----读取size属性----
(3, 4)
----设置size属性----
----读取value属性----
Traceback (most recent call last):
File "g:/code/python/crazy_python/chapter8/rectange.py", line 29, in <module>
rect.size = (6, 8)
File "g:/code/python/crazy_python/chapter8/rectange.py", line 9, in __setattr__
self.width, self.height = self.value
TypeError: cannot unpack non-iterable type object
PS G:\code\python\crazy_python\chapter8> & D:/ProgramData/Anaconda3/python.exe g:/code/python/crazy_python/chapter8/rectange.py
----设置width属性----
----设置height属性----
----读取size属性----
(3, 4)
----设置size属性----
----设置width属性----
----设置height属性----
6
----删除size属性----
----读取size属性----
(0, 0)
对于__getattr__()方法:它只处理程序访问指定属性且该属性不存在的情形。比如程序访问width或height属性,Rectangle对象本身包含该属性,因此该方法不会被触发。所以重写该方法只需处理我们需要“合成”的属性(如size),假如程序试图访问其他不存在的属性,当然直接引发AttributeError异常即可。
对于__setattr__()方法:只要程序试图对指定属性赋值时总会触发该方法,因此无论程序是对width、height属性赋值,还是对size属性赋值,该方法都会被触发。所以重写该方法即要处理对size属性赋值的情形,也要对width、height属性赋值的情形。尤其是处理对width、height属性赋值的时候,千万不要在__setattr__()方法中再次对width、height赋值,因为对这两个属性赋值会再次触发__setattr__()方法,这样会让程序陷入死循环中。
如果程序需要在读取、设置属性之前进行某种拦截处理(比如检查数据是否合法之类的),也可以通过重写__setattr__()或__getattribute__()方法来实现。
二、与反射相关的属性和方法
(一)动态操作属性
如果程序在运行过程中要动态判断是否包含某个属性(包括方法),设置要动态设置某个属性值,则可通过Python的反射支持来实现。
在动态检查对象是否包含某些属性(包括方法)相关的函数有如下几个:
● hasattr(obj, name):检查obj对象是否包含名为name的属性或方法
● getattr(object, name[, default]):获取object对象中名为name的属性的属性值。
● stattr(obj, name, value, /):将obj对象的name属性设为value。
class Comment:
def __init__(self, detail, view_times):
self.detail = detail
self.view_times = view_times
def info(self):
print('一条简单的评论,内容是%s' % self.detail)
c = Comment('疯狂Python讲义很不错', 20)
# 判断是否包含指定的属性或方法
print(hasattr(c, 'detail')) # True
print(hasattr(c, 'view_times')) # True
print(hasattr(c, 'info')) # True
# 获取指定属性的属性值
print(getattr(c, 'detail')) # '疯狂Python讲义很不错'
print(getattr(c, 'view_times')) # 20
# 由于info是方法,故下面代码会提示:name 'info' is not defined
# print(getattr(c, info, '默认值'))
# 为指定属性设置属性值
setattr(c, 'detail', '天气不错')
setattr(c, 'view_times', 32)
# 输出重新设置后的属性值
print(c.detail)
print(c.view_times)
(二)__call__属性
程序可通过判断该属性(或方法)是否包含__call__属性来确定它是否可调用。
class User:
def __init__(self, name, passwd):
self.name = name
self.passwd = passwd
def validLogin(self):
print('验证%s登陆' % self.name)
u = User('crazyit', 'leegang')
# 判断u.name是否包含__call__方法
print(hasattr(u.name, '__call__')) # False
# 判断u.passwd是否包含__call__方法
print(hasattr(u.passwd, '__call__')) # False
# 判断u.validLogin是否包含__call__方法
print(hasattr(u.validLogin, '__call__')) # True
实际上,一个函数(甚至对象)之所以能执行,关键就在于__call__()方法。实际上x(arg1, arg2,...)只是x.__call__(arg1, arg2,...)的快捷写法,因此我们可以为自定义类添加__call__方法,从而使得该类实例也变成可调用的。
# 定义Role类
class Role:
def __init__(self, name):
self.name = name
# 定义__call__方法
def __call__(self):
print('执行Role对象')
r = Role('管理员')
# 直接调用Role对象,就是调用该对象的__call__方法
r()
三、与序列相关的特殊方法
python的序列可包含多个元素,只要实现符合序列要求的特殊方法,就可以实现自己的序列。
(一)序列相关方法
● __len__(self):该方法的返回值决定序列中元素的个数。
● __getitem__(self, key):该方法获取指定索引对应的元素。该方法的key应该是整数值或slice对象,否则该方法会引发KeyError异常。
● __contains__(self, item):该方法判断序列是否包含指定元素。
● __setitem__(self, key, value):该方法设置指定索引对应的元素。该方法的key应该是整数值或slice对象,否则该方法会一发KeyError异常。
● __delitem__(self, key):该方法删除指定索引对应的元素。
如果程序要实现不可变序列(程序只能获取序列中的元素,不能修改),只要实现上面前3个方法就行;如果程序要实现可变序列(程序既能获取序列中的元素,也可修改),则需要实现上面5个方法。
(二)实现迭代器
如果开发者需要实现迭代器,只要实现如下两个方法即可。
● __iter__(self):该方法返回一个迭代器(iterator),迭代器必须包含一个__next__()方法,该方法返回迭代器的下一个元素。
● __reversed__(self):该方法主要为内建的reversed()反转函数提供支持,当程序调用reversed()函数对指定迭代器执行反转时,实际上由该方法实现。
# 定义一个代表斐波那锲数列的迭代器
class Fibs:
def __init__(self, len):
self.first = 0
self.sec = 1
self.__len = len
# 定义迭代器所需的__next__方法
def __next__(self):
# 如果__len属性为0,结束迭代
if self.__len == 0:
raise StopIteration
# 完成数列计算
self.first, self.sec = self.sec, self.first + self.sec
# 数列长度减1
self.__len -= 1
return self.first
# 定义__iter__方法,该方法范虎迭代器
def __iter__(self):
return self
# 创建Fibs对象
fibs = Fibs(10)
# 获取迭代器的下一个元素
print(next(fibs))
# 使用for循环遍历迭代器
for el in fibs:
print(el, end=' ')
程序可使用内置的iter()函数将列表、元组等转换成迭代器。
# 定义ValueDict类,继承dict类
class ValueDict(dict):
# 定义构造函数
def __init__(self, *args, **kwargs):
# 调用父类构造函数
super().__init__(*args, **kwargs)
# 新增getkeys方法
def getkeys(self, val):
result = []
for key, value in self.items():
if value == val:
result.append(key)
return result
my_dict = ValueDict(语文=92, 数学=89, 英语=92)
# 获取92对应的所有key
print(my_dict.getkeys(92)) # 语文 英语
my_dict['编程'] = 92
print(my_dict.getkeys(92)) # 语文 英语 编程
四、生成器
生成器和迭代器的功能非常相似,它也会提供__next__()方法,所以程序同样可以调用内置的next()函数来获取生成器的下一个值,也可以使用for循环来遍历生成器。
生成器与迭代器的区别在于:迭代器通常是先定义一个迭代器类,然后通过创建实例来创建迭代器;而生成器则是先定义一个包含yield语句的函数,然后通过调用该函数来创建生成器。
创建生成器需要两步操作:
● 定义一个包含yield语句的函数
● 调用函数得到生成器。
def test(val, step):
print('--------函数开始执行--------')
cur = 0
# 遍历0 ~ val
for i in range(val):
# cur添加i*step
cur += i * step
yield cur
yield cur语句的作用:
● 每次返回一个值,有点类似于return语句
● 冻结执行,程序每次执行到yield语句时就会被暂停
在程序被yield语句冻结之后,当程序再次调用next()函数获取生成器下一个值时,程序才会继续向下执行。
※Python2.x不使用next()函数来获取生成器的下一个值,而是直接调用生成器的next()方法。
程序可使用for循环来遍历生成器,相当于不断地使用next()函数获取生成器的下一个值。
程序可使用list()或tuple()函数来将生成器的所有值转换成列表或元组。
Python主要提供了一下两种方式来创建生成器:
● 使用for循环的生成器推导式。
● 调用带yield语句的生成器函数。
生成器的优势:
● 当使用生成器来生成多个数据时,程序是按需获取数据的,它不会一开始就把所有数据都生成出来,而是每次调用next()获取下一个数据时,生成器才会执行一次,因此可以减少代码的执行次数。
● 当函数需要返回多个数据时,如果不使用生成器,程序就需要使用列表或元组来收集函数返回的多个值,当函数要返回的数据量较大时,这些列表、元组会带来一定的内存开销。
● 使用生成器的代码更加简洁。
为了实现生成器与“外部程序”动态地交换数据,需要借助于生成器的send()方法,该方法的功能与next()函数的功能非常相似,他们都用于获取生成器所生成的下一个值,并将生成器“冻结”在yield语句处:但send()方法可以接收一个参数,该采纳数值会被发送给生成器函数。
● 外部程序通过send()方法发送数据。
● 生成器函数使用yield语句接收数据。
※只有等到生成器被“冻结”之后,外部程序才能使用send()方法向生成器发送数据。
获取生成器第一次所生成的值,应该是用next()函数;如果非要使用send()方法获取生成器第一次所产生的值,也不能向生成器发送数据,只能为该方法传入None参数。
def square_gen(val):
i = 0
out_val = None
while True:
# 使用yield语句生成值,使用out_val接收send方法发送的值
out_val = (yield out_val ** 2) if out_val is not None else (yield i ** 2)
# 如果程序使用sned()方法获取下一个值,out_val会获取send()方法的参数值
if out_val is not None:
i += 1
sg = square_gen(5)
# 第一次调用sned()方法获取值,只能传入None
print(sg.send(None))
print(next(sg))
print('-------------------')
# 调用send()方法获取生成器的下一个值。
print(sg.send(9))
# 再次调用next()函数获取生成器的下一个值。
print(next(sg))
生成器提供了一下两个常用方法:
● close():该方法用于停止生成器
● throw():该方法用于在生成器内部(yield语句内)引发一个异常。
五、运算符重载的特殊方法
与数值运算相关的运算符包括算术运算符、位运算符等,开发人员可以为自定义类共如下方法来为对应的运算符进行重载:
● object.__add__(self, other):加法运算,为“+”运算符提供支持。
● object.__sub__(self, other):减法运算,为“-”运算符提供支持。
● object.__mul__(self, other):乘法运算,为“*”运算符提供支持。
● object.__matmul__(self, other):矩阵乘法,为"@"运算符提供支持。
● object.__truediv__(self, other):除法运算,为"/"运算符提供支持。
● object.__floordiv__(self, other):整除运算,为"//"运算符提供支持。
● object.__mod__(self, other):求余运算,为“%”运算符提供支持。
● object.__divmod__(self, other):求余运算,为divmod运算符提供支持。
● object.__pow__(self, other):乘方运算,为“**”运算符提供支持。
● object.__lshift__(self, other):左移运算,为"<<"运算符提供支持。
● object.__rshift__(self, other):右移运算,为“>>”运算符提供支持。
● object.__and__(self, other):按位与运算,为“&”运算符提供支持。
● object.__xor__(self, other):按位异或运算,为“^”运算符提供支持。
● object.__or__(self, other):按位或运算,为"|"运算符提供支持。
以上方法还有一个带r的版本,如__radd__()方法,Python会首先尝试使用x的__add__方法进行计算,如果x没有提供__add__方法,Python还会尝试调用y的__radd__方法进行计算。简单的说,如果自定义类提供了__rxxx__方法,那么该自定义类的对象就可以出现在对应运算符的右边。
此外Python还支持各种扩展后的赋值运算符,这些扩展后的赋值运算符也是由特殊方法来提供支持的。形如__ixxx__的方法提供+=、-=等类似的支持。
● object.__lt__(self, other):为"<"运算符提供支持。
● object.__le__(self, other):为"<="运算符提供支持。
● object.__eq__(self, other):为"=="运算符提供支持。
● object.__ne__(self, other):为"!="运算符提供支持。
● object__gt__(self, other):为">"运算符提供支持。
● object.__ge__(self, other):为">="运算符提供支持。
虽然Python为每个比较运算符都提供了特殊方法,但实际上往往并不需要实现这么多的特殊方法,对于同一个类的实例比较大小而言,通常只要实现其中三个方法即可。因为在实现__gt__()方法之后,程序即可使用“>”和“<”两个运算符,在实现__eq__()方法之后,程序即可使用"=="和“!=”两个运算符;在实现__ge__()方法之后,程序即可使用">="和"<="两个运算符。
● object.__neg__(self):为单目求负(-)运算符提供支持。
● object.__pos__(self):为单目求正(+)运算符提供支持。
● object.__invert__(self):为单目取反(~)运算符提供支持。
● object.__str__(self):对应于调用内置的str()函数将该对象转换成字符串。
● object.__bytes__(self):对应于调用内置的bytes()函数将对象转换成字节内容。该方法应该返回bytes对象。
● object.__complex__(self):对应于调用内置的complex()函数将对象转换成复数。该方法应该返回complex对象。
● object.__int__(self):对应于调用内置的int()函数将对象转换成整数。该方法应该返回int对象。
● object.__float__(self):对应于调用内置函数float()函数将对象转换成浮点数。该方法应该返回float对象。
● object.__format__(self, format_spec):对应于调用内置的format()函数将对象转换成格式化字符串。
● object.__hash__(self):对应于调用内置的hash()函数来获取该对象的hash码。
● object.__abs__(self):对应于调用内置的abs()函数返回绝对值。
● object.__round__(self[, ndigits]):对应于调用内置的round()函数执行四舍五入取整。
● object.__trunc__(self):对应于调用内置的trunc()函数执行截断取整。
如果某个自定义类没有提供__int__(self)方法,而是提供了__trunc__(self)方法,那么程序在调用内置的int()函数将其转换为整数时,底层将由__trunc__(self)方法提供支持。
● object.__floor__(self):对应于调用内置的floor()函数执行向下取整。
● object.__ceil__(self):对应于调用内置的ceil()函数执行向上取整。
习题:
1. 自定义一个序列,该序列按顺序包含52 张扑克牌,分别是黑桃、红心、草花、方块的2~A 。要求:提供序列的各种操作方法。
class Poker:
# 定义构造方法
def __init__(self):
self.flowers = ('♠', '♥', '♣', '♦')
self.values = ('2', '3', '4', '5', '6', '7', '8', '9'
'10', 'J', 'Q', 'K', 'A')
self.__changed = {}
self.__deleted = []
# 定义__len__方法,返回序列长度
def __len__(self):
return 52
# 定义__getitem__方法,用于获取序列中位置为key的元素
def __getitem__(self, key):
# 判断key是否合法
check_key(key)
# 如果在__changed中找到已经修改后的数据,则返回该数据
if key in self.__changed:
return self.__changed[key]
# 如果在__deleted中找到已经删除的数据,则返回None
if key in self.__deleted:
return None
# 否则根据计算返回序列值
flower = key // 13
value = key % 13
return self.flowers[flower] + self.values[value]
# 定义__setitem__方法,修改序列值
def __setitem__(self, key, value):
# 判断key是否合法
check_key(key)
# 将序列位置为key的元素值设置为value
self.__changed[key] = value
# 定义__delitem__方法,用于删除序列中的值
def __delitem__(self, key):
# 判断key是否合法
check_key(key)
# 如果要删除的元素没有包含在__deleted中,则添加
if key not in self.__deleted:
self.__deleted.append(key)
# 如果要删除的元素包含在__changed中,则删除
if key in self.__changed:
del self.__changed[key]
def check_key(key):
# 如果key不是整数,则抛出TypeError异常
if not isinstance(key, int):
raise TypeError('索引值必须是整数')
# 如果key大于52,则抛出IndexError异常
if key >= 52 or key < 0:
raise IndexError('索引值必须在0~52之间')
if __name__ == '__main__':
cq = Poker()
print(len(cq))
print(cq[2]) # '♠4'
print(cq[1]) # '♠3'
# 修改cq[1]元素
cq[1] = '♣2'
# 打印修改之后的cq[1]
print(cq[1]) # '♣2'
# 删除cq[1]
del cq[1]
print(cq[1]) # None
# 再次对cq[1]赋值
cq[1] = '♦5'
print(cq[1]) # ♦5
for pk in cq:
print(pk)
2. 自定义一个序列,该序列按顺序包含所有三位数(如100, 101, 102) 。要求: 提供序列的各种操作方法。
start = 100
end = 999
count = end - start + 1
def check_key(key):
if not isinstance(key, int):
raise KeyError('索引必须是一个整数')
if key < 0:
raise KeyError('索引值必须是一个非负整数')
if key > count:
raise KeyError('索引值必须在%d~%d之间' % (0, count))
def check_value(value):
if not isinstance(value, int):
raise ValueError('值必须是一个整数')
if value < start or value > end:
raise ValueError('值必须在%d~%d之间' % (start, end))
class NumberSeq:
# 定义构造函数
def __init__(self):
self.__changed = {}
self.__deleted = []
# 定义__len__方法,返回序列长度
def __len__(self):
return count
# 定义__getitem__方法,根据key返回对应的值
def __getitem__(self, key):
# 检查key是否合法
check_key(key)
# 如果key在__changed中,则返回已修改的值
if key in self.__changed:
return self.__changed[key]
# 如果key在__deleted中,则说明该元素已删除,返回None
if key in self.__deleted:
return None
# 否则返回计算后的值
return key + start
# 定义__setitem__方法,设置位置为key的元素的值为value
def __setitem__(self, key, value):
# 检查key是否合法
check_key(key)
# 检查value是否合法
check_value(value)
# 将索引为key的元素值修改为value
self.__changed[key] = value
# 定义__delitem__方法,删除位置为key的元素
def __delitem__(self, key):
# 检查key是否合法
check_key(key)
# 如果key不在__deleted中,则添加
if key not in self.__deleted:
self.__deleted.append(key)
# 如果key在__changed中,则删除
if key in self.__changed:
del self.__changed[key]
if __name__ == '__main__':
nq = NumberSeq()
print(len(nq))
print(nq[2]) # 101
print(nq[1]) # 100
# 修改nq[1]元素
nq[1] = 123
# 打印修改之后的nq[1]
print(nq[1]) # 123
# 删除nq[1]
del nq[1]
print(nq[1]) # None
# 再次对nq[1]赋值
nq[1] = 987
print(nq[1]) # 987
3. 自定义一个迭代器,该迭代器分别返回1 , 1+2, 1+2+3 …的累积和。
class Sums:
# 定义构造函数,初始化当前索引为1, 值为0
def __init__(self, len):
self.current_index = 1
self.current_value = 0
self.__len = len
# 定义迭代器所需的__next__方法,返回下一个元素
def __next__(self):
if self.__len == 0:
raise StopIteration
# 完成数列计算
self.current_value += self.current_index
self.current_index += 1
# 数列长度减1
self.__len -= 1
return self.current_value
# 定义__iter__方法,返回迭代器本身
def __iter__(self):
return self
sums = Sums(10)
# 获取迭代器的下一个元素
print(next(sums))
for el in sums:
print(el, end=' ')
4. 自定义一个生成器,该生成器可按顺序返回52 张扑克牌, 分别是黑桃、红心、草花、方块的2~A 。
def poker_generator():
nums = 52
flowers = ('♠', '❤', '♣', '♦')
values = ('2', '3', '4', '5', '6', '7', '8', '9',
'10', 'J', 'Q', 'K', 'A')
for i in range(nums):
yield flowers[i // 13] + values[i % 13]
if __name__ == '__main__':
pk = poker_generator()
print(next(pk)) # ♠2,生成器“冻结”在yield处
print(next(pk)) # ♠3,生成器再次冻结在yield处
for el in pk:
print(el, end=' ')
5. 自定义一个生成器,可依次返回l , 2, 3, 4…的阶乘。
def factory(n):
if n == 0:
return 1
if n == 1:
return 1
return n * factory(n-1)
def factory_generator(n):
for i in range(1,n + 1):
yield factory(i)
fg = factory_generator(5)
print(next(fg))
for f in fg:
print(f, end=' ')
6. 自定义一个生成器,可依次访问前面目录下的所有Python 源文件(以.py 为后缀的文件〉。
import os
def file_generator():
for filename in os.listdir(r'.'):
if filename.endswith('.py'):
yield filename
if __name__ == '__main__':
fg = file_generator()
print(next(fg)) # 返回目录中的第一个文件
print(next(fg)) # 返回目录中的下一个文件
for el in fg:
print(el, end=' ')
7. 自定义一个代表二维坐标系上某个点的Point 类(包括x 、y 两个属性),为Point 类提供自定义的减法运算符支持,计算结果返回两点之间的距离。
class Points:
# 定义构造函数,传入x,y值
def __init__(self, x, y):
self.x = x
self.y = y
# 定义__sub__方法,以支持-操作符
def __sub__(self, other):
return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5
if __name__ == '__main__':
p1 = Points(1, 2)
p2 = Points(1, 3)
print('两点之间的距离为:', p1 - p2)
8. 自定义代表扑克牌的Card 类(包括花色和牌面值),为Card 类提供自定义的比较大小的运算符支持, 大小比较标准是先比较牌面值,如果牌面值相等则比较花色,花色大小规则为: 黑桃>红心>草花>方块。
flowers = ('♦', '♣', '♥', '♠')
values = ('2', '3', '4', '5',
'6', '7', '8', '9',
'10', 'J', 'Q', 'K', 'A')
class Card:
# 定义构造函数
def __init__(self, flower, value):
self.flower = flower
self.value = value
def __gt__(self, other):
if not isinstance(other, Card):
raise TypeError('+运算要求目标是Card')
if values.index(self.value) > values.index(other.value):
return True
elif values.index(self.value) == values.index(other.value) and \
flowers.index(self.flower) > flowers.index(other.flower):
return True
else:
return False
def __eq__(self, other):
if not isinstance(other, Card):
raise TypeError('+运算要求目标是Card')
if values.index(self.value) == values.index(other.value) and \
flowers.index(self.flower) == flowers.index(other.flower):
return True
else:
return False
def __ge__(self, other):
if not isinstance(other, Card):
raise TypeError('+运算要求目标是Card')
return self > other or self == other
def __repr__(self):
return '%s-%s' % (self.flower, self.value)
if __name__ == "__main__":
cd1 = Card(flower="♠", value="A")
cd2 = Card(flower="♠", value="K")
cd3 = Card(flower="♥", value="K")
cd4 = Card(flower="♥", value="J")
cd5 = Card(flower="♥", value="K")
print(cd1 > cd2) # True
print(cd1 < cd2) # False
print(cd2 < cd3) # False
print(cd2 > cd3) # True
print(cd3 == cd5) # True
print(cd3 < cd5) # False
print(cd3 > cd5) # False
print(cd3 >= cd5) # True
print(cd3 <= cd5) # True