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