饮冰三年-人工智能-Python-16Python基础之迭代器、生成器、装饰器

一:闭包


  1.1 什么是闭包  

    闭包就是内层函数, 对外层函数(非全局)的变量的引⽤.  
def outerFun():
    name="Aaron"  # 外层函数outerFun 中的变量 name
    def innerFun():
        print(name) # 内层函数innerFun 引用了外层函数中的 name
    return innerFun

f1= outerFun()
f1()

# 输出结果   Aaron 
内层函数引用外层函数中的变量
def outerFun():
    nameList=[]  # 外层函数outerFun 中的变量 nameList
    def innerFun(name):
        nameList.append(name) # 内层函数innerFun 引用了外层函数中的 nameList,并对其进行修改
        print(nameList)
    return innerFun

f1= outerFun()
f1("")
f1("")
f1("")
f2= outerFun()
f2("Tom")
f1("")
f2("Tony")
'''
['赵']
['赵', '钱']
['赵', '钱', '孙']
['Tom']
['赵', '钱', '孙', '李']
['Tom', 'Tony']
'''
外部变量的变化
  • 结论:外部变量跟着各自的闭包走,可传递。但闭包之间不受影响
def outfun(namelist=[]):
    def inner(name):
        namelist.append(name)
        print(namelist)

    return inner


f = outfun()
f("")
f("")
f("")
f1 = outfun(namelist=["1"])
f1("2")
f1("3")
f("")
f1("4")
"""前方高能"""
f2 = outfun()
f2("A")
修改接收到的可变对象参数

 1.2 如何判断函数是否是闭包 

  使⽤函数名.__closure__返回cell就是闭包。
def outerFun():
    name="Aaron"  # 外层函数outerFun 中的变量 name
    def innerFun():
        print(name) # 内层函数innerFun 引用了外层函数中的 name
    return innerFun

f1= outerFun()

print(f1.__closure__)     #输出结果   (<cell at 0x00E7FE10: str object at 0x00E7FDE0>,)
print(outerFun.__closure__) # 输出结果   None
__closure__

 1.3 闭包的特点

  • 安全
  • 常驻内存

 1.4 闭包练习题  

def my_func(*args):
    fs = []
    for i in range(3):
        def func():
            return i * i
        fs.append(func)
    return fs
fs1, fs2, fs3 = my_func()
print(type(my_func()))  #<class 'list'>
print(fs1())            #4
print(fs2())            #4
print(fs3())            #4
易错题

  python解释器开始执⾏之后, 就会在内存中开辟⼀个空间, 每当遇到⼀个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表⽰这个函数存在了, ⾄于函数内部的变量和逻辑, 解释器是不关⼼的. 也就是说⼀开始的时候函数只是加载进来, 仅此⽽已, 只有当函数被调⽤和访问的时候, 解释器才会根据函数内部声明的变量来进⾏开辟变量的内部空间. 随着函数执⾏完毕, 这些函数内部变量占⽤的空间也会随着函数执⾏完毕⽽被清空.  

def my_func(*args):
    fs = []
    for i in range(3):
        def func(_i=i):
            return _i * _i
        fs.append(func)
    return fs
fs1, fs2, fs3 = my_func()
print(type(my_func()))  #<class 'list'>
print(fs1())            #0
print(fs2())            #1
print(fs3())            #4
正确写法

  结论:返回闭包中不要引用任何循环变量,或者后续会发生变化的变量

二:迭代器


  1.1 迭代器协议    

    a:对象必须提供一个next方法

    b:执行方法要么返回迭代中的下一项,要么抛弃一个Stopiteration异常

    c:只能向后不能向前。

 1.2 可迭代对象

    实现了迭代器协议的对象。(对象内部定义一个__iter__()方法)

 1.3 使用迭代器访问对象

    检验可迭代对象向 Iterable: 可迭代对象. 内部包含__iter__()函数   Iterator: 迭代器. 内部包含__iter__() 同时包含__next__().

    常见的 str,list, tuple, dict, set,都是可以迭代的。

from collections.abc import Iterable
from collections.abc import Iterator
l = [1,2,3]
l_iter = l.__iter__()

#检查是否是可迭代的对象
print(isinstance(l,Iterable)) #True
print(isinstance(l,Iterator)) #False
print(isinstance(l_iter,Iterator)) #True
print(isinstance(l_iter,Iterable)) #True
检验可迭代对象
st="hello";
# 先将字符串对象转化为可迭代对象
iterSt = st.__iter__()
while True:
    try:
        print(iterSt.__next__())
    except StopIteration:
        # print('迭代完成')
        break
迭代器之for循环模拟

三:生成器


1.1 什么是生成器?生成器的获取方法    

    使用了 yield 的函数被称为生成器,其实质就是迭代器.

    跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。 

  1.2 生成器特征    

    生成器函数:使用yield语句返回结果,在每个结果中间,挂起函数的状态,以便下次从此处继续执行。

    不用调用__iter__(),生成器生成的元素直接可调用__next__()方法。

    ⽣成器的惰性机制: 生成器只有在访问的时候才取值.   

  1.3 获取生成器:

    1:  通过生成器函数(含有yield的函数)    
def fun():
    print(111)
    yield 222
g = fun() #生成器
print(g);  #输出结果:<generator object fun at 0x00EA5D30>
生成器函数

    2: 通过生成器推导式来实现生成器    

      生成器表达式: (结果 for 变量 in 可迭代对象 if 条件筛选)
      gen = (i for i in range(1, 10) if i%2==0)  

  1.4 遍历生成器:

    1: 通过__next__()(一次只能获取一个)

    2:通过send()(一次只能获取一个,可以传递参数)

    3:通过for循环

    4:通过list()将其转换为列表   

def fun():
    a = yield 1
    print("接收send传递的值", a)
    b = yield 2
    c = yield 3
    d = yield 4
    e = yield 5


gen = fun()
print("通过__next__()获取:", gen.__next__())
hh=gen.send("12")
print(hh)
print("结论一:可以看出生成器是惰性机制,也可以叫做是懒加载。用一次给一次,节省内存!")
for i in gen:
    print(i)
print("结论二:由于生成器和迭代器类似一旦用过,不能再次使用!")
print(list(gen))

''' 输出结果
通过__next__()获取: 1
接收send传递的值 12
2
结论一:可以看出生成器是惰性机制,也可以叫做是懒加载。用一次给一次,节省内存!
3
4
5
结论二:由于生成器和迭代器类似一旦用过,不能再次使用!
[]
 
'''
遍历生成器

    注意:send和__next__()的区别在于,send()可以传递参数。但是send可以给上一个yield的位置传递值, 不能给最后一个yield发送值。同时生成器某些元素用完以后就没有了。

  1.5. 其他推导式    

    列表推导式: [ 结果 for 变量 in 可迭代对象 if 条件 ]
           lst = [i for i in range(1, 10) if i%2==0] # [2, 4, 6, 8]
    字典推导式:{结果 for 变量 in 可迭代对象 if 条件}
           dic = {i-1:i for i in range(1, 10) if i%2==0} #{1: 2, 3: 4, 5: 6, 7: 8}
    集合推导式:{结果 for 变量 in 可迭代对象 if 条件}
          set = {i%2for i in range(1, 10) } #{0, 1}   

1.6. 练习题 

    1.6.1 人口普查小练习  

{"location":"北京",'count':100}
{"location":"上海",'count':300}
{"location":"广州",'count':200}
{"location":"深圳",'count':400}
人口普查.txt
# 通过with as 的方式打开文件不用关闭
def getPeople():
    with open("人口普查","r",encoding="utf-8") as f:
        for i in f:
            yield i
g = getPeople();
amount=sum(eval(i)["count"] for i in g)
h=getPeople()
for i in h:
    print(eval(i)["location"]+"的人数为:"+ str(eval(i)["count"]) +",占统计总人数的"+str(eval(i)["count"]/amount)+"%")

'''
北京的人数为:100,占统计总人数的0.1%
上海的人数为:300,占统计总人数的0.3%
广州的人数为:200,占统计总人数的0.2%
深圳的人数为:400,占统计总人数的0.4%
'''
人口.py

    1.6.2 生产者消费者模式

def consumer(name):
    print('我是【%s】,我准备开始吃包子了' %name)
    while True:
        baozi = yield
        print("【%s】很开心的吃掉了【%s】" %(name,baozi))

def producer():
    c1=consumer('张三')
    c2=consumer("李四")
    c1.__next__()
    c2.__next__()
    for i in range(10):
        if i % 2 == 0:
            c1.send("包子 %s" %i )
        else:
            c2.send("包子 %s" %i)
producer()

'''
我是【张三】,我准备开始吃包子了
我是【李四】,我准备开始吃包子了
【张三】很开心的吃掉了【包子 0】
【李四】很开心的吃掉了【包子 1】
【张三】很开心的吃掉了【包子 2】
【李四】很开心的吃掉了【包子 3】
【张三】很开心的吃掉了【包子 4】
【李四】很开心的吃掉了【包子 5】
【张三】很开心的吃掉了【包子 6】
【李四】很开心的吃掉了【包子 7】
【张三】很开心的吃掉了【包子 8】
【李四】很开心的吃掉了【包子 9】
'''
生产者消费者模式
# 猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个
# 第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下
# 的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少。


# 方法一,递归调用
def getNum(day,cou=1):
    cou =2 *(cou + 1) ;
    day-=1
    if day==1:
        return cou;
    return getNum(day, cou)
print(getNum(10))
# 方法二,递归调用
def f(n):
    if n ==1:
        return 1
    return (f(n-1)+1)*2
print(f(10))
# 方法三,reduce函数
s=1
func=lambda x:(x+1)*2
for x in range(9):
    s=func(s)
print(s)
猴子吃桃

    1.6.3 判断输出结果    

def add(a, b):
    return a + b
def test():
    for r_i in range(1,4):
        yield r_i
g=test()

for n in [2,10]:
    g=(add(n,i) for i in g)

print(list(g))

#[21, 22, 23]
#根据生成器惰性机制,只有在list(g)的时候才开始调用,此时g的内容是什么?
# 当n=2 时 g=(add(n,i) for i in g) 但是此时仍然没有执行
# 当n=10 时 g=add(n,i) for i in (add(n,i) for i in g) ),开始执行。
#   内存计算后得:g=add(n,i) for i in [11,12,13]
#   再次计算可得:[21,22,23]
生成器,惰性机制

四:装饰器


1.1 什么是装饰器?

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

  本质就是函数,功能是为其他函数添加附加功能

  原则:对修改封闭,对扩展开放

    1:不能修改被修饰函数的源代码

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

# 装饰器=高阶函数+函数嵌套+闭包
# 高阶函数:传入参数或输出结果是一个函数
# 函数嵌套:函数中定义函数
import time
def timmer(func):
    def wrapper():
        startTime= time.time()
        func();
        endTime= time.time()
        print("被测试函数一共运行:"+str(endTime-startTime)+"")
    return wrapper
@timmer  #语法糖,相当于#test=timmer(test)
def test():
    time.sleep(0.3);
    print("test函数运行完毕")
test()
装饰器-计算方法时间
# 装饰器=高阶函数+函数嵌套+闭包
# 高阶函数:传入参数或输出结果是一个函数
# 函数嵌套:函数中定义函数
import time
def timmer(func):
    def wrapper(*args,**kwargs):
        startTime= time.time()
        res=func(*args,**kwargs);
        endTime= time.time()
        print("被测试函数一共运行:"+str(endTime-startTime)+"")
        return  res
    return wrapper
@timmer  #语法糖,相当于#test=timmer(test)
def test(a,b):
    return a+b

print(test(100,200))
装饰器-设置参数和返回值
# 装饰器=高阶函数+函数嵌套+闭包
# 高阶函数:传入参数或输出结果是一个函数
# 函数嵌套:函数中定义函数
import time
# 添加一个参数,如果参数是n就打n折
def disCount(n=1):
    def timmer(func):
        def wrapper(*args,**kwargs):
            startTime= time.time()
            res=func(*args,**kwargs)*n;
            endTime= time.time()
            print("今天是国庆节,每位客户打的折扣为:"+str(n*10))
            return  res
        return wrapper
    return timmer
#@timmer  #语法糖,相当于#test=timmer(test)
@disCount(n=0.9)
def test(a,b):
    return a+b

print(test(100,200))
闭包,设置参数
原文地址:https://www.cnblogs.com/YK2012/p/9726739.html