博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python基础4 迭代器、装饰器、软件开发规范
阅读量:5369 次
发布时间:2019-06-15

本文共 5864 字,大约阅读时间需要 19 分钟。

 1.列表生成式,迭代器&生成器

 现在有个需求,看列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],我要求你把列表里的每个值加1

 

a= [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]#方法一:for i in range(len(a)):    a[i]+=100#方法二:for index ,i in enumerate(a):    a[index] = i+100#方法三:a = map(lambda x:x+100,a)  #map(),Python 2.x 返回列表。Python 3.x 返回迭代器。a = list(a)                #所以要强制转化一下#方法四: 该方法为列表生成式a = [i+100 for i in a]print(a)

生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

 

L = [x * x for x in range(10)]print(L)#输出:[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]g = (x * x for x in range(10))  #g是个迭代器print(g)#输出:
at 0x000001DF01888360>l = list(g) #g为迭代器强制转成列表print(l)#输出:[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]#迭代器访问方式for n in g: print (n)

 

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

 

def fib(n):    count,a,b = 0,0,1    while count

fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

#斐波拉契函数import sysdef fib(n):    count,a,b = 0,0,1    while count
for i in g: print(i)#输出11235813213455

这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:

def fib(n):    count,a,b = 0,0,1    while True:        print("start")        yield b        print("end")        a,b = b,a+b  #相当于 t = (b,a+b) a = t[0],b=t[1]# fib(10)g = fib(10)print(g.__next__())#输出:# start# 1print(g.__next__())#输出# end# start# 1

这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

 

还可通过yield实现在单线程的情况下实现并发运算的效果

import timedef consumer(name):    # print("%s 准备吃包子啦!" %name)    while True:       baozi = yield       print("包子[%s]来了,被[%s]吃了!" %(baozi,name))def producer(name):    c = consumer('A')    c2 = consumer('B')    c.__next__()    c2.__next__()    print("老子开始准备做包子啦!")    for i in range(3):        time.sleep(1)        print("做了1个包子,分两半!")        c.send(i)  #重新获取yield,并发送一个值        c2.send(i)producer("Lisi")#输出# 老子开始准备做包子啦!# 做了1个包子,分两半!# 包子[0]来了,被[A]吃了!# 包子[0]来了,被[B]吃了!# 做了1个包子,分两半!# 包子[1]来了,被[A]吃了!# 包子[1]来了,被[B]吃了!# 做了1个包子,分两半!# 包子[2]来了,被[A]吃了!# 包子[2]来了,被[B]吃了!

迭代器

我们已经知道,可以直接作用于for循环的数据类型有以下几种:

一类是集合数据类型,如listtupledictsetstr等;

一类是generator,包括生成器和带yield的generator function。

这些可以直接作用于for循环的对象统称为可迭代对象:Iterable

可以使用isinstance()判断一个对象是否是Iterable对象:

>>> from collections 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

而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

*可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator

可以使用isinstance()判断一个对象是否是Iterator对象:

 

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

 

生成器都是Iterator对象,但listdictstr虽然是Iterable,却不是Iterator

listdictstrIterable变成Iterator可以使用iter()函数:

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

为什么listdictstr等数据类型不是Iterator

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

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

小结

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

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

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

Python的for循环本质上就是通过不断调用next()函数实现的,例如:

for x in [1, 2, 3, 4, 5]:    pass

实际上完全等价于:

# 首先获得Iterator对象:it = iter([1, 2, 3, 4, 5])# 循环:while True:    try:        # 获得下一个值:        x = next(it)    except StopIteration:        # 遇到StopIteration就退出循环        break

 

 

2.装饰器

 

#写法import timedef timmer(func):    def deco(*args,**kwargs):     #传入非固定参数        start_time=time.time()        res = func(*args,**kwargs)      #传入非固定参数        stop_time=time.time()        print('the func run time is %s' %(stop_time-start_time))        return res    return deco@timmer   #相当于 test1 = timmer(test1) = deco ,test1() = deco()def test1():    time.sleep(1)    print('in the test1')@timmer  #相当于 test2 = timmer(test2) = deco ,test2(name) = deco(name)def test2(name):    time.sleep(1)    print('in the test1:%s'%name)    return "from test2"test1()test2("LISI")print(test2("LISI"))

 

#带参数的装饰器import timeuser,passwd = 'ZhangSan','abc123'def auth(auth_type):    print("auth func:",auth_type)    def outer_wrapper(func):        def wrapper(*args, **kwargs):            print("wrapper func args:", *args, **kwargs)            if auth_type == "local":                username = input("Username:").strip()                password = input("Password:").strip()                if user == username and passwd == password:                    print("\033[32;1mUser has passed authentication\033[0m")                    res = func(*args, **kwargs)  # from home                    print("---after authenticaion ")                    return res                else:                    exit("\033[31;1mInvalid username or password\033[0m")            elif auth_type == "ldap":                print("ldap verification")        return wrapper    return outer_wrapperdef index():    print("welcome to index page")@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")index()print(home()) #wrapper()bbs()

 

 

 

 

 

 

 

 

end

转载于:https://www.cnblogs.com/name2019/p/10780836.html

你可能感兴趣的文章
css-IE中的border-radius和box-shadow
查看>>
利用bootstrap和webform的异步CRUD及分页
查看>>
HDUOJ 1879继续畅通工程(并查集)
查看>>
OC12_自动释放池
查看>>
Saiku资源帖
查看>>
解决手机页面中点击文本框,网页放大问题
查看>>
2-5
查看>>
牛客多校3 A-PACM Team(状压降维+路径背包)
查看>>
HDU - 4284 Travel(floyd+状压dp)
查看>>
1027 制作表格
查看>>
Android之Socket通信、List加载更多、Spinner下拉列表
查看>>
面向对象的介绍与特性
查看>>
typing-python用于类型注解的库
查看>>
20189215 2018-2019-2 《密码与安全新技术专题》第13周作业
查看>>
第四周作业
查看>>
一、HTML基础
查看>>
蓝牙进阶之路 (002) - HC-05与HC-06的AT指令的区别(转)
查看>>
mysql的limit经典用法及优化
查看>>
C#后台程序与HTML页面中JS方法互调
查看>>
mysql 同一个表中 字段a 的值赋值到字段b
查看>>