part8-2 Python 类的特殊方法,算术、比较、单目运算符重载,类型转换的特殊方法,内建函数的特殊方法,类的特殊方法练习


一、 运算符重载的特殊方法
为自定义类提供特殊方法,让自定义类的对象也支持各种运算符的运算。
1、 与数值运算相关的特殊方法
与数值相关的算术运算符、位运算符等运算符都是由对应的方法提供支持。可以自行为自定义类提供下面这些方法。
(1)、object.__add__(self, other):加法运算,为“+”运算提供支持。
(2)、object.__sub__(self, other):减法运算,为“-”运算符提供支持。
(3)、object.__mul__(self, other):乘法运算,为“*”运算符提供支持。
(4)、object.__matmul__(self, other):矩阵乘法,为“@”运算符提供支持。
(5)、object.__truediv__(self, other):除法运算,为“/”运算符提供支持。
(6)、object.__floordiv__(self, other):整除运算,为“//”运算符提供支持。
(7)、object.__mod__(self, other):求余运算,为“%”运算符提供支持。
(8)、object.__divmod__(self, other):求余运算,为 divmod 运算符提供支持。
(9)、object.__pow__(self, other[,modulo]):乘方运算,为“**”运算符提供支持。
(10)、object.__lshift__(self, other):左移运算,为“<<”运算符提供支持。
(11)、object.__rshift__(self, other):右移运算,为“>>”运算符提供支持。
(12)、object.__and__(self, other):按位与运算,为“&”运算符提供支持。
(13)、object.__xor__(self, other):按位异域运算,为“^”运算符提供支持。
(14)、object.__or__(self, other):按位或运算,为“|” 运算符提供支持。
只要为自定义类提供了这些方法,就可以直接用运算符来操作该类的实例,比如执行 x+y ,相当于调用 x.__add__(self,y),只要为 x 所属的类提供 __add__(self,other) 方法即可,如果自定义类没有提供对应的方法,程序会返回 NotImplemented。
下面代码示例两个 Rectangle 类对象执行加法运算,只要为这个类提供 __add__(self, other) 方法即可,示例如下:
 1 class Rectangle:
 2     def __init__(self, width, height):
 3         self.width = width
 4         self.height = height
 5     # 定义 setsize() 函数
 6     def setsize(self, size):
 7         self.width, self.height = size
 8     # 定义 getsize() 函数
 9     def getsize(self):
10         return self.width, self.height
11     # 使用 property 定义属性
12     size = property(getsize, setsize)
13     # 定义__add__方法,该对象可执行“+”运算
14     def __add__(self, other):
15         # 要求参与 “+” 运算的另一个操作数必须是 Rectangle 对象
16         if not isinstance(other, Rectangle):
17             raise TypeError("加(+)运算要求目标是 Rectangle 类对象")
18         # 返回的是 Rectangle 类对象
19         return Rectangle(self.width + other.width, self.height + other.height)
20     def __repr__(self):
21         return "Rectange(width=%g, height=%g)" % (self.width, self.height)
22 r1 = Rectangle(3, 6)
23 r2 = Rectangle(5, 9)
24 # 两个 Rectangle 对象执行加法运算
25 r = r1 + r2
26 print(r)        # 输出:Rectange(width=8, height=15)
上面代码中为 Rectangle 类提供了 __add__ 方法,接下来 Rectangle 类的两个对象可以使用 “+” 执行加法运算,加法运算的结果仍是 Rectangle 类对象,这是由 __add__ 方法的返回值决定的。
在对x、y 对象执行 x+y 运算时,首先使用 x 对象的 __add__ 方法进行计算;如果 x 对象没有提供 __add__ 方法,则会尝试调用y 对象的 __radd__ 方法进行计算,也就是上面这些与数值运算相关的特殊方法,还有一个带 r 的方法。这些方法如下所示:
(1)、object.__radd__(self,other):当 y 提供该方法时,可执行 x + y。
(2)、object.__rsub__(self,other):当 y 提供该方法时,可执行 x - y。
(3)、object.__rmul__(self,other):当 y 提供该方法时,可执行 x * y。
(4)、object.__rmatmul__(self,other):当 y 提供该方法时,可执行 x @ y。
(5)、object.__rtruediv__(self,other):当 y 提供该方法时,可执行 x / y。
(6)、object.__rfloordiv__(self,other):当 y 提供该方法时,可执行 x // y。
(7)、object.__rmod__(self,other):当 y 提供该方法时,可执行 x % y。
(8)、object.__rdivmod__(self,other):当 y 提供该方法时,可执行 x divmod y。
(9)、object.__rpow__(self,other):当 y 提供该方法时,可执行 x ** y。
(10)、object.__rlshift__(self,other):当 y 提供该方法时,可执行 x << y。
(11)、object.__rrshift__(self,other):当 y 提供该方法时,可执行 x >> y。
(12)、object.__rand__(self,other):当 y 提供该方法时,可执行 x & y。
(13)、object.__rxor__(self,other):当 y 提供该方法时,可执行 x ^ y。
(14)、object.__ror__(self,other):当 y 提供该方法时,可执行 x | y。
也就是说,只要为自定义类提供了上面这些 __rxxx__() 方法时,该自定义类的对象就可以出现在对应运算符的右边。下面代码示例了为 Rectangle 类提供 __radd__() 方法,此时运算符左边的对象没有提供对应的运算方法时,同样可以执行运算。示例如下:
 1 class Rectangle:
 2     def __init__(self, width, height):
 3         self.width = width
 4         self.height = height
 5     # 定义 setsize() 函数
 6     def setsize(self, size):
 7         self.width, self.height = size
 8     # 定义 getsize() 函数
 9     def getsize(self):
10         return self.width, self.height
11     # 使用 property 定义属性
12     size = property(getsize, setsize)
13     # 定义__radd__方法,该对象可出现在“+”运算的右边
14     def __radd__(self, other):
15         # 要求参与 “+” 运算的另一个操作数必须是数值
16         if not isinstance(other, int) or isinstance(other, float):
17             raise TypeError("加(+)运算要求目标是数值")
18         # 返回的是 Rectangle 类对象
19         return Rectangle(self.width + other, self.height + other)
20     def __repr__(self):
21         return "Rectange(width=%g, height=%g)" % (self.width, self.height)
22 r1 = Rectangle(3, 6)
23 # 两个 Rectangle 对象执行加法运算
24 r = 7 + r1
25 print(r)        # 输出:Rectange(width=10, height=13)
从上面代码最后一行的输出可知,为类提供了 __radd__方法,因此类的对象可出现在 “+” 运算符的右边,支持加法运算。
此外,Python 还支持各种扩展后赋值运算符,这些运算符也是由特殊方法来提供支持,方法说明如下:
(1)、object.__iadd__(self, other):为 “+=” 运算符提供支持。
(2)、object.__isub__(self, other):为 “-=” 运算符提供支持。
(3)、object.__imul__(self, other):为 “*=” 运算符提供支持。
(4)、object.__imatmul__(self, other):为 “@=” 运算符提供支持。
(5)、object.__itruediv__(self, other):为 “/=” 运算符提供支持。
(6)、object.__ifloordiv__(self, other):为 “//=” 运算符提供支持。
(7)、object.__imod__(self, other):为 “%=” 运算符提供支持。
(8)、object.__ipow__(self, other[,modulo]):为 “**=” 运算符提供支持。
(9)、object.__ilshift__(self, other):为 “<<=” 运算符提供支持。
(10)、object.__irshift__(self, other):为 “>>=” 运算符提供支持。
(11)、object.__iand__(self, other):为 “&=” 运算符提供支持。
(12)、object.__ixor__(self, other):为 “^=” 运算符提供支持。
(13)、object.__ior__(self, other):为 “|=” 运算符提供支持。
下面代码为 Rectangle 类定义一个 __iadd__() 方法,使得该类的对象支持 “+=” 运算。示例如下:
 1 class Rectangle:
 2     def __init__(self, width, height):
 3         self.width = width
 4         self.height = height
 5     # 定义 setsize() 函数
 6     def setsize(self, size):
 7         self.width, self.height = size
 8     # 定义 getsize() 函数
 9     def getsize(self):
10         return self.width, self.height
11     # 使用 property 定义属性
12     size = property(getsize, setsize)
13     # 定义__iadd__方法,该对象可出现在“+”运算的右边
14     def __iadd__(self, other):
15         # 要求参与 “+” 运算的另一个操作数必须是数值
16         if not isinstance(other, int) or isinstance(other, float):
17             raise TypeError("加(+=)运算要求目标是数值")
18         # 返回的是 Rectangle 类对象
19         return Rectangle(self.width + other, self.height + other)
20     def __repr__(self):
21         return "Rectange(width=%g, height=%g)" % (self.width, self.height)
22 r1 = Rectangle(3, 6)
23 # r1 有 __iadd__ 方法,因此支持 “+=” 运算
24 r1 += 7
25 print(r1)        # 输出:Rectange(width=10, height=13)
从输出可知,Rectangle 类的对象可以支持 “+=” 运算。
2、 与比较运算符相关的特殊方法
对于 >、<、>=、<=、== 和 != 等运算符也是由特殊方法提供支持的。如果为自定义类提供了这些特殊方法,该类的对象就可使用比较运算符来比较大小。与比较运算符相关的特殊方法有下面几个。
(1)、object.__lt__(self, other):为 “<” 运算符提供支持。
(2)、object.__le__(self, other):为 “<=” 运算符提供支持。
(3)、object.__eq__(self, other):为 “==” 运算符提供支持。
(4)、object.__ne__(self, other):为 “!=” 运算符提供支持。
(5)、object.__gt__(self, other):为 “>” 运算符提供支持。
(6)、object.__ge__(self, other):为 “>=” 运算符提供支持。
上面这些比较运算符不需要每个都都实现,只需要实现其中三个方法即可。实现 __gt__() 方法后,可使用 “>” 和 “<” 两个运算符;
实现 __eq__() 方法后,可使用 “==” 和 “!=” 两个运算符;实现 __ge__() 方法后,可使用 “>=” 和 “<=” 两个运算符。
下面代码为 Rectangle 类提供这些特殊方法,使两个 Rectangle 类对象基于面积比较大小。示例如下:
 1 class Rectangle:
 2     def __init__(self, width, height):
 3         self.width = width
 4         self.height = height
 5     # 定义 setsize() 函数
 6     def setsize(self, size):
 7         self.width, self.height = size
 8     # 定义 getsize() 函数
 9     def getsize(self):
10         return self.width, self.height
11     # 使用 property 定义属性
12     size = property(getsize, setsize)
13     # 定义 __gt__ 方法,该对象可支持“>” 和 “<” 比较
14     def __gt__(self, other):
15         # 要求参与 “>” 比较的另一个操作数必须是 Rectangle 对象
16         if not isinstance(other, Rectangle):
17             raise TypeError('比较运算(>)要求目标是 Rectangle 类对象')
18         return True if self.width * self.height > other.width * other.height else False
19     # 定义 __eq__ 方法,该对象可支持 “==” 和 “!=” 比较
20     def __eq__(self, other):
21         # 要求参与 “==” 比较的另一个操作数必须是 Rectangle 类对象
22         if not isinstance(other, Rectangle):
23             raise TypeError("比较运算(==)要求目标是 Rectangle 类对象")
24         return True if self.width * self.height == other.width * other.height else False
25     # 定义 __ge__ 方法,该对象可支持 “>=” 和 “<=” 比较
26     def __ge__(self, other):
27         # 要求参与 “>=” 比较的另一个操作数必须是 Rectanlge 类对象
28         if not isinstance(other, Rectangle):
29             raise TypeError("比较运算(>=)要求目标是 Rectangle 类对象")
30         return True if self.width * self.height >= other.width * other.height else False
31     def __repr__(self):
32         return "Rectange(width=%g, height=%g)" % (self.width, self.height)
33 
34 r1 = Rectangle(5, 6)
35 r2 = Rectangle(3, 4)
36 print(r1 > r2)          # 输出:True
37 print(r1 >= r2)         # 输出:True
38 print(r1 < r2)          # 输出:False
39 print(r1 <= r2)         # 输出:False
40 print(r1 == r2)         # 输出:False
41 print(r1 != r2)         # 输出:True
42 print('-'*20)
43 r3 = Rectangle(3, 7)
44 print(r2 > r3)          # 输出:False
45 print(r2 >= r3)         # 输出:False
46 print(r2 <= r3)         # 输出:True
47 print(r2 < r3)          # 输出:True
48 print(r2 == r3)         # 输出:False
49 print(r2 != r3)         # 输出:True
上面代码中,为 Rectangle 类定义了 __gt__()、__eq__()和 __ge__() 方法,接下来该类的对象可以使用各种比较运算符进行大小的比较。
3、 与单目运算符相关的特殊方法
单目运算符有:单目求正(+)、单目求负(-)、单目取反(~),这些运算也有对应的特殊方法。
(1)、object.__neg__(self):为单目求负(-)运算符提供支持。
(2)、object.__pos__(self):为单目求正(+)运算符提供支持。
(3)、object.__invert__(self):为单目取反(~)运算符提供支持。
下面代码为 Rectangle 类实现一个 __neg__() 方法,用于控制将矩形的宽、高交换。代码如下:
 1 class Rectangle:
 2     def __init__(self, width, height):
 3         self.width = width
 4         self.height = height
 5     # 定义 setsize() 函数
 6     def setsize(self, size):
 7         self.width, self.height = size
 8     # 定义 getsize() 函数
 9     def getsize(self):
10         return self.width, self.height
11     # 使用 property 定义属性
12     size = property(getsize, setsize)
13     # 定义 __neg__ 方法,该对象可执行求负(-)运算
14     def __neg__(self):
15         self.width, self.height = self.height, self.width
16     def __repr__(self):
17         return "Rectange(width=%g, height=%g)" % (self.width, self.height)
18 r = Rectangle(3, 5)
19 # 对 Rectangle 类的对象执行求负运算
20 -r
21 print(r)        # 输出:Rectange(width=5, height=3)
上面代码为 Rectange 类实现了 __neg__ 方法,该类的对象即可执行求负运算,由于在 __neg__ 方法内部是交换矩形的宽和高,因此程序对 Rectangle 类对象执行求负运算其实交换矩形的宽和高。从输出可知矩形的宽和高已经交换。
4、 与类型转换相关的特殊方法
Python 提供的 str()、int()、float()、complex() 等函数(其实是这些类的构造器)可将其他类型的对象转换成字符串、整数、浮点数和复数,这些转换同样也是由特殊方法在底层提供支持。关于这些特殊方法说明如下:
(1)、object.__str__(self):对应于调用内置的 str() 函数将该对象转换成字符串。
(2)、object.__bytes__(self):对应于调用内置的 bytes() 函数将该对象转换成字节内容。该方法应返回 bytes 对象。
(3)、object.__complex__(self):对应于调用内置的 complex() 函数将该对象转换成复数。该方法返回 complex 对象。
(4)、object.__int__(self):对应于调用内置的 int() 函数将对象转换成整数。该方法返回 int 对象。
(5)、object.__float__(self):对应于调用内置的 float() 函数将对象转换成浮点数。该方法返回 float 对象。
要注意的是,__str__() 和 __repr__() 方法功能有些相似,都用于将对象转换成字符串。不同的是:__repr__ 表示“自我描述”的方法,在程序中调用 print() 函数输出对象时,Python 会自动调用该对象的 __repr__() 方法,而 __str__() 方法只有在显式调用 str() 时才会起作用。
下面代码为 Rectangle 类实现一个 __int__() 方法,这样就可调用 int() 函数将 Rectangle 类对象转换成整数。示例如下:
 1 class Rectangle:
 2     def __init__(self, width, height):
 3         self.width = width
 4         self.height = height
 5     # 定义 setsize() 函数
 6     def setsize(self, size):
 7         self.width, self.height = size
 8     # 定义 getsize() 函数
 9     def getsize(self):
10         return self.width, self.height
11     # 使用 property 定义属性
12     size = property(getsize, setsize)
13     # 定义 __int__ 方法,这样可调用 int() 函数将该类对象转换成整数
14     def __int__(self):
15         return int(self.width * self.height)
16     def __repr__(self):
17         return "Rectange(width=%g, height=%g)" % (self.width, self.height)
18 r = Rectangle(3, 5)
19 print(int(r))           # 输出:15
从上面代码的最一行输出可知,在类中实现的 __int__ 方法成功被调用,在这个方法中是计算的面积。
5、与常见的内建函数相关的特殊方法
调用内建函数处理对象时,也是由下面这些特殊方法来提供支持的。
(1)、object.__format__(self, format_spec):对应于调用内置的 format() 函数将对象转换成格式化字符串。
(2)、object.__hash__(self):对应于调用内置的 hash() 函数获取对象的 hash 值。
(3)、object.__abs__(self):对应于调用内置的 abs() 函数返回值。
(4)、object.__round__(self, ndigits):对应于调用内置的 round() 函数执行四舍五入取整。
(5)、object.__trunc__(self):对应于调用内置的 trunc() 函数执行截断取整。
(6)、object.__floor__(self):对应于调用内置的 floor() 函数执行向下取整
(7)、object.__ceil__(self):对应于调用内置的 ceil() 函数执行向上取整。
要注意的是,在自定义类中没有提供 __int__(self) 方法,但是提供了 __trunc__(self) 方法时,那么自定义类的对象在调用内置的 int() 函数转换成整数时,底层将由 __trunc__(self):方法提供支持。
下面代码为 Rectangle 类实现一个 __round__() 方法,使其可调用 round() 函数对该类的对象执行四舍五入取整。示例如下:
 1 class Rectangle:
 2     def __init__(self, width, height):
 3         self.width = width
 4         self.height = height
 5     # 定义 setsize() 函数
 6     def setsize(self, size):
 7         self.width, self.height = size
 8     # 定义 getsize() 函数
 9     def getsize(self):
10         return self.width, self.height
11     # 使用 property 定义属性
12     size = property(getsize, setsize)
13     # 定义 __round__ 方法,使其可调用 round() 函数将对象执行四舍五入取整
14     def __round__(self, n=None):
15         self.width, self.height = round(self.width, n), round(self.height, n)
16         return self
17     def __repr__(self):
18         return "Rectange(width=%g, height=%g)" % (self.width, self.height)
19 
20 r = Rectangle(3.14, 8.53)
21 # 对 Rectangle 类对象执行四舍五入取整,__round__ 方法返回的是 self 对象,可用变量接收
22 result = round(r, 1)
23 print(r)                # 输出:Rectange(width=3.1, height=8.5)
24 print(result)           # 输出:Rectange(width=3.1, height=8.5)
从输出表明,在 Rectangle 类中定义 __round__() 方法后,该类的对象就可调用 round() 函数执行四舍五入取整。对于其他的特殊方法就不每个都去实现了。
二、 小结
Python 提供了大量有特殊意义的方法,这些方法有些可直接使用,有些需要重写,掌握这些方法是面向对象编程的基础。
此外,序列和生成器也有与之相关的特殊方法;与运算符重载相关的特殊方法、与类型转换相关的特殊方法、与常见的内建函数相关的特殊方法等,要对这些方法加深理解。
练习:



1、自定义一个序列,该序列按顺序包含 52 张扑克牌,分别是黑桃、红心、梅花、方块的2~A。要求提供序列的各种操作方法。
 1 def check_key(key):
 2     if not isinstance(key, int): raise TypeError("索引值必须是整数")
 3     if key < 0: raise IndexError('索引值必须是非负数')
 4     if key >= 52: raise IndexError('索引值不能超过%d' % 52)
 5 
 6 class CardSeq:
 7     def __init__(self):
 8         self.flowers = ('♠', '♥', '♣', '♦')
 9         self.values = ('2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A')
10         self.__changed = {}
11         self.__deleted = []
12     def __len__(self):
13         return 52
14     def __getitem__(self, item):
15         check_key(item)
16         # 如果在 self.__changed 中找到,就返回修改后的数据
17         if item in self.__changed:
18             return self.__changed[item]
19         # 如果 item 在 self.__deleted 中找到,说明该元素已被删除
20         if item in self.__deleted:
21             return None
22         # 否则根据计算返回序列元素
23         flower = item // 13         # 花色通过对 13 整除获得
24         value = item % 13           # 点数通过对 13 求余获得
25         return self.flowers[flower] + self.values[value]
26     def __setitem__(self, key, value):
27         check_key(key)
28         self.__changed[key] = value
29     def __delitem__(self, key):
30         check_key(key)
31         # 如果 self.__deleted 列表中没有包含被删除的 key,添加被删除的 key
32         if key not in self.__deleted: self.__deleted.append(key)
33         # 如果 self.__changed 中包含被删除的 key,则需要删除它
34         if key in self.__changed: del self.__changed[key]
35 
36 if __name__ == '__main__':
37     cd = CardSeq()
38     print(len(cd))          # 52
39     print(cd[1])            # ♠3
40     print(cd[9])            # ♠J
41     # 修改 cd[1] 的元素
42     cd[1] = '♣5'
43     # 输出修改后的 cq[1]
44     print(cd[1])            # ♣5
45     # 删除 cd[1]
46     del cd[1]
47     print(cd[1])
48     # 再次对 cd[1] 赋值      # None
49     cd[1] = "♦A"
50     print(cd[1])            # ♦A

2、定义一个序列,该序列按顺序包含所有三位数(如100,101,102,...)。要求提供序列的各种操作方法。
注意:序列的索引是从0开始,序列的第一个值是100。
 1 start = 100
 2 end = 999
 3 nums = end - start + 1      # 序列的总数
 4 def check_key(key):
 5     """检查要获取的序列索引是否符合要求"""
 6     if not isinstance(key, int): raise TypeError("索引值必须是整数")
 7     if key < 0: raise IndexError('索引值必须是非负数')
 8     if key >= nums: raise IndexError('索引值不能超过%d' % nums)
 9 def check_value(value):
10     """检查要获取的序列值是否符合要求"""
11     if not isinstance(value, int): raise TypeError("序列值必须是整数")
12     if not (end >= value >= start): raise TypeError('序列值必须在%d和%d之间' % (start, end))
13 
14 class NumSeq:
15     def __init__(self):
16         self.__changed = {}
17         self.__deleted = []
18     def __len__(self):
19         return nums
20     def __getitem__(self, item):
21         check_key(item)
22         # 如果在 self.__changed 中找到已经修改后的数据,就返回修改后的数据
23         if item in self.__changed:
24             return self.__changed[item]
25         # 如果 item 在 self.__deleted 中找到,说明该元素已经被删除,则返回 None
26         if item in self.__deleted:
27             return None
28         # 起始值加索引的方式就是要在序列中获取的值
29         return start + item
30     def __setitem__(self, key, value):
31         """根据索引(key)设置值(value)"""
32         check_key(key)
33         check_value(value)
34         # 满足上面两个条件后设置值
35         self.__changed[key] = value
36     def __delitem__(self, key):
37         """根据索引(key)删除值"""
38         check_key(key)
39         # 如果 self.__deleted 列表中没包含被删除的 key,添加被删除的 key
40         if key not in self.__deleted: self.__deleted.append(key)
41         # 如果 self.__changed 中包含被删除的 key,就删除它
42         if key in self.__changed: del self.__changed[key]
43 
44 if __name__ == '__main__':
45     nq = NumSeq()
46     print(len(nq))          # 900
47     print(nq[2])            # 102
48     print(nq[1])            # 101
49     # 修改 nq[1] 元素
50     nq[1] = 111
51     # 输出修改后的 nq[1]
52     print(nq[1])            # 111
53     # 删除 nq[1]
54     del nq[1]
55     print(nq[1])            # None
56     # 再次对 nq[1] 赋值
57     nq[1] = 666
58     print(nq[1])            # 666

3、 定义一个迭代器,该迭代器分别返回 1,1+2,1+2+3,...的累积和。
 1 class Sums:
 2     def __init__(self, length):
 3         self.current_index = 1
 4         self.current_value = 0
 5         self.__length = length
 6     # 定义迭代器所需的 __next__ 方法
 7     def __next__(self):
 8         if self.__length == 0:
 9             raise StopIteration
10         # 完成数列计算
11         self.current_value += self.current_index
12         self.current_index += 1
13         # 数列长度减1
14         self.__length -= 1
15         return self.current_value
16     # 定义 __iter__ 方法,该方法返回迭代器
17     def __iter__(self):
18         return self
19 
20 sums = Sums(10)
21 # 获取迭代器的下一个元素
22 print(next(sums))       # 1
23 for e in sums:
24     print(e, end=" ")   # 3 6 10 15 21 28 36 45 55

4、 定义一个生成器,该生成器可按顺序返回 52 张扑克牌,分别是黑桃、红心、梅花、方块的 2~A。
 1 def card_generator():
 2     nums = 52
 3     flowers = ('♠', '♥', '♣', '♦')
 4     values = ('2', '3', '4', '5', '6', '7', '8',
 5               '9', '10', 'J', 'Q', 'K', 'A')
 6     for i in range(nums):
 7         yield flowers[i // 13] + values[i % 13]
 8 
 9 if __name__ == '__main__':
10     cg = card_generator()
11     print(next(cg))         # ♠2,生成器冻结在 yield 处
12     print(next(cg))         # ♠3,生成器再次冻结在 yield 处
13     for e in cg:
14         print(e, end=" ")

5、定义一个生成器,可依次返回1,2,3,4,...,的阶乘。
注意:需要先将阶乘计算后再返回。
 1 def factorial_generator(n):
 2     rvt_list = [1]
 3     for i in range(2, n + 1):
 4         rvt_list.append(rvt_list[-1] * i)
 5     print("----------", len(rvt_list))
 6     for i in range(n):
 7         yield rvt_list[i]
 8 
 9 if __name__ == '__main__':
10     fg = factorial_generator(10)
11     print(next(fg))     # 1,生成器被冻结在 yield 处
12     print(next(fg))     # 2,生成器再次被冻结在 yield 处
13     for e in fg:
14         print(e, end=" ")

6、定义一个生成器,可依次访问当前目录下的所有 Python 源文件(以.py 结尾的文件)。
 1 import os
 2 
 3 def files_generator():
 4     for filename in os.listdir(r'.'):
 5         if filename.endswith(".py"):
 6             yield filename
 7 
 8 if __name__ == '__main__':
 9     fg = files_generator()
10     print(next(fg))
11     print(next(fg))
12     for e in fg:
13         print(e, end=" ")

7、 定义一个代表二维坐标系上某个点的 Point 类(有 x、y 两个属性),为 Point 类提供自定义的减法运算符支持,计算结果是两点之间的距离。
 1 class Point:
 2     def __init__(self, x, y):
 3         self.x = x
 4         self.y = y
 5     def __sub__(self, other):
 6         if not isinstance(other, Point): raise TypeError("要求另一个点必须是 Point 类对象")
 7         return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5
 8 
 9 if __name__ == "__main__":
10     p1 = Point(3, 5)
11     p2 = Point(10, 28)
12     print(p1 - p2)      # 24.041630560342615
13     print(p2 - p1)      # 24.041630560342615

8、定义一个代表扑克牌的 Card 类(包括花色和点数),为 Card 类提供自定义的比较大小的运算符支持,大小比较标准是先比较点数,如果点数相等则比较花色,花色大小规则是:黑桃 > 红心 > 梅花 > 方块。
 1 flowers = ('♦', '♣', '♥', '♠')      # 按照从小到大顺序存放
 2 values = ('2', '3', '4', '5', '6', '7', '8',
 3           '9', '10', 'J', 'Q', 'K', 'A')
 4 class Card(object):
 5     def __init__(self, flower, value):
 6         self.flower = flower
 7         self.value = value
 8 
 9     def __gt__(self, other):
10         if not isinstance(other, Card):
11             raise TypeError('比较运算要求目标是 Card 类型')
12         if values.index(self.value) > values.index(other.value):
13             return True
14         elif values.index(self.value)  == values.index(other.value) and \
15             flowers.index(self.flower) > flowers.index(other.flower):
16             return True
17         else:
18             return False
19     def __eq__(self, other):
20         if not isinstance(other, Card):
21             raise TypeError('比较运算要求目标是 Card 类型')
22         if values.index(self.value) == values.index(other.value) and \
23             flowers.index(self.flower) == flowers.index(other.flower):
24             return True
25         else:
26             return False
27     def __ge__(self, other):
28         if not isinstance(other, Card):
29             raise TypeError('比较运算要求目标是 Card 类型')
30         return self > other or self == other
31     def __repr__(self):
32         return '%s%s' % (self.flower, self.value)
33 
34 if __name__ == '__main__':
35     cd1 = Card(flower="♠", value="A")
36     cd2 = Card(flower='♠', value='K')
37     cd3 = Card(flower='♥', value='K')
38     cd4 = Card(flower='♥', value='J')
39     cd5 = Card(flower='♣', value='K')
40     cd6 = Card(flower='♥', value='K')
41     print(cd1 > cd2)        # True
42     print(cd1 < cd2)        # False
43     print(cd2 < cd3)        # False
44     print(cd2 > cd3)        # True
45     print(cd3 == cd6)       # True
46     print(cd3 < cd5)        # False
47     print(cd3 > cd5)        # True
48     print(cd3 >= cd6)       # True
49     print(cd3 <= cd6)       # True
50     print(cd2)              # ♠K