python之类与对象进阶(三)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


类中的特殊函数—魔法函数

10. 使用重载实现自定义序列

在这里插入图片描述
下面程序实现了一个比较简单的序列类,这是一个字典类,其特点是只能存储 int 类型的元素:

class IntDic:   
    def __init__(self):
        # 用于存储数据的字典
        self.__date = {}
    def __len__(self):
        return len(list(self.__date.values()))
           
    def __getitem__(self, key):
        # 如果在self.__changed中找到已经修改后的数据
        if key in self.__date :
            return self.__date[key]
        return None
    
    def __setitem__(self, key, value):
        #判断value是否为整数
        if not isinstance(value, int):
            raise TypeError('必须是整数')
        #修改现有 key 对应的 value 值,或者直接添加
        self.__date[key] = value
    def __delitem__(self, key):
        if key in self.__date : del self.__date[key]
dic = IntDic()
#输出序列中元素的个数,调用 __len__() 方法
print(len(dic))
#向序列中添加元素,调用 __setitem__() 方法
dic['a'] = 1
dic['b'] = 2
print(len(dic))
dic['a'] = 3
dic['c'] = 4
print(dic['a'])
#删除指定元素,调用 __delitem__() 方法
del dic['a']
print(dic['a'])
print(len(dic))

输出为:

0
2
3
None
2

迭代器

支持for循环遍历的都是可迭代对象(Iterable),注意还不是迭代器;
迭代器(Iterator)除了是可迭代对象外,还要满足可以被next()函数调用并不断返回下一个值。
例如:

>>> from collections.abc import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
>>> from collections.abc import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

你可能会问,为什么list、dict、str等数据类型不是Iterator?

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

  • 小结
- 凡是可作用于for循环的对象都是Iterable类型;
- 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
- 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
- Python的for循环本质上就是通过不断调用next()函数实现的 

前面已经学会了如何自定义一个序列类,但该序列类对象并不支持迭代,因此还不能称之为迭代器。如果要自定义实现一个迭代器,则类中必须实现如下 2 个方法:
__ next __(self): 返回容器的下一个元素。
__ iter __(self): 该方法返回一个迭代器(iterator)。

例如,下面程序自定义了一个简易的列表容器迭代器,支持迭代:

class listDemo:
    def __init__(self):
        self.__date=[]
        self.__step = 0
    def __next__(self):
        if self.__step <= 0:
            raise StopIteration
        self.__step -= 1
        #返回下一个元素
        return self.__date[self.__step]
    def __iter__(self):
        #实例对象本身就是迭代器对象,因此直接返回 self 即可
        return self
    #添加元素
    def __setitem__(self,key,value):
        self.__date.insert(key,value)
        self.__step += 1
mylist = listDemo()
mylist[0]=1
mylist[1]=2
for i in mylist:
    print (i)

程序执行结果为:

2
1

注意:要设置遍历终止条件,也就是在自定义类中加入对循环结束的判断,并抛出 StopIteration 异常,只有这么做了,for 循环才会接收到 StopIteration 异常,并当做终止信号来结束循环。

除此之外,Python 内置的 iter() 函数也会返回一个迭代器,该函数的语法格式如下:

iter(obj[, sentinel])

其中,obj 必须是一个可迭代的容器对象,而 sentinel 作为可选参数,现在先不做要求。

# 将列表转换为迭代器
myIter = iter([1, 2, 3])
# 依次获取迭代器的下一个元素
print(myIter.__next__())   #也可以使用(next(myIter))
print(myIter.__next__())
print(myIter.__next__())
print(myIter.__next__())

输出为:

1
2
3
Traceback (most recent call last):
  File "C:\Users\mengma\Desktop\demo.py", line 7, in <module>
    print(myIter.__next__())
StopIteration

从程序的执行结果可以看出,当迭代完存储的所有元素之后,如果继续迭代,则 next() 方法会抛出 StopIteration 异常。


生成器

生成器本质上也是迭代器。 只不过它比较特殊,以 list 容器为例,在使用该容器迭代一组数据时,必须事先将所有数据存储到容器中,才能开始迭代;而生成器却不同,它可以实现在迭代的同时生成元素。

不仅如此,生成器的创建方式也比迭代器简单很多,大体分为以下 2 步:

 - 定义一个以 yield 关键字标识返回值的函数;
 - 调用刚刚创建的函数,即可创建一个生成器。

举个例子:

def intNum():
    print("开始执行")
    for i in range(5):
        yield i
        print("继续执行")
num = intNum()

由此,我们就成功创建了一个 num 生成器对象。显然,和普通函数不同,intNum() 函数的返回值用的是 yield 关键字,而不是 return 关键字,此类函数又成为生成器函数。
和 return 相比,yield 除了可以返回相应的值,还有一个更重要的功能,即每当程序执行完该语句时,程序就会暂停执行。

要想使生成器函数得以执行,或者想使执行完 yield 语句立即暂停的程序得以继续执行,有以下 2 种方式:

- 通过生成器(上面程序中的 num)调用 next() 内置函数或者 __next__() 方法;
- 通过 for 循环遍历生成器。   #本质上也是底层不断调用next

例如,在上面程序的基础上,添加如下语句:

#调用 next() 内置函数
print(next(num))
#调用 __next__() 方法
print(num.__next__())
#通过for循环遍历生成器
for i in num:
    print(i)

结果为:

开始执行
0
继续执行
1
继续执行
2
继续执行
3
继续执行
4
继续执行

除此之外,还可以使用 list() 函数和 tuple() 函数,直接将生成器能生成的所有值存储成列表或者元组的形式。例如:

num = intNum()
print(list(num))
num = intNum()
print(tuple(num))

结果为:

开始执行
继续执行
继续执行
继续执行
继续执行
继续执行
[0, 1, 2, 3, 4]
开始执行
继续执行
继续执行
继续执行
继续执行
继续执行
(0, 1, 2, 3, 4)

相比迭代器,生成器最明显的优势就是节省内存空间,即它不会一次性生成所有的数据,而是什么时候需要,什么时候生成。

生成器的send、close、throw
生成器的send、close、throw


函数装饰器@

函数装饰器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值