python开发之装饰器&迭代器&生成器

闭包的引入

闭包:首先必须是嵌套函数,再次内部函数调用外部函数的变量,如下代码

def outer():
    a = 1
    def inner():
        print("内部函数>",a)
    print(inner.__closure__) # 用来判断闭包的,(<cell at 0x004573D0: int object at 0x53416880>,)
outer()

外部函数的返回值是内部函数的函数名

def outer():
    a = 1
    def inner():
        print("内部函数>",a)
    return inner
res = outer()
res()

闭包的小小应用:

from urllib.request import urlopen
def get_url():
    url = 'http://www.xiaohua100.cn/index.html'
    def inner():
        url_res = urlopen(url).read()
        print(url_res)
    return inner

get_url()

总结:闭包好处,多次调用函数,不必多次生成外部函数的变量。

装饰器

装饰器的引出,计算程序的执行时间

import time

def func():
    time.sleep(2)

def timer(f):
    start = time.time()
    f()
    end = time.time()
    print('函数运行的时间是:',end-start)

timer(func)
虽然上面的代码可以将实现将计算时间的函数分离,但是改变了原函数额调用方式
def func():
    time.sleep(2)

def timer(f): # 装饰器函数
    def inner():
        start = time.time()
        f() # 被装饰函数
        end = time.time()
        print('函数运行的时间是:',end-start)
    return inner
func = timer(func)
func()

装饰器的作用:不想修改原来函数的调用方式,但是还想再原来的函数前后添加功能。

最终版没有改变函数的调用方式

def timer(f): # 装饰器函数
    def inner():
        start = time.time()
        f() # 被装饰函数
        end = time.time()
        print('函数运行的时间是:',end-start)
    return inner
@timer # 语法糖,@装饰器函数名
def func():
    time.sleep(2)

func()

带返回值的装饰器

def timer(f): # 装饰器函数
    def inner():
        start = time.time()
        res = f() # 被装饰函数
        end = time.time()
        print('函数运行的时间是:',end-start)
        return res
    return inner
@timer # 语法糖,@装饰器函数名
def func():
    time.sleep(2)
    return 'ok'
res = func()
print(res)

装饰带参数的函数装饰器

def timer(f): # 装饰器函数
    def inner(*args,**kwargs):
        start = time.time()
        res = f(*args,**kwargs) # 被装饰函数
        end = time.time()
        print('函数运行的时间是:',end-start)
        return res
    return inner
@timer # 语法糖,@装饰器函数名
def func(a):
    time.sleep(2)
    print('参数:',a)
    return 'ok'
res = func(1)
print(res)

装饰器的最终形式

def wrapper(func):
    def inner(*args,**kwargs):
        ret = func(*args,**kwargs)
        return ret
    return inner
@wrapper
def qqxing(): # qqing=wrapper(qqxing)
    print(123)

qqxing()
print(qqxing.__name__) # inner

 例子:通过使用装饰器来将要执行的函数的函数名写入文件中

import datetime
def log(func):
    def inner(*args,**kwargs):
        with open('log.txt','a',encoding='utf8') as f:
            date_str = str(datetime.datetime.now())
            f.write(date_str+func.__name__+'
')
        ret = func(*args,**kwargs)
        return ret
    return inner
@log
def qqxing():
    print('qqxing')
qqxing()

通过别人写的装饰器模块,来得到被装饰函数的函数名

import datetime
from functools import wraps
def log(func):
    @wraps(func)
    def inner(*args,**kwargs):
        with open('log.txt','a',encoding='utf8') as f:
            date_str = str(datetime.datetime.now())
            f.write(date_str+func.__name__+'
')
        ret = func(*args,**kwargs)
        return ret
    return inner
@log
def qqxing():
    print('qqxing')
qqxing()
print(qqxing.__name__) # 如果没有加@wraps(func),qqxing__name__是inner,@wraps也成为装饰器修复技术

 装饰器进阶

 1、三层装饰器

import time

Flag = True
def timer_out(flag):
     def timer(func):
         def inner(*args,**kwargs):
             if flag:
                start = time.time()
                ret = func(*args,**kwargs)
                end = time.time()
                print(end-start)
                return ret
             else:
                ret = func(*args, **kwargs)
                return ret

         return inner
     return timer
# timer = timer_out(Flag)
@timer_out(Flag) # <==>@timer
def wahaha():
    time.sleep(2)
    print('wahahah')

wahaha()

2、@wrapper2

      @wrapper1

如上所示的装饰器

def warpper1(func):
    def inner1(*args,**kwargs):
        print("wrapper1,before func")
        func()
        print("wrapper1,after func")
    return inner1

def warpper2(func):
    def inner2(*args,**kwargs):
        print("wrapper2,before func") 
        func()
        print("wrapper2,after func")
    return inner2

@warpper2 # foo=wrapper(foo)-->wrapper(inner1)==inner2
@warpper1 # 先执行这个装饰器 foo=warpper1(foo)==inner1
def foo():
    print('foo')
foo()
#结果
# wrapper2,before func
# wrapper1,before func
# foo
# wrapper1,after func
# wrapper2,after func

 3、面试题:免登录版装饰器

Flag = False

def login(func):
    def inner(*args,**kwargs):
        global Flag
        if Flag:
            ret = func()
            return ret
        else:
            username = input('username:')
            password = input('password:')
            if username=='jack' and password=='123':
                Flag=True
                ret = func(*args,**kwargs)
                return ret
            else:
                print('登录失败')
    return inner

 4、装饰器版的登录校验

   这个例子其中的一些方法会在Django框架中学习到,这里只是为了总结装饰器而已。

def check_login(func):
    def inner(requset,*args,**kwargs):
        ret = requset.get_signed_cookie("is_login",default="0",salt="salt") # 加盐的cookie
        if ret == '1':
            # 已经登录过的继续执行
            return func(requset,*args,**kwargs)
        else:
            next_url = requset.path_info # 获取当前访问的URL
            return redirect("login/?next={}".format(next_url))
        return inner

生成器 

如下两种形式都是生成器

1、生成器函数:只要含有yield关键字的函数都是生成器,类似return,只能写在函数内部,不能和return共用。而且yield语句一次返回一个结果,在每个结果中间挂起原来的状态,以便下次从它开始离开的地方继续执行。

首先看普通函数的返回值

def generator():
    print('============')
    return '返回值函数'

g = generator()
print(g)

再看含有yield 的生成器函数

def generator():
    print('>>>>>>>>>>')
    yield '生成器函数'

g = generator()
print(g.__next__())
print(next(g))
# 生成器函数
# Traceback (most recent call last):
#   File "D:/pythonstudy/装饰器迭代器生成器/生成器.py", line 16, in <module>
#     print(next(g))
# StopIteration  如果取不到了值会报错为StopIteration
def generator():
    print(1)
    yield 'a'
    print(2)
    yield 'b'

g = generator()
print(g.__next__())
print(next(g))

 生成器进阶

def generator():
    print(123)
    content = yield 1
    print('content:', content)
    print(456)
    yield 2


g = generator()
print(g.__next__())
print(g.send('hello')) # send()和next()的效果一样

 send获取下一个值的效果和next基本一样,只是在获取下一个值的时候,给上一个值的位置传递一个数据。

    注意:第一次使用生成器的时候必须用next获取第一个值。

              最后一个yield不能接受外部的值。

生成器面试题

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)) #[0, 1, 2, 3]
print(list(g2)) #[]

 注释:g1从g中取值直到取完,g2从g1中取到g1末,但是g2开始取值的时候,g1已经为空,所以该结果为空。

2、生成器表达式:本质迭代器【自带__iter__方法和__next__方法】

     列表解析式 egg_list = ['鸡蛋%s'%i for i in range(10)]

     生成器表达式 laomuji=('鸡蛋%s'%i for i in range(10))

 迭代器

1、什么叫迭代

字符串、列表、元祖、字典、集合都可以被for循环,也可以通过Iterable来进行证明,这些都是可迭代的。

from collections import Iterable
l = [11,22,33]
t = (11,22,33)
d = {"name":"jack"}
s = {11,22,33}

print(isinstance(l,Iterable)) # True
print(isinstance(t,Iterable)) #True
print(isinstance(d,Iterable)) #True
print(isinstance(s,Iterable)) #True

2、可迭代协议

可迭代协议定义非常简单,就是内部实现了__iter__方法。

迭代器遵循迭代器协议:必须使用__iter__方法和__next__方法

3、类中的__iter__和__next__方法

class Foo:
    def __init__(self,n):
        self.n = n
    def __iter__(self): # 成为一个可迭代对象。
        return self
    def __next__(self):
        self.n += 1
        return self.n

f = Foo(10)
f.__next__()
f.__next__()
for i in f:
    if i>20:
        break
    print(i)

小练习:迭代器实现斐波那契数列

class Fib:
    def __init__(self,a,b):
        self._a = a
        self._b = b
    def __iter__(self):
        return self
    def __next__(self):
        self._a,self._b = self._b,self._a + self._b
        return self._a
f = Fib(1,1)
print(f.__next__()) # 1
print(f.__next__()) # 2
print(f.__next__()) # 3 
print(f.__next__()) # 5
print(f.__next__()) # 8
print(f.__next__()) # 13

迭代器的好处:

1、从容器中一个一个的取值,会把所有的值都取到

2、节省内存空间,迭代器并不会在内存中再占用一大块内存,而是随着循环每次生成一个,每次next每次回给出一个值。

原文地址:https://www.cnblogs.com/crazyforever/p/5081914.html