关于迭代器:

什么是迭代?

迭代是拜访调集元素的一种办法。

对list、tuple、str等类型的数据,能够运用for…in…的循环语法从其中依次拿到数据进行运用,这样的进程则称为遍历,也叫迭代。

什么是可迭代目标?

只要是能够经过for…in…的形式进行遍历的,那么这个数据类型便是能够迭代的。

怎么判别数据是否可迭代?

内置函数 isinstance() ,可用来判别一个目标是否为指定数据类型。

若是则回来True,反之则回来False。

但是在python中是没有这个可迭代目标这个数据类型的,不过能够经过导入模块中的类来完成。

from collections.abc import Iterable, Iterator  # 从调集.abc 导入可迭代
print(isinstance([], Iterable))      # 列表,True
print(isinstance({}, Iterable))      # 字典,True
print(isinstance((1,), Iterable))    # 元组,True
print(isinstance(set(), Iterable))   # 调集,True
print(isinstance('asaf', Iterable))  # 字符串,True
print(isinstance(123, Iterable))     # False,整型:非可迭代目标
print(isinstance(range(1, 10), Iterable))  # range类型,True

什么是迭代器:

迭代是python中拜访调集元素的一种十分强壮的一种办法。

迭代器是一个能够记住遍历方位的目标,迭代器目标从第一个元素开端拜访,直到一切的元素被拜访完完毕。迭代器只能往前不会后退。因而不会像列表那样一次性悉数生成,而是能够等到用的时候才生成,因而节省了很多的内存资源。

迭代器有两个办法:iter() 和 next()办法。

迭代器的本质:

咱们能够经过 iter() 函数获取这些可迭代目标的迭代器。

然后咱们能够对获取到的迭代器不断运用 next() 函数来获取下一条数据。

运用迭代器取数据:

"""运用迭代器取数据"""
nums = [1, 2, 3]   # 可迭代目标
nums = iter(nums)  # 迭代器
print('nums是迭代器么?答案是:', isinstance(nums, Iterator))  # True
# 取出迭代器的数据
num1 = next(nums)
print(num1)
num2 = next(nums)
print(num2)
"""留意: 若迭代的次数超过了可迭代目标的长度, 就会报 StopIteration 反常"""

自定义迭代器:

运用 iternext 办法自定义迭代器。

只要在类中,定义 iter 办法,那么这个类创立出来的目标一定是可迭代目标。假如类中完成了 iter 办法和 next 办法的目标,便是迭代器。

当咱们调用 iter() 函数提取一个可迭代目标的迭代器时,实际上会主动调用这个目标的iter办法,而且这个办法回来迭代器。

代码实例:
from collections.abc import Iterator, Iterable
# 类中完成了 iter() 和 next() 办法
class myNum:
    def __iter__(self):  # 回来的是迭代器目标
        # 实例特点
        self.a = 1
        print(self, 'self')  # <__main__.myNum object at 0x100390c50> self
        return self  # 表明实例目标自身是自己的迭代器目标
    def __next__(self):
        # 举例:自增1,为了体现 next() 办法
        self.a += 1
        return self.a
mydata = myNum()  # 创立目标
print(isinstance(mydata, Iterable))   # True
myIter = iter(mydata)  # 把目标生成迭代器
print(myIter, 'myIter')   # <__main__.myNum object at 0x100390c50> myIter
print(isinstance(mydata, Iterator))  # 完成上面2个办法时,才是迭代器:True,这句是自行判别,能够不要
"""
print(next(myIter))
print(next(myIter))  
运用 for 循环替代
"""
for i in range(1, 20):
    print(next(myIter))

小结:

  • 但凡可效果于 for 循环的目标都是 Iterable 类型;
  • 但凡可效果于 next() 函数的目标都是 Iterator 类型;
  • 调集数据类型如 list 、dict、str 等是 Iterable 但不是Iterator,不过能够经过 iter() 函数获得一个 Iterator 目标。

生成器:

在 python 中一边循环一边核算的这种机制,叫做生成器。也叫生成数据的机器代码

以 list 容器为例,在运用该容器迭代一组数据时,必须事先将一切数据存储到容器中,才干开端迭代;而生成器却不同,它能够完成在迭代的一起生成元素。

即:对于能够用某种算法核算得到的多个数据,生成器并不会一次性生成它们,而是什么时候需求,才什么时候生成。

生成器是一种用时刻换空间的做法。比方,运用 list 列表贮存全体正整数,无穷个正整数再大的内存也无法装得下,这个时候就能够运用生成器,完成用一段代码来贮存悉数正整数的效果。

生成器的创立:

  1. 定义一个以 yield 关键字标识回来值的函数;
  2. 调用刚刚创立的函数,即可创立一个生成器。
代码实例:
# 生成器的创立
def intNum():
    print('开端履行程序:...')
    for i in range(5):
        yield i     # return 回到函数调用处,后边的不会履行,yield 可理解为暂停,一起回到函数调用处,但是此刻进到for循环
        print('go on')
# intNum() 函数的回来值用的是 yield 关键字,而不是 return 关键字,此类函数又成为生成器函数。
num = intNum()   # 生成器函数
print(num)       # <generator object intNum at 0x104824110>,生成器函数
# 假如需求持续履行,需求调用 next() 办法,或许用for循环替代
# print(num.__next__())    # 履行一次,就完毕
# print(num.__next__())
# print(num.__next__())
# print(num.__next__())
# print(num.__next__())    # 假如履行时超出 规模,会报错
for i in num:
    print(f'for循环:{i}')
"""
和 return 相比,yield 除了能够回来相应的值,还有一个更重要的功用,即每逢程序履行完该句子时,程序就会暂停履行。
不仅如此,即便调用生成器函数,Python 解释器也不会履行函数中的代码,它只会回来一个生成器(目标)。
"""
"""要想使生成器函数得以履行,或许想使履行完 yield 句子当即暂停的程序得以持续履行,有以下 2 种办法:
1. 经过生成器(上面程序中的 num)调用 next() 内置函数或许 __next__() 办法;
2. 经过 for 循环遍历生成器。"""

闭包:

什么是闭包?

在函数中再嵌套一个函数,而且引用外部函数的变量

构成条件:

1、函数嵌套
2、外部函数回来内部函数名
3、内部函数运用外部函数的变量

代码实例:
def outer(x):
    # 嵌套函数inner(),则是一个闭包函数
    def inner(y):
        return x + y   # 引用外部函数的变量
    return inner
print(outer(6)(5))
def myfunc(a, b=1):
    c = 100
    def useC():
        print(f'调用外部函数的变量,并打印:{c}')   # 100
        print(a + b)    # 17
    useC()
myfunc(6, 11)   # 实参值会覆盖形参的值

装修器:

什么是装修器:

经过装修器函数,来修正原函数的一些功用,又不会改动原函数的内部完成,又使得原函数不需求修正。装修器自身能够增强其他函数的功用。

长处:

防止很多的重复代码;

效果:

1、将这个润饰符下面的函数作为该润饰符函数的参数传入。

2、假如被 @ 的函数是一个闭包函数,那么就把被润饰函数的参数传给闭包的内函数,结合方位参数列表 args 和可变参数字典 kwargs,就能够完成一切参数的接纳。

小结:

装修器的效果便是为了解耦一些通用处理或许不必要功用的,
尽可能让一个函数只担任一个任务,防止后续维护时散弹式修正代码。
代码实例:
import time
# 需求:打印2到20000的一切质数所需时刻
# 装修器
def runTime(func):    # 装修器,传入一个参数,把咱们要运转的函数 printNum() 作为参数传入
    def wrapper():    # 表明的是,要运转这个函数,需求运转哪些内容
        time1 = time.time()  # 运转这个函数前,截取一个时刻
        func()  # 然后运转咱们的函数
        time2 = time.time()  # 运转完毕,再获取一个当时时刻
        print(time2 - time1)  # 最终,打印总耗时
    return wrapper
# 判别是否为质数
def isPrime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True
@runTime
def printNum():   # 打印质数
    for i in range(2, 20000):
        if isPrime(i):
            print(i)
printNum()

对上述事例进行优化,核算一共有多少个质数?

import time
def runTime(func):  # 装修器,传入一个参数,把咱们要运转的函数 printNum() 作为参数传入
    def wrapper():  # 表明的是,要运转这个函数,需求运转哪些内容
        time1 = time.time()  # 运转这个函数前,截取一个时刻
        result = func()  # 然后运转咱们的函数,把记载个数的 countNum 记载并保存起来
        time2 = time.time()  # 运转完毕,再获取一个当时时刻
        print("Toal time:{:.4} s".format(time2 - time1))  # 然后,打印总耗时,保存4位小数
        return result  # 最终,回来记载个数的 countNum
    return wrapper
def isPrime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True
@runTime
def countPrintNum():
    countNum = 0  # 假如加上计数器,比方,计算2-20000之间有多少个质数
    for i in range(2, 10000):
        if isPrime(i):
            countNum += 1
    return countNum
countNum = countPrintNum()
print(countNum)

针对上述事例,持续优化,随意截取恣意之间两个人的质数个数:

import time
def runTime(func):  # 装修器,传入一个参数,把咱们要运转的函数 printNum() 作为参数传入
    def wrapper(*args):  # 表明的是,要运转这个函数,需求运转哪些内容,*args是接纳函数countPrintNum中的不定长参数
        time1 = time.time()  # 运转这个函数前,截取一个时刻
        result = func(*args)  # 然后运转咱们的函数(fun()等价于countPrintNum()),把记载个数的 countNum 记载并保存起来
        time2 = time.time()  # 运转完毕,再获取一个当时时刻
        print("Total time: {:.4} s".format(time2 - time1))  # 然后,打印总耗时,保存4位小数
        return result  # 最终,回来记载个数的 countNum
    return wrapper
def isPrime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True
@runTime
def countPrintNum(maxNum):  # 假如想要计算到恣意数之间的质数,顺便参数时,咱们需求把这个参数 maxNum 带到装修器中
    countNum = 0
    for i in range(2, maxNum):
        if isPrime(i):
            countNum += 1
            # print(i)
    return countNum
countNum = countPrintNum(100)    # 这儿能够传入大于2的恣意数
print(countNum)
解析:

代码中的 @runTime,相当于

countPrintNum = runTime(countPrintNum)
countPrintNum()

当运转最终的 countPrintNum() 函数时,调用进程是这样的:

  • 先履行 countPrintNum = runTime(countPrintNum),此刻的变量 countPrintNum 指向的是 runTime()
  • runTime(func)中传参的是 countPrintNum,回来的 wrapper,而 wrapper 又会调用到原函数 countPrintNum

所以,先履行 wrapper 函数里面的函数,然后再履行 countPrintNum()函数里的。