生成器、装饰器、迭代器

2018-06-17 23:24:02来源:未知 阅读 ()

新老客户大回馈,云服务器低至5折

一、生成器

本来我们可以通过列表生成直接生成一个列表,为什么要用生成器呢?我们都知道列表的数据会被全部加在到内存,然后可以通过下标进行访问数据,如果我们要的数据量非常大呢,岂不是很占用内存,所以我们需要生成器,生成器呢是我们需要哪个数据就会生成哪个数据,也就是懒加载,只有当我们使用数据的时候才会被生成,这样就是一边循环,一边计算数据,这个就叫做生成器: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  page
local 模式验证
-----------------------分隔符------------------------
wrapper func args:
welcome to bbs  page
ldap 模式验证

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

三、迭代器

我们知道可以作用于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对象。

 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装

下一篇:基于docker 如何部署surging分布式微服务引擎