python迭代器生成器装饰器_python迭代器,生成器和装饰器

生成器

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

所以我们不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,成为生成器:generator

创建list和generator的区别仅在于最外成的[]和()

直接打印出list的每一个元素;通过next()函数或者__next()__获取generator的下一个返回值

generator保存的是算法,每次调用next()就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

也可以使用for循环,因为generator也是可迭代对象;

g = (x * x for x in range(10))for n ing:print(n, end=",") #0,1,4,9,16,25,36,49,64,81,

所以我们创建一个generator后,基本上永远不会调用next(),而是通过for循环来迭代,并且不需要关心StopIteration的错误

通过使用yield关键字定义

生成器对象通过yield关键字定义的函数对象,因此,生成器也是一个函数。生成器用于一个值的序列,以便在迭代器中使用。

defmyYield(n):while n >0:print("开始生成...")yieldnprint("完成一次...")

n-= 1

if __name__ == '__main__':

test= myYield(3)for i intest:print(i)"""开始生成...

3

完成一次...

开始生成...

2

完成一次...

开始生成...

1

完成一次..."""

yield 语句是生成器中的关键语句,生成器在实例化时并不会被执行,而是等待调用其__next__()方法才开始运行。并且当程序运行完yield语句后就会“吼(hold)住”,即保持当前状态且停止运行,等待下一次遍历时才恢复运行。

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

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

deffib(max):

n, a, b= 0, 0, 1

while n

a, b= b, a+b

n+= 1fib(10) #1;1;2;3;5;8;13;21;34;55;

生成器的方式

deffib(max):

n, a, b= 0, 0, 1

while n

a, b= b, a+b

n+= 1a= fib(10)

b= [i for i ina]print(b) #[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

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

生成器使用实例

#encoding:utf-8

importtimeimportrandom

food= ["韭菜鸡蛋","猪肉白菜","猪肉荠菜","羊肉白菜","猪肉大葱","虾仁海鲜"]defconsumer(name):print("%s 准备吃包子啦!" %name)whileTrue:

baozi= yield "n"

print("[%s]馅包子来了,被[%s]吃了!" %(baozi, name))defproducer(name):

c1= consumer('大儿子')

c2= consumer('小儿子')

c1.__next__()

c2.__next__()print("%s开始准备做包子啦" %name)for i in range(6):print("第%d次做了%s个包子" % (i + 1, len(food)))

time.sleep(random.randint(1, 3))

f1=food[i]

c1.send(f1)

food.append(f1)#对列表随机排序

random.shuffle(food)

c2.send(food[i])

producer('老子')"""大儿子 准备吃包子啦!

小儿子 准备吃包子啦!

老子开始准备做包子啦

第1次做了6个包子

[韭菜鸡蛋]馅包子来了,被[大儿子]吃了!

[虾仁海鲜]馅包子来了,被[小儿子]吃了!

第2次做了7个包子

[猪肉大葱]馅包子来了,被[大儿子]吃了!

[虾仁海鲜]馅包子来了,被[小儿子]吃了!

第3次做了8个包子

[韭菜鸡蛋]馅包子来了,被[大儿子]吃了!

[韭菜鸡蛋]馅包子来了,被[小儿子]吃了!

第4次做了9个包子

[韭菜鸡蛋]馅包子来了,被[大儿子]吃了!

[虾仁海鲜]馅包子来了,被[小儿子]吃了!

第5次做了10个包子

[猪肉大葱]馅包子来了,被[大儿子]吃了!

[猪肉大葱]馅包子来了,被[小儿子]吃了!

第6次做了11个包子

[虾仁海鲜]馅包子来了,被[大儿子]吃了!

[猪肉大葱]馅包子来了,被[小儿子]吃了!"""

迭代器

可以直接作用于for循环的数据类型有以下几种

1:集合数据类型,如list,tuple,dict,set,str等

2:generator,包括生成器和带yield的generator frunction

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

list,dict,str虽然是Iterable,却不是Iterator

from collections importIterablefrom collections importIteratorprint(isinstance([],Iterator)) #False

print(isinstance([],Iterable)) #True

print(isinstance({},Iterable)) #True

print(isinstance('abc',Iterable)) #True

iter()函数 创建迭代器

iter(iterable)  #一个参数,要求参数为可迭代的类型

把list、dict、str等Iterable变成Iterator可以使用iter()函数:

from collections importIteratorprint(isinstance(iter([]), Iterator)) #True

print(isinstance(iter({}), Iterator)) #True

print(isinstance(iter('abc'), Iterator)) #True

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

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

小结

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

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

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

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

for x in [1, 2, 3, 4, 5]:print(x, end=",")print("\n")

it= iter([1, 2, 3, 4, 5])whileTrue:try:

x=next(it)print(x, end=",")exceptStopIteration:break

创建一个迭代器(类)

把一个类作为一个迭代器使用需要在类中实现两个方法__iter__()与__next__().

__iter__()方法返回一个特殊的迭代器对象,这个迭代器对象实现__next__() 方法并通过StopIteration异常标识迭代的完成。

from itertools importisliceclassFib:def __init__(self):

self.pre=0

self.curr= 1

def __iter__(self):returnselfdef __next__(self):

self.pre, self.curr= self.curr, self.pre+self.currreturnself.pre

f=Fib()print(list(islice(f, 0, 10))) #[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

内置迭代器工具

count无限迭代器

from itertools importcount

counter= count(start=10)print(next(counter)) #10

print(next(counter)) #11

cycle无限迭代器,从一个有限序列中生成无限序列

from itertools importcycle

counter= cycle([1, 2, 3])print(next(counter)) #1

print(next(counter)) #2

print(next(counter)) #3

print(next(counter)) #1

itertools的子模块islice控制无限迭代器输出的方式

islice的第二个参数控制何时停止迭代,从无限的序列中生成有限序列

from itertools importislice, countfor i in islice(count(10), 5):print(i)

装饰器

函数即“”变量“”

高阶函数

把一个函数名当做实参传给另一个函数

返回值中包含函数名

高阶函数+嵌套函数=装饰器

importtimedeftimer(func):def deco(*args, **kwargs):

start_time=time.time()

func(*args, **kwargs)

stop_time=time.time()print("the func run time is %s"%(stop_time -start_time))returndeco

@timerdeftest_1():

time.sleep(1)print("in the test_1")

@timerdeftest_2():

time.sleep(1)print("in the test_2")

test_1()

test_2()"""in the test_1

the func run time is 1.0200583934783936

in the test_2

the func run time is 1.0000574588775635"""

类装饰器

类装饰器具有灵活度大,高内聚,封装性等优点。使用类装饰器主要依靠类的__call__方法

classFoo():def __init__(self, func):

self._func=funcdef __call__(self, *args, **kwargs):print("class running")

self._func()print("class end")

@Foodefbar():print("bar")

bar()

装饰器可以把与业务逻辑无关的代码抽离出来,让代码保持干净清爽,而且装饰器还能被多个地方重复利用。比如一个爬虫网页的函数,如果该 URL 曾经被爬过就直接从缓存中获取,否则爬下来之后加入到缓存,防止后续重复爬取。

importurllib.request as urlibdefcache(func):

saved={}defwrapper(url):if url insaved:returnsaved[url]else:

page=func(url)

saved[url]=pagereturnpagereturnwrapper

@cachedefweb(url):return urlib.urlopen(url).read()

带参数的decorator

importfunctoolsdeflog(text):defdecorator(func):

@functools.wraps(func)def wrapper(*args, **kwargs):print("%s %s()" %(text, func.__name__))return func(*args, **kwargs)returnwrapperreturndecorator

@log("execute")defnow():print("2015-3-25")

now()print(now.__name__)

实例-登录认证

importfunctools

user, passwd= "test", "123456"

defauth(auth_type):defdecotator(func):

@functools.wraps(func)def wrapper(*args, **kwargs):if auth_type == "local":

username= input("请输入用户名:").strip()

password= input("请输入密码:").strip()if user == username and passwd ==password:print("\033[32;1mUser has passed authentication\033[0m")

res= func(*args, **kwargs)print("--after authentication--")returnreselse:

exit("\033[31;1mInvalid username or password\033[0m")elif auth_type == "ldap":

res= func(*args, **kwargs)print("ldap都不会")returnresreturnwrapperreturndecotatordefindex():print("welcome to index page")

@auth(auth_type="local")defhome():print("welcome to home page")

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

index()

home()

bbs()

转自: https://blog.csdn.net/sunchengquan/article/details/84494101

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值