生成器、装饰器、迭代器

一、生成器

本来我们可以通过列表生成直接生成一个列表,为什么要用生成器呢?我们都知道列表的数据会被全部加在到内存,然后可以通过下标进行访问数据,如果我们要的数据量非常大呢,岂不是很占用内存,所以我们需要生成器,生成器呢是我们需要哪个数据就会生成哪个数据,也就是懒加载,只有当我们使用数据的时候才会被生成,这样就是一边循环,一边计算数据,这个就叫做生成器:generator。但是只能按顺序生成,因为会根据上一步的返回进行生成,这样我们就可以节约内存了。

接下来我们看一个小列子:生成列表和生成器的创建区别,创建Lg的区别仅在于最外层的[]()L是一个list,而g是一个generator

>>> l = [i*2 for i in range(10)]  #直接生成列表,会把数据全部加载
>>> l
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> g = (i*2 for i in range(10)) #生成一个生成器则会返回个内存地址,然后可以通过next方法一个一个的取值,如果很多后面的值不需要使用,我们就可以节约很多内存
>>> g
<generator object <genexpr> at 0x0000010E2E6A5DB0>
>>> next(g)
0
>>> next(g)
2
>>> next(g)
4
>>> next(g)
6
>>> next(g)
8
>>> next(g)
10>>> next(g)12>>> next(g)14>>> next(g)16>>> next(g)18>>> next(g)  #每次调用next方法,就会计算出下一个元素的值,直到计算出最后一个就会跑出StopIteration的异常Traceback (most recent call last):  File "<stdin>", line 1, in <module>StopIteration

当然除了用next获取生成器的值,我们还可以使用for循环来获取,所以我们不建议使用next方法,那个太麻烦了,使用for循环的话也不用担心取得元素超出范围抛出StopIteration异常:

>>> g = (x **2 for x in range(5))
>>> g
<generator object <genexpr> at 0x0000010E2E6A5570>
>>> for i in g:
...  print(i)
...
0
1
4
9
16

当然除了使用类似于生成列表的方式生成生成器,我们还可以使用函数来生成,我们都知道斐波拉契数列,除了前面两个元素,每个值都是前两个的值相加得到。

>>> def fib(n):
...  i,a,b = 0,0,1
...  while i<n:
...   print(b)
...   a,b = b,a+b
...   i += 1
...  return "done"
...
>>> fib(5)
1
1
2
3
5

如上就是斐波拉契数列的非递归式代码,那么我们如何把这个函数变成一个生成器呢,从代码看来,我们产生的数据都是b,所以我们只要在输出b的代码行稍加更改动就可以。

>>> def fib(n):
...  i,a,b = 0,0,1
...  while i<n:
...    #print(b)
      yield b   #修改成yield  b即可
...   a,b = b,a+b
...   i += 1
...  return "done"
...>>> fib(5)<generator object fib at 0x0000010E2E6A5D00

就是这么神奇,这就是generator的另一种生成方法,如果一个函数中包含yield关键字,那么函数不再是一个普通的函数了,而是一个generator,不考虑多线程,我们都知道函数是按顺序执行的,但是generator是不一样的,在每次调用next()的时候,遇到yield就会返回,再一次执行从上次返回的yield语句处执行。

def fib(n):
    i, a, b = 0, 0, 1
    while(i<n):
        yield b
        a, b = b, a+b
        i += 1
    return ‘done‘

data = fib(5)
print(data)
print(data.__next__())
print(data.__next__())
print("我来打断一下")
print(data.__next__())

#结果如下
<generator object fib at 0x000002766915D6D0>
1
1
我来打断一下
2

用for循环调用generator时,我们无法获取generator的返回值,如果想要获取到返回值,则要捕获StopIteration异常,返回值包含在StopIteration的value中

data = fib(5)
while True:
    try:
        x = next(data)
        print("data=",x)
    except StopIteration as e:
        print("generator return value:",e.value)
        break

#程序结果是
data= 1
data= 1
data= 2
data= 3
data= 5
generator return value: done #这样我们就可以获取到这个返回值

二、装饰器

接下来我们聊聊装饰器,从字面意思来看是一个装饰的效果,在java中也有装饰模式,简单来说装饰器就是用来给函数添加新功能的,也就是函数功能的强化器,但是,装饰器在装饰函数的时候需要遵循几个原则:

1、不能改变原函数的代码

2、不能改变原函数的调用方式

3、不能改变原函数原有的结果

在聊装饰器前我们简单了解下高阶函数和嵌套函数:

那什么是高阶函数呢?我们可以这么理解,就是把一个函数a作为另一个函数b的参数进行传递,把函数当做变量来传递(我们可以理解函数即‘变量’),则函数b则是高阶函数:

def func(x, y, f):
    print(f(x)+f(y))

func(-3,9,abs)

#执行结果
12

那什么是嵌套函数呢?嵌套函数并不是一个函数里面调用另一个函数,而是在定义函数的时候在其里面再定义函数的形式:

def func(x):
    def func1(y):
        print(y)
    func1(x)

func(4)
#执行结果是
4

好了,现在进去主题,聊聊我们的装饰器,来看一段代码(使用高阶函数)

import time
def bar(): #原函数,也就是要被装饰的函数
    time.sleep(1)
    print(‘in the bar‘)

def test(f):
    start_time=time.time()
    f()
    stop_time=time.time()
    print("the f run time is %s" %(stop_time-start_time))

bar()
print("分割线".center(50,‘-‘))
test(bar)

#程序结果如下
in the bar
-----------------------分割线------------------------
in the bar
the f run time is 1.0021047592163086

Process finished with exit code 0

从上面结果看来,我们确实增强了函数bar的功能,但是不符合装饰的一个原则,改变了调用方式,接下来我们处理这个问题。当然我们也可以使用bar=test(bar)的方式重新给了bar函数,也可以通过bar()的方式调用,也能达到了装饰的效果,但是这样我们没法传参数,接下来我们看一段代码(嵌套函数出场了)

import time#其实就是利用嵌套函数+高阶函数连用
def timer(func): #timer(test1)  func=test1
    def deco(*args,**kwargs): #通过可变参数解决相对应的原函数传来的参数问题,这样就很灵活
        start_time=time.time()
        func(*args,**kwargs)   #run test1()
        stop_time = time.time()
        print("the func run time  is %s" %(stop_time-start_time))
    return deco
@timer  #这个相当于test1=timer(test1)
def test1():
    time.sleep(1)
    print(‘in the test1‘)

@timer # test2 = timer(test2)  = deco  test2(name) =deco(name)
def test2(name,age):
    print("test2:",name,age)

test1()
test2("HZhuizai",22)
#程序执行结果如下
in the test1
the func run time  is 1.0001072883605957
test2: HZhuizai 22
the func run time  is 0.0

是不是这个装饰器很完美了,符合了三个原则,但是如果我们有一个需求就是对不同的两个函数有不一样的装饰呢,那我们该怎么实现呢?

import time
user,passwd = ‘alex‘,‘abc123‘
def auth(auth_type):
    def outer_wrapper(func):
        def wrapper(*args, **kwargs):
            print("wrapper func args:", *args, **kwargs)            func()
            if auth_type == "local":
                print("local 模式验证")
            elif auth_type == "ldap":
                print("ldap 模式验证")
        return wrapper
    return outer_wrapper

@auth(auth_type="local") # home = wrapper()
def home():
    print("welcome to home  page")
    return "from home"

@auth(auth_type="ldap")
def bbs():
    print("welcome to bbs  page")

home() #wrapper()
print("分隔符".center(50,‘-‘))
bbs()

#程序执行结果
wrapper func args:welcome to home  pagelocal 模式验证-----------------------分隔符------------------------wrapper func args:welcome to bbs  pageldap 模式验证

为了能够通过装饰器传递参数,我们又在外层套了一个函数,可以归纳出装饰就是高阶函数和嵌套函数的组合,第一层为装饰次传递参数,第二层是高阶函数,把要装饰的函数作为变量传递进去,第三层则是我们原函数的参数,这样我们能完美的装饰原函数,挺高函数的功能。

三、迭代器

我们知道可以作用于for循环的数据有列表、字典、元组、集合、字符串,这些都是集合类型,当然还有我们的生成器也可以作用于for循环,这些可以直接作用于for循环的对象统称为迭代对象:Iterable,我们可以使用isinstance()判断一个对象是否是Iterable对象。

>>> from collections import Iterable
>>> isinstance([],Iterable)
True
>>> isinstance("",Iterable)
True

可以被next()函数调用并且不断返回下一个值的对象为迭代器:Iterator,同样可以使用isinstance()判断一个对象是否是Iterator对象。

>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([x for x in range(10)], Iterator)
False

列表、字典、元组、集合、字符串都是Iterable对象,我们可以通过iter()方法把Iterable对象转换成Iterator对象

>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter(‘abc‘), Iterator)
True

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

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

归纳下:

凡是可作用于for循环的对象都是Iterable类型;

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

时间: 2024-10-18 19:13:33

生成器、装饰器、迭代器的相关文章

函数嵌套 ,名称空间与作用域 ,闭包函数 ,装饰器 ,迭代器, 生成器 三元表达式,列表解析,生成器表达式 递归与二分法, 内置函数

函数嵌套名称空间与作用域闭包函数装饰器迭代器生成器三元表达式,列表解析,生成器表达式递归与二分法内置函数--------------------------------------------函数的嵌套调用:在调用一个函数的过程中,又调用了其他函数函数的嵌套定义:在一个函数的内部,又定义另外一个函数def max(x,y): if x>y: return x else: return ydef max1(a,b,c,d): res=max(a,b) res2=max(res,c) res3=ma

Python 迭代器&amp;生成器,装饰器,递归,算法基础:二分查找、二维数组转换,正则表达式,作业:计算器开发

本节大纲 迭代器&生成器 装饰器  基本装饰器 多参数装饰器 递归 算法基础:二分查找.二维数组转换 正则表达式 常用模块学习 作业:计算器开发 实现加减乘除及拓号优先级解析 用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式后,必须自己解析里面的(),+,-,*,/符号和公式,运算后得出结果,结果必须与真实的计算器所得出的结果一致 迭代器&

python_day04 函数嵌套 名称空间和作用域 闭包 装饰器 迭代器 生成器 列表解析 三元表达式 生成器表达式

本节课重要知识点内容如下: 函数嵌套 名称空间和作用域 闭包 装饰器 迭代器 生成器 列表解析 三元表达式 生成器表达式 1.函数嵌套 函数的嵌套调用:在调用一个函数的过程中,又调用了其他函数函数的嵌套定义:在一个函数的内部,又定义另外一个函数 def bar(): print('from nbar')def foo(): print('from foo') bar()foo()def max2(x,y): if x > y: return x else: return ydef max4(a,

python 迭代器 生成器 装饰器

迭代器 可以直接作用于for循环的对象统称为可迭代对象(Iterable). 可以被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator). 所有的Iterable均可以通过内置函数iter()来转变为Iterator. names = iter(['sun', 'ibm', 'sunny']) print(names) print(names.__next__()) print(names.__next__()) print(names.__next__()) print(

&lt;04day&gt;_函数嵌套--闭包函数--装饰器--迭代器--生成器

一.函数的嵌套定义 1.python函数支持嵌套 def f1(): #f1函数的定义 def f2(): #f2函数的定义 print('from f2') def f3(): #f3函数的定义 print('from f3') f2() f1() 嵌套函数--运行结果说明: 1首先调用f1()结果,f1函数为空.担保函f2函数,f2函数有内容打印并且有调用,f2函数包含f3函数,但f3函数无调用. 运行结果: 列子:多个数据之间的大小比较. #!/usr/bin/python # -*- c

python学习笔记(5)--迭代器,生成器,装饰器,常用模块,序列化

生成器 在Python中,一边循环一边计算的机制,称为生成器:generator. 如: 1 >>> g = (x * x for xin range(10)) 2 >>> g3 <generator object <genexpr> at 0x1022ef630> 此处g就是一个生成器. 迭代器 我们已经知道,可以直接作用于for循环的数据类型有以下几种: 一类是集合数据类型,如list.tuple.dict.set.str等: 一类是gene

函数+装饰器+迭代器+生成器

闭包函数 闭包:定义在内网函数,包含对外部作用域而非全局作用域 范围:一个函数套用1或n个函数 from urllib.request import urlopen #urlopen模块 作用:爬网页 #闭包函数,内部get函数调用外部page函数 def page(url): #调用url def get(): #下载 return urlopen(url).read() #爬网页 return get #返回url值 baidu=page("http://www.baidu.com"

day4装饰器-迭代器&amp;&amp;生成器

一.装饰器 定义:本质是函数,(装饰其他函数)就是为其它函数添加附加功能 原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 实现装饰器知识储备: 1.函数及“变量” 2.高阶函数 a.把一个函数名当做实参传给另一个函数(在不修改被装饰器函数源代码的情况下为其添加新功能) b.返回值中包含函数名 3.嵌套函数 高阶函数+嵌套函数 = 装饰器 延迟3秒 import time def test1(): time.sleep(3) print('in the test1')

python第四天装饰器+迭代器+生成器

1.函数嵌套:在调用一个函数的过程中,调用了其他函数 def f1(): x=1 def f2(): print('from f2') f2() f1()  2.名称空间与作用域 a. 名称空间:存放名字与变量值绑定关系的地方 (1)内置名称空间:在python解释器启动时产生,存放一些python内置的名字 (2)全局名称空间:在执行文件时产生,存放文件级别定义的名字. (3)局部名称空间:在执行过程中,如果调用了该函数则会产生该函数的局部名称空间.在调用该函数的时候生效,调用结束时失效 加载

迭代器/生成器/装饰器

迭代器 迭代器对象要求支持迭代器协议的对象,在Python中,支持迭代器协议就是实现对象的__iter__()和next()方法.其中__iter__()方法返回迭代器对象本身:next()方法返回容器的下一个元素,在结尾时引发StopIteration异常 可迭代对象 如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration),默认的list.tuple.stri.dict对象都是可以迭代的. isinstance(obj