Python全栈开发之4、迭代器、生成器、装饰器

一、迭代器

1、为何要有迭代器?

对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器

2、什么是可迭代对象?

可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下
'hello'.__iter__     #字符串
(1,2,3).__iter__   #元组
[1,2,3].__iter__   #列表
{'a':1}.__iter__   #字典
{'a','b'}.__iter__  #集合
open('a.txt').__iter__  #文件

3、什么是迭代器?

可迭代对象执行obj.__iter__()得到的结果就是迭代器对象
而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象
但是,listdictstr等可迭代对象并没有__next__()方法,所以它不是迭代器
可以把使用iter()函数把
listdictstr等可迭代对象变成迭代器
from collections import Iterator

a=isinstance([],Iterator)
b=isinstance((),Iterator)
c=isinstance({},Iterator)
d=isinstance('abc',Iterator)
e=isinstance((i for i in range(10)),Iterator)
f=isinstance(100,Iterator)
print(a)
print(b)
print(c)
print(d)
print(e)
print(f)

输出:
False
False
False
False
True
False
使用isinstance()判断一个对象是否是可迭代对象
str1='hello'    #字符串
tuple1=(1,2,3) #元组
list1=[1,2,3]  #列表
dict1={'a':1}  #字典
set1={'a','b'} #集合

#使用iter()方法把可迭代对象变成迭代器
iter_str=iter(str1)
iter_tuple=iter(tuple1)
iter_list=iter(list1)
iter_dict=iter(dict1)
iter_set=iter(set1)

print(iter_str.__next__())
print(iter_tuple.__next__())
print(iter_list.__next__())
print(iter_dict.__next__())
print(iter_set.__next__())
把list、dict、str等可迭代对象变成迭代器

4、for循环

#基于for循环,我们可以完全不再依赖索引去取值了
dic={'a':1,'b':2,'c':3}
for k in dic:
    print(dic[k])

#for循环的工作原理

#1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic
#2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码
#3: 重复过程2,直到捕捉到异常StopIteration,结束循环

5、迭代器的缺点

#优点:
  - 提供一种统一的、不依赖于索引的迭代方式
  - 惰性计算,节省内存
#缺点:
  - 无法获取长度(只有在next完毕才知道到底有几个值)
  - 一次性的,只能往后走,不能往前退

二、生成器

 1、什么是生成器?

可以理解为一种数据类型、这种数据类型实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__()方法才可以),所以生成器就是可迭代对象。

2、生成器在Python中的表现形式

1、生成器函数
常规函数定义、但是是使用yield语句而不是使用return返回结果
yield语句一次返回一个结果、在每个结果中间挂起函数的状态

2、生成器表达式
类似于列表推导、但是生成器返回按需产生一个对象而不是一次构建一个结果列表

3、列表生成式

现在有一个需求,需要把列表[0,1,2,3,4,5,6,7,8,9]里的每个元素都加1,怎么实现?有以下几种方式:
list1=[0,1,2,3,4,5,6,7,8,9]

for index,i in enumerate(list1):
    list1[index]=i+1
    
print(list1)     #原值修改

或者

a=[]
for i in range(10):
    a.append(i*2)
print(a)

列表生成式写法

num_list = [ i*2 for i in range(10) ]

#或者赋值
num_list = [ "数字%s"%i  for i in range(10) ]
print(num_list)

#输出:
['数字0', '数字1', '数字2', '数字3', '数字4', '数字5', '数字6', '数字7', '数字8', '数字9']

#结合三元表达式

num_list = [ "数字%s"%i  for i in range(10) if i > 5]

print(num_list)
#输出:
['数字6', '数字7', '数字8', '数字9']

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

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

在Python中,这种一边循环一边计算的机制,称为生成器:generator。

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

num_list = ("数字%s"%i  for i in range(10) if i > 5)

print(num_list.__next__())
print(num_list.__next__())
print(num_list.__next__())

#输出:
数字6
数字7
数字8

4、yield生成器函数

只要函数中存在yield,这个函数就是一个生成器函数

yield功能:1、相当于return一个返回值。2、保存函数的状态,以便下次从它离开的地方执行

def test():

    yield 1
    yield 2
    yield 3
    yield 4

num=test()
print(num.__next__())
print(num.__next__())
print(num.__next__())
print(num.__next__())
#输出:1
#     2
#     3
#     4
def fib(max):
    n,a,b=0,0,1
    while n<max:
        # print(b)
        yield b
        a,b=b,a+b
        n=n+1

gen=fib(10)
for i in gen:
    print(i)
yield实现斐波拉契数列
import time
def consumer(name):
    print("%s准备吃包子了"%name)
    while True:
        baozi=yield

        print('包子[%s]来了,被%s吃了'%(baozi,name))


# c1=consumer('lily')
# c1.__next__()
#
# c1.send('韭菜')

def producer(name):
    c=consumer('A')
    c2=consumer('B')
    c.__next__()
    c2.__next__()
    print('%s开始做包子了'%name)
    for i in range(10):
        time.sleep(1)
        print('做了两个包子')
        c.send(i)
        c2.send(i)

producer('Tom')
通过yield实现在单线程的实现并发运算
#yield相当于return控制的是函数的返回值
#x=yield的另外一个特性是,接收send()传过来的值赋值给yield

def func():
    print("yield来啦")

    name=yield

    print("yield接收send传参-%s"%name)
    yield 2
    print("下一步yield执行啦")


test=func()

print(test.__next__())  # 这一步打印的yield返回值是None

res=test.send('Tom')  #把Tom传给yield然后赋值给name,执行到yield 2返回  send相当于一次next操作

print(res)
yield-send()方法

 三、装饰器

定义:装饰器本质上是一个函数

功能:就是为其他函数添加附加功能

原则:1.不能修改被装饰的函数的源代码

          2.不能修改被装饰的函数的调用方式

#实现装饰器的知识:

#1.函数即变量 

#2.高阶函数:满足下面两个条件

        #a: 把一个函数名当做实参传给另外一个函数  (在不修改被装饰函数源代码的情况下为其添加功能)

        #b: 返回值中包含函数名(不改变函数的调用方式)

#3.嵌套函数

#高阶函数+嵌套函数+闭包 =装饰器
import time

def timer(func):
    def deco(*args,**kwargs):
        start_time=time.time()
        func(*args,**kwargs)       #会往上一级找
        end_time=time.time()
        print('the func run time is %s'%(end_time-start_time))
    return deco
@timer
def test1(name):
    time.sleep(3)
    print('in the test1')
    print(name)
@timer
def test2():
    time.sleep(3)
    print('in the test2')

#test1=timer(test1)
#test2=timer(test2)



test1('sunhao')
test2()
装饰器例子
import time

def timmer(func):  
    def wrapper():
        start_time=time.time()
        result=func()    #拿到func函数的返回值
        stop_time=time.time()
        print('the func run time is %s'%(stop_time-start_time))
     return result    #再返回
    return wrapper


def test():
    time.sleep(1)
    print('in the test1')


res=timmer(test)   #返回的是wrapper的地址   timmer(test) 相当于@timmer

res() #执行的是wrapper函数

#把timmer(test)赋值给test,再执行test(),就没修改被装饰的函数的调用方式 
import time
def timmer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    return wrapper

@timmer #相当于foo=timmer(foo)
def foo():
    time.sleep(3)
    print('from foo')


foo()
无参装饰器
user_list=[{'name':'allen','password':'123456'},
           {'name':'lucy','password':'123456'},
           {'name':'lily','password':'123456'}
           ]

current_user={'username':None,'login':False}

def auth_type(func):

    def wrapper(*args,**kwargs):

        if current_user['username'] and current_user['login']:

            ret = func(*args,**kwargs)
            return ret

        username=input("请输入用户名:").strip()
        password=input("请输入密码:").strip()
        for name in user_list:

            if name['name']== username and name['password'] == password:
                current_user['username']=username
                current_user['login']=True

                ret = func(*args, **kwargs)
                return ret

        else:
            print("用户名密码错误")


    return wrapper

@auth_type
def index(name):
    print("欢迎来%s购物商城"%name)

@auth_type
def shopping():

    print("shopping 吧")


index('京东')
shopping()
登录认证模拟session
user_list=[{'name':'allen','password':'123456'},
           {'name':'lucy','password':'123456'},
           {'name':'lily','password':'123456'}
           ]

current_user={'username':None,'login':False}

def outer(auth_type):


    def auth_func(func):

        def wrapper(*args,**kwargs):
            print("认证类型是%s"%auth_type)
            
            if auth_type =="file":
                if current_user['username'] and current_user['login']:

                    ret = func(*args,**kwargs)
                    return ret

                username=input("请输入用户名:").strip()
                password=input("请输入密码:").strip()
                for name in user_list:

                    if name['name']== username and name['password'] == password:
                        current_user['username']=username
                        current_user['login']=True

                        ret = func(*args, **kwargs)
                        return ret

                else:
                    print("用户名密码错误")

            elif auth_type =='mysql':

                ret = func(*args, **kwargs)
                return ret


        return wrapper

    return auth_func


@outer(auth_type='file')  #auth_func=outer(auth_type='filedb')
def index(name):
    print("欢迎来%s购物商城"%name)

@outer(auth_type='mysql')
def shopping():

    print("shopping 吧")


index('京东')
shopping()
有参装饰器
原文地址:https://www.cnblogs.com/sunhao96/p/7656453.html