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

装饰器

  • 不修改被装饰对象的源代码
  • 不修改被装饰对象的调用方式
  • 被装饰函数的正上方,单独一行

1、无参装饰器

import time
def timer(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
 
@timer    #把@timer放到foo()函数的定义处,至关于执行了foo=timer(foo)
def foo():
    time.sleep(3)
    print('from foo')
foo()
"""执行结果
from foo
run time is 3.000171661376953
"""
无参装饰器

2、有参装饰器

def timer2(ms = 'file'):
    def timer(func):
        def wrapper(*args, **kwargs):
            if ms == 'file':
                name = 'file_name'
                res = func(*args, **kwargs)
                print('%s login successful'%name)
                return res
            elif ms == 'mysql':
                name = 'mysql_name'
                res = func(*args, **kwargs)
                print('%s login successful'%name)
                return res
            else:
                name = 'other_name'
                res = func(*args, **kwargs)
                print('%s login successful'%name)
                return res
        return wrapper
    return timer
 
@timer2(ms = 'file')  #timer2(ms = 'file')返回timer函数引用 就和无参的同样了
def foo(name):
    print('from foo %s'%name)
foo('rose')
"""
from foo rose
file_name login successful
"""
有参装饰器

3、多装饰器

  • 加载顺序(outter函数的调用顺序):自下而上
  • 执行顺序(wrapper函数的执行顺序):自上而下
# 叠加多个装饰器
# 1. 加载顺序(outter函数的调用顺序):自下而上
# 2. 执行顺序(wrapper函数的执行顺序):自上而下

def outter1(func1):  # func1=wrapper2的内存地址
    print('加载了outter1')

    def wrapper1(*args, **kwargs):
        print('执行了wrapper1')
        res1 = func1(*args, **kwargs)
        return res1

    return wrapper1


def outter2(func2):  # func2=wrapper3的内存地址
    print('加载了outter2')

    def wrapper2(*args, **kwargs):
        print('执行了wrapper2')
        res2 = func2(*args, **kwargs)
        return res2

    return wrapper2


def outter3(func3):  # func3=最原始的那个index的内存地址
    print('加载了outter3')

    def wrapper3(*args, **kwargs):
        print('执行了wrapper3')
        res3 = func3(*args, **kwargs)
        return res3

    return wrapper3

# 被装饰函数的正上方,单独一行
@outter1  # outter1(wrapper2的内存地址)======>index=wrapper1的内存地址
@outter2  # outter2(wrapper3的内存地址)======>wrapper2的内存地址
@outter3  # outter3(最原始的那个index的内存地址)===>wrapper3的内存地址
def index():           # index=outter1(outter2(outter3(index)))
    print('from index')


print('==========================')
index()
"""
加载了outter3
加载了outter2
加载了outter1
==========================
执行了wrapper1
执行了wrapper2
执行了wrapper3
from index
"""
多个装饰器

迭代器

1、为什么要有迭代器?

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

2、什么是可迭代对象iterable?

可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,以下sql

  • 'hello'.__iter__
  • (1,2,3).__iter__
  • [1,2,3].__iter__
  • {'a':1}.__iter__
  • {'a','b'}.__iter__
  • open('a.txt').__iter__

3、什么是迭代器对象iterator?

  • 内置有__iter__方法的对象就是可迭代对象。
  • 内置有__iter__方法和__next__方法的对象就是迭代器对象。
  • 可迭代对象执行obj.__iter__()获得的结果就是迭代器对象。
  • 迭代器对象指的是即内置有__iter__又内置有__next__方法的对象。
  • 迭代器对象.__iter__()后仍然是迭代器对象自己,文件类型是迭代器对象open('a.txt').__iter__()open('a.txt').__next__()
  • 迭代器遵循迭代器协议 :必须拥有__iter__方法和__next__方法。
  • for循环就是基于迭代器协议提供了一个统一的能够遍历全部对象的方法,即在遍历以前,先调用对象的__iter__方法将其转换成一个迭代器,而后使用迭代器协议去实现循环访问,这样全部的对象就均可以经过for循环来遍历了

"""
#优势:
  - 提供一种统一的、不依赖于索引的迭代方式
  - 惰性计算,节省内存
#缺点:
  - 没法获取长度(只有在next完毕才知道到底有几个值)
  - 一次性的,只能日后走,不能往前退,迭代完,就不能再次迭代
"""
dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #获得迭代器对象,迭代器对象即有__iter__又有__next__  迭代器对象.__iter__()后仍然是迭代器对象自己
print(iter_dic) #<dict_keyiterator object at 0x0000000000BA99F8>
print(iter_dic.__iter__()) #<dict_keyiterator object at 0x0000000000BD99F8>
print(iter_dic.__iter__() is iter_dic) #True  迭代器对象.__iter__()后仍然是迭代器对象自己
 
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
# print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志
 
#有了迭代器,咱们就能够不依赖索引迭代取值了
iter_dic=dic.__iter__()
while 1:
    try:
        k=next(iter_dic)
        print(k,dic[k])
    except StopIteration:   # 须要咱们本身捕捉异常
        break
 
#基于for循环,咱们能够彻底再也不依赖索引去取值了
dic={'a':1,'b':2,'c':3}
for k in dic:
    print(k, dic[k])
 
print('123123213')
for k in dic:
    print(k, dic[k])
 
#for循环的工做原理
#1:执行in后对象的dic.__iter__()方法,获得一个迭代器对象iter_dic
#2: 执行next(iter_dic),将获得的值赋值给k,而后执行循环体代码
#3: 重复过程2,直到捕捉到异常StopIteration,结束循环
迭代器
dir([1,2].__iter__())#是列表迭代器中实现的全部方法,
dir([1,2])           #是列表中实现的全部方法,都是以列表的形式返回,为了看的更清楚,分别把他们转换成集合,而后取差集。

# print(dir([1,2].__iter__()))   # 返回一个列表
# print(dir([1,2]))
print(set(dir([1,2].__iter__()))-set(dir([1,2])))  #{'__length_hint__', '__next__', '__setstate__'}

#列表迭代器中多了三个方法
iter_l = [1,2,3,4,5,6].__iter__()
#获取迭代器中元素的长度
print(iter_l.__length_hint__())   #6
#根据索引值指定从哪里开始迭代
print('*',iter_l.__setstate__(4))  #* None
#一个一个的取值
print('**',iter_l.__next__())      #** 5
print('***',iter_l.__next__())     #*** 6


print('__next__' in dir(range(12)))  #False
print('__iter__' in dir(range(12)))  #True

from collections import Iterator
print(isinstance(range(100000000),Iterator))  #  False  验证range执行以后获得的结果不是一个迭代器
迭代器方法
from collections import Iterable,Iterator
class Foo:
    def __init__(self,start):
        self.start=start

    def __iter__(self):
        return self

    def __next__(self):
        return 'aSB'
f=Foo(0)
print(isinstance(f,Iterable)) #True 可迭代
print(isinstance(f,Iterator)) #True 迭代器
Iterable,Iterator

生成器Generator

  • 迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象经过执行 iter方法获得的
  • 迭代器有的好处是能够节省内存。
  • 若是在某些状况下,咱们也须要节省内存,就只能本身写。本身写的这个能实现迭代器功能的东西就叫生成器。
  • 生成器本质:迭代器 ( 因此自带了 __iter__方法和 __next__方法,不须要咱们去实现)
  • 特色:惰性运算,开发者自定义
  • 生成器本质上就是个迭代器,咱们根据本身的想法创造的迭代器,支持for循环

1、生成器表达式

  • 把列表解析的[]换成()获得的就是生成器表达式
  • 列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
  • Python不但使用迭代器协议,让for循环变得更加通用。大部份内置函数,也是使用迭代器协议访问对象的。
"""
生成器表达式:
优势:省内存,一次只产生一个值在内存中
把列表推导式的[]换成()就是生成器表达式
"""
gen_exp = (i for i in range(10))  #生成器表达式
print(gen_exp) #<generator object <genexpr> at 0x0000000000A1DEB8>
# for i in gen_exp:  #取出生成器表达式的值,for循环
#     print(i)
print(gen_exp.__next__()) #next方法
print(gen_exp.__next__())
print(gen_exp.__next__())
生成器表达式
egg_list=['鸡蛋%s' %i for i in range(10)] #列表解析
print(egg_list)  #['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']

laomuji=('鸡蛋%s' %i for i in range(10))#生成器表达式
print(laomuji)  #<generator object <genexpr> at 0x0000000000BA3938>
print(next(laomuji)) #鸡蛋0    next本质就是调用__next__
print(laomuji.__next__())   #鸡蛋1
print(next(laomuji))   #鸡蛋2

#sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,因此,能够直接这样计算一系列值的和:
print(sum([x ** 2 for x in range(4)]))  #14
#而不用画蛇添足的先构造一个列表:
print(sum(x ** 2 for x in range(4)))    #14


###################################
def demo():
    for i in range(4):
        yield i

g=demo()

g1=(i for i in g)
g2=(i for i in g1)

print(list(g1),type(g1))  #[0, 1, 2, 3] <class 'generator'>
print(list(g2))  #[]

###################################
def demo():
    for i in range(4):
        yield i

g=demo()

g1=[i for i in g]
g2=(i for i in g1)

print(list(g1),type(g1))  #[0, 1, 2, 3] <class 'list'>
print(list(g2))  #[0, 1, 2, 3]
print(list(g2))  #[]
View Code

2、生成器函数

  • 一个包含yield关键字的函数就是一个生成器函数。
  • yield和return同样能够从函数中返回值,可是yield又不一样于return,return的执行意味着程序的结束,只能返回一次,yield能够返回屡次。
  • 调用生成器函数不会获得返回的具体的值,而是获得一个生成器对象。
  • 每一次从这个可迭代对象获取值,就能推进函数的执行,获取新的返回值。直到函数执行结束(yield像是拥有可以让函数暂停的魔力)。
  • 生成器有什么好处呢?就是不会一会儿在内存中生成太多数据
"""
生成器函数:
只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,而且不会执行函数内部代码
生成器就是迭代器(执行函数获得生成器)
"""
 
 
def product():
    for i in range(1, 3):
        print("开始生产包子")
        yield "第 %d 屉包子" % (i)
        print("卖包子,买完再生产")
 
 
pro = product()  # 生成一个作包子的生成器,至关于作包子的
print(pro)
p = print(pro.__next__())  # 卖包子的
print('===')
print(pro.__next__())
print('============')
for i in pro:
    print(i)
 
"""
<generator object product at 0x00000000006DDEB8>
开始生产包子
第 1 屉包子
===
卖包子,买完再生产
开始生产包子
第 2 屉包子
============
卖包子,买完再生产
"""
 
def product():
    for i in range(1,3):
        print("开始生产包子")
        yield  "第 %d 屉包子" %(i)
        print("卖包子,买完再生产")
pro = product()  #生成一个作包子的生成器,至关于作包子的
 
while 1:
    try:
        k=next(pro)
        print(k)
    except StopIteration:   # 须要咱们本身捕捉异常
        break
生成器函数
#生成器函数
def my_range():
    print('我是一个生成器函数')
    n = 0
    while 1:
        yield n
        n += 1

#实现开始和结束
def my_range2(start, stop):
    n = start
    while n < stop:
        yield n
        n += 1

#再进一步,实现步长:
def my_range3(start, stop, step):
    n = start
    while n < stop:
        yield n
        n += step

#生成器本质上就是个迭代器,咱们根据本身的想法创造的迭代器,它固然也支持for循环:
for i in my_range3(1, 10, 2):
    print(i)
my_range
import time


def tail(filename):
    f = open(filename)
    f.seek(0, 2) #从文件末尾算起
    while True:
        line = f.readline()  # 读取文件中新的文本行
        if not line:
            time.sleep(0.1)
            continue
        yield line

tail_g = tail('tmp')
for line in tail_g:
    print(line)
生成器监听文件输入的例子
def gen1():
    for c in 'AB':
        yield c
    for i in range(3):
        yield i

print(gen1())   #['A', 'B', 0, 1, 2]

def gen2():
    yield from 'AB'
    yield from range(3)

print(list(gen2()))  #['A', 'B', 0, 1, 2]
在一个生成器中引用另一个生成器。
import pickle
class Course:
    def __init__(self,name,price,period,teacher):
        self.name = name
        self.price = price
        self.period = period
        self.teacher = teacher

def get_course():
    with open('course_info', 'rb') as f:
        while True:
            try:
                course_obj = pickle.load(f)
                yield course_obj
            except EOFError:
                break

cour=get_course()
print(cour)
print(cour.__iter__())
print(cour.__next__())
print(cour.__next__())

for obj in cour:
    print(obj.name)


############################

def get_line():
    with open('a.txt', encoding='utf-8', mode='r') as f:
        while True:
            try:
                line = f.readline()
                yield line
            except StopIteration:
                break
    g=get_line()
    print(g)
    print(g.__next__())
    print(g.__next__())
    print(g.__next__())
生成器-文件

3、协程函数

  • yield能够返回值,也能够接收值。
  • 经过生成器的send方法能够给yield传值。
  • send两个做用:1.给yield传值 2.继续执行函数
"""
协程函数:
yield关键字的另一种使用形式:表达式形式的yield
对于表达式形式的yield,在使用时,第一次必须传None,g.send(None)等同于next(g)
 
yield总结
一、把函数作成迭代器
二、对比return,能够返回屡次值,能够挂起/保存函数的运行状态
"""
def eater(name):
    print('%s 准备开始吃饭啦' % name)
    food_list = []
    for i in range(4):
        food = yield food_list
        print('%s 吃了 %s' % (name, food))
        food_list.append(food)
        print('while', food_list)
    # print('eater',food_list)   ####这的代码不会执行,尚未走到这程序就退出了
 
 
g = eater('tom')
print(g)
g.send(None)  # 对于表达式形式的yield,在使用时,第一次必须传None,g.send(None)等同于next(g)
print('====')
 
g.send('包子')
g.send('米饭')
g.send('面条')
 
try:
    g.send('稀饭')
except StopIteration:
    g.close() #依旧执行后面的print(g)
    # quit() #后面的print(g)就不执行了
try:
    g.send('肉夹馍')
except StopIteration:
    g.close()
 
print(g)
 
"""
<generator object eater at 0x0000000000BA1BF8>
tom 准备开始吃饭啦
====
tom 吃了 包子
while ['包子']
tom 吃了 米饭
while ['包子', '米饭']
tom 吃了 面条
while ['包子', '米饭', '面条']
tom 吃了 稀饭
while ['包子', '米饭', '面条', '稀饭']
<generator object eater at 0x0000000000BA1BF8>
"""
 
# 单线程一边发送,一边执行
import time
 
 
def consumer(name):
    print("%s 准备吃包子啦!" % name)
    while True:
        food = yield
        print("包子[%s]来了,被[%s]吃了!" % (food, name))
 
 
def producer(name):
    c = consumer('顾客A')
    c2 = consumer('顾客B')
    c.__next__()
    c2.__next__()
    print("老板%s==开始准备作包子啦!" % name)
    for i in range(3):
        time.sleep(1)
        print("作了2个包子!")
        c.send(i)  # 发送的值,就是yield的返回值
        c2.send(i)
 
 
producer("tom")
print('===========')
producer("jack")
 
"""
顾客A 准备吃包子啦!
顾客B 准备吃包子啦!
老板tom==开始准备作包子啦!
作了2个包子!
包子[0]来了,被[顾客A]吃了!
包子[0]来了,被[顾客B]吃了!
作了2个包子!
包子[1]来了,被[顾客A]吃了!
包子[1]来了,被[顾客B]吃了!
作了2个包子!
包子[2]来了,被[顾客A]吃了!
包子[2]来了,被[顾客B]吃了!
===========
顾客A 准备吃包子啦!
顾客B 准备吃包子啦!
老板jack==开始准备作包子啦!
作了2个包子!
包子[0]来了,被[顾客A]吃了!
包子[0]来了,被[顾客B]吃了!
作了2个包子!
包子[1]来了,被[顾客A]吃了!
包子[1]来了,被[顾客B]吃了!
作了2个包子!
包子[2]来了,被[顾客A]吃了!
包子[2]来了,被[顾客B]吃了!
"""
协程函数
# def eat(name):
#     print('%s要开始吃了!' % name)
#     while 1:
#         food = yield
#         print('{}在吃{}'.format(name, food))
#
#
# a = eat('alex')
# a.__next__()  # 初始化,让函数暂停在yield处
# a.send('包子')  # send两个做用:1.给yield传值 2.继续执行函数
# a.send('饺子')

def init(func):  #在调用被装饰生成器函数的时候首先用next激活生成器
    def inner(*args,**kwargs):
        g = func(*args,**kwargs)
        next(g)
        return g
    return inner

@init
def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield average
        total += term
        count += 1
        average = total/count


g_avg = averager()
# next(g_avg)   在装饰器中执行了next方法
print(g_avg.send(10))  #10.0
print(g_avg.send(30))  #20.0
print(g_avg.send(5))   #15.0
  
协程的装饰器
相关文章
相关标签/搜索