疯狂Python讲义学习笔记(含习题)之 Python类的特殊方法

在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
​

 

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值