• 装饰器、迭代器、生成器


    一、装饰器:

    1、装饰器定义:

    本质是函数,装饰其它函数,就是为其他函数添加附加功能。

    @+函数

    2、原则:

    2.1.不能修改被装饰的函数的源代码
    2.2不能修改被装饰的函数的调用方式
    import time
    def timmer(func):
        def warpper(*args,**kwargs):
            start_time=time.time()
            func()
            stop_time=time.time()
            print('the func run time is %s' %(stop_time-start_time))
        return warpper
    
    @timmer
    def test1():
        time.sleep(3)
        print('in the test1')
    test1()
    装饰器例子一

     3、实现装饰器知识储备

    3.1.函数即’变量' 

    3.2.高阶函数

    满足下列条件之一就可成函数为高阶函数

    1. 某一函数名当做实参传给另一个函数中(在不修改被装饰函数源代码的情况下为其添加功能)

    2. 函数的返回值包含n个函数名,n>0(不修改函数的调用方式)

    高阶函数示范:

    def bar():
        print 'in the bar'
    def foo(func):
        res=func()
        return res
    foo(bar)
    View Code

    高阶函数牛x之处:

    def foo(func):
        return func
     
    print 'Function body is %s' %(foo(bar))
    print 'Function name is %s' %(foo(bar).func_name)
    foo(bar)()
    #foo(bar)() 等同于bar=foo(bar)然后bar()
    bar=foo(bar)
    bar()

    test2(bar()) #传递函数返回值

    和test2(bar) #传递的函数地址

    3.3.嵌套函数 

    在一个函数体内定义一个函数,而不是调用一个函数(基于python支持静态嵌套域)

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

    函数嵌套示范:

    def foo():
        def bar():
            print 'in the bar'
     
        bar()
     
    foo()
    # bar()
    View Code

    局部作用域和全局作用域的访问顺序:

    x=0
    def grandpa():
        # x=1
        def dad():
            x=2
            def son():
                x=3
                print x
            son()
        dad()
    grandpa()
    View Code

    局部变量修改对全局变量的影响:

    y=10
    # def test():
    #     y+=1
    #     print y
     
    def test():
        # global y
        y=2
        print y
     
    test()
    print y
     
     
    def dad():
        m=1
        def son():
            n=2
            print '--->',m + n
        print '-->',m
        son()
    dad()
    View Code

    3.4匿名函数 lambda 和普通函数一样

    calc = lambda x:x*3
    
    print(clac(3)
    View Code

     4.闭包:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是 closure

    def counter(start_num=0):
        count=[start_num]
        def incr():
            count[0]+=1
            return count[0]
        return incr
     
    print counter()
    print counter()()
    print counter()()
    c=counter()
    print c()
    print c()
    View Code

    5.装饰器进阶

    装饰器原理:

    import time
    def timer(func): #timer(test1)
        def deco():
            """
            在返回时调用该函数
            :return:
            """
            start_time = time.time()
            func()  #run test1函数
            stop_time=time.time()
            print('the func run time is %s' %(stop_time-start_time))
        return deco
    
    @timer  #test1=timer(test1)运行函数
    def test1():
        time.sleep(3)
        print('in the test1')
    @timer
    def test2():
        time.sleep(3)
        print('in the test2')
    
    # test1()
    # test2()
    
    # deco(test1)   #可以实现运行,但改变了test1运行方式
    # deco(test2)    #可以实现运行,但改变了test2运行方式
    # test1=timer(test1)
    test1()
    test2()
    # test2=deco(test2)
    # test2()
    View Code

    增加参数:

    带有参数的,报错:

    test2()
      File "E:/python34foexam/test-decorator.py", line 58, in deco
        func()  #run test1函数
    TypeError: test2() missing 1 required positional argument: 'name'
    View Code

    改正,加入参数:

    import time
    def timer(func): #timer(test1)
        def deco(*args,**kwargs):
            """
            在返回时调用该函数
            :return:
            """
            start_time = time.time()
            func(*args,**kwargs)  #run test1函数
            stop_time=time.time()
            print('the func run time is %s' %(stop_time-start_time))
        return deco  #返回deco函数内存地址
    
    @timer  #test1=timer(test1)运行函数
    def test1():
        time.sleep(3)
        print('in the test1')
    @timer
    def test2(name):
        time.sleep(3)
        print('in the test2')
    
    # test1()
    # test2()
    
    # deco(test1)   #可以实现运行,但改变了test1运行方式
    # deco(test2)    #可以实现运行,但改变了test2运行方式
    # test1=timer(test1)
    test1()
    test2('alex')
    # test2=deco(test2)
    # test2()
    View Code

    装饰器应用实例:

    装饰器功能:函数超时则终止

    # -*- coding: utf-8 -*-  
    from threading import Thread  
    import time  
       
    class TimeoutException(Exception):  
        pass  
       
    ThreadStop = Thread._Thread__stop#获取私有函数  
       
    def timelimited(timeout):  
        def decorator(function):  
            def decorator2(*args,**kwargs):  
                class TimeLimited(Thread):  
                    def __init__(self,_error= None,):  
                        Thread.__init__(self)  
                        self._error =  _error  
                           
                    def run(self):  
                        try:  
                            self.result = function(*args,**kwargs)  
                        except Exception,e:  
                            self._error =e  
       
                    def _stop(self):  
                        if self.isAlive():  
                            ThreadStop(self)  
       
                t = TimeLimited()  
                t.start()  
                t.join(timeout)  
            
                if isinstance(t._error,TimeoutException):  
                    t._stop()  
                    raise TimeoutException('timeout for %s' % (repr(function)))  
       
                if t.isAlive():  
                    t._stop()  
                    raise TimeoutException('timeout for %s' % (repr(function)))  
       
                if t._error is None:  
                    return t.result  
       
            return decorator2  
        return decorator  
      
    @timelimited(2)  
    def fn_1(secs):  
        time.sleep(secs)  
        return 'Finished'  
           
    if __name__ == "__main__":  
        print fn_1(4)
    View Code

    装饰器终极(高级版):

    模拟n个页面,m个需要验证登录的情况,且home()和bbs()需要不同的验证方式

    user,passwd='alex','abc123'
    def auth(func):
        def wrapper(*args,**kwargs):
            username = input('Username').strip()
            password = input('Password').strip()
            if username == user and password == passwd:
                print('33[32;1mUser has passed authentication33[0m')
                # return func(*args, **kwargs)
                res = func(*args,**kwargs)
                print(res)
                print('----after authentication')
                return res        #处理调用不同函数时的返回值
            else:
                exit('33[32;1mInvalid username or password33[0m')
        return wrapper
    
    
    @auth
    def index():
        print('welcome to index page')
    
    @auth
    def home():
        print('welcome to home page')
        return "from home"
    @auth
    def bbs():
        print('welcome to bbs page')
    index()
    home()
    bbs()
    View Code
    user,passwd='alex','abc123'
    def auth(auth_type):
        def outer_wrapper(func):
            def wrapper(*args,**kwargs):
                username = input('Username').strip()
                password = input('Password').strip()
                if username == user and password == passwd:
                    print('33[32;1mUser has passed authentication33[0m')
                    # return func(*args, **kwargs)
                    res = func(*args,**kwargs)
                    print(res)
                    print('----after authentication')
                    return res        #处理调用不同函数时的返回值
                else:
                    exit('33[32;1mInvalid username or password33[0m')
            return wrapper
        return outer_wrapper
    
    
    def index():
        print('welcome to index page')
    
    @auth(auth_type="local")  #传local验证
    def home():
        print('welcome to home page')
        return "from home"
    @auth(auth_type="ldap") #传ldap验证
    def bbs():
        print('welcome to bbs page')
    index()
    home()
    bbs()
    View Code
    #!/usr/bin/env python
    # -*-coding:utf-8-*-
    # __author__="Liudong"
    def outer(func):
        def deco(*args,**kwargs):
            print('before %s' %func.__name__)
            res=func(*args,**kwargs)
            print('after %s' %func.__name__)
            return res
        return deco
    
    
    
    @outer
    def f1(arg):
        return arg + 1
    
    @outer
    def f2(arg1, arg2):
        return arg1 + arg2
    
    # f1(1)
    # f2(2,3)
    print(f1(1))
    print(f2(2,3))
    测试题

     二、迭代器:

    1.列表生成式,迭代器&生成器

     我现在有个需求,看列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],我要求你把列表里的每个值加1,你怎么实现?你可能会想到2种方式 

    普通版
    >>> a
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    >>> a = map(lambda x:x+1, a)
    >>> a
    <map object at 0x101d2c630>
    >>> for i in a:print(i)
    ... 
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    文艺版

    其实还有一种写法,如下 :

    >>> a = [i+1 for i in range(10)]
    >>> a
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    装逼版

    这就叫做列表生成,如:

    [func(i) for i in range(10)]

    迭代器与生成器:

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

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

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

    >>> L = [x * x for x in range(10)]
    >>> L
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    >>> g = (x * x for x in range(10))
    >>> g
    <generator object <genexpr> at 0x1022ef630>

    创建Lg的区别仅在于最外层的[]()L是一个list,而g是一个generator。

    我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?

    如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:

    >>> next(g)
    0
    >>> next(g)
    1
    >>> next(g)
    4
    >>> next(g)
    9
    >>> next(g)
    16
    >>> next(g)
    25
    >>> next(g)
    36
    >>> next(g)
    49
    >>> next(g)
    64
    >>> next(g)
    81
    >>> next(g)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration

    2.x中用next()方法,3.x中用x.__next__()

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

    生成器只有在调用时才会生成相应数据,且只记录当前位置,只有一个__next__()方法

    当然,上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:

    >>> g = (x * x for x in range(10))
    >>> for n in g:
    ...     print(n)
    ...
    0
    1
    4
    9
    16
    25
    36
    49
    64
    81

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

    generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

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

    1, 1, 2, 3, 5, 8, 13, 21, 34, ...

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

    def fib(max):
        n, a, b = 0, 0, 1
        while n < max:
            print(b)
            a, b = b, a + b
            n = n + 1
        return 'done'

    注意,赋值语句:

    a, b = b, a + b

    但不必显式写出临时变量t就可以赋值。

    上面的函数可以输出斐波那契数列的前N个数:

    >>> fib(10)
    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    done

    仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

    也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

    def fib(max):
        n,a,b = 0,0,1
    
        while n < max:
            #print(b)
            yield  b   #返回的值,加yield,返回并保存当前状态值
            a,b = b,a+b
    
            n += 1
    
        return 'done' 

    这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:

    >>> f = fib(6)
    >>> f
    <generator object fib at 0x104feaaa0>

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

    data = fib(10)
    print(data)
    
    print(data.__next__())
    print(data.__next__())
    print("干点别的事")
    print(data.__next__())
    print(data.__next__())
    print(data.__next__())
    print(data.__next__())
    print(data.__next__())
    
    #输出
    <generator object fib at 0x101be02b0>
    1
    1
    干点别的事
    2
    3
    5
    8
    13

    在上面fib的例子,我们在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。

    同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

    >>> for n in fib(6):
    ...     print(n)
    ...
    1
    1
    2
    3
    5
    8

    但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIterationvalue中:

    >>> g = fib(6)
    >>> while True:
    ...     try:
    ...         x = next(g)
    ...         print('g:', x)
    ...     except StopIteration as e:
    ...         print('Generator return value:', e.value)
    ...         break
    ...
    g: 1
    g: 1
    g: 2
    g: 3
    g: 5
    g: 8
    Generator return value: done

    关于如何捕获错误,后面的错误处理还会详细讲解。

    还可通过yield实现在单线程的情况下实现并发运算的效果

    import time
    def consumer(name):
        print('%s 准备吃包子了!'%name)
        while True:
            baozi = yield
            print('包子[%s]来了,被[%s]吃了!'%(baozi,name))
    def producer(name):
        c=consumer('A')
        c2=consumer('B')
        c.__next__()  #生成器不是函数,只会在内存生成,所以必须先运行一下next,先让它生成做好准备
        c2.__next__()
        print('老子开始做包子啦!')
        for i in range(10):
            time.sleep(1)
            print('做了2个包子!')
            c.send(i)
            c2.send(i)
    producer('alex')
    生成器并行计算实例

    c.send('值'),可以把值发给yield。

    迭代器

     我们已经知道,可以直接作用于for循环的数据类型有以下几种:

    一类是集合数据类型,如listtupledictsetstr等;

    一类是generator,包括生成器和带yield的generator function。

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

    可以使用isinstance()判断一个对象是否是Iterable对象:

    >>> from collections import Iterable
    >>> isinstance([], Iterable)
    True
    >>> isinstance({}, Iterable)
    True
    >>> isinstance('abc', Iterable)
    True
    >>> isinstance((x for x in range(10)), Iterable)
    True
    >>> isinstance(100, Iterable)
    False

    而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

    *可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator

    可以使用isinstance()判断一个对象是否是Iterator对象:

    >>> from collections import Iterator
    >>> isinstance((x for x in range(10)), Iterator)
    True
    >>> isinstance([], Iterator)
    False
    >>> isinstance({}, Iterator)
    False
    >>> isinstance('abc', Iterator)
    False
    a=[1,2,3]  #是可迭代对象,不是迭代器,因为它没有__next__()方法
    dir(a)

    生成器都是Iterator对象,但listdictstr虽然是Iterable,却不是Iterator

    listdictstrIterable变成Iterator可以使用iter()函数:

    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter('abc'), Iterator)
    True

    可以通过iter()方法,将对象变成迭代器:

    >>> from collections import Iterator
    >>> a=[1,2,3]
    >>> iter(a)
    <list_iterator object at 0x0138BE50>
    >>> b=iter(a)
    >>> b.__next__()
    1
    >>> b.__next__()
    2
    >>> b.__next__()
    3

    你可能会问,为什么listdictstr等数据类型不是Iterator

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

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

    小结

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

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

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

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

    for x in [1, 2, 3, 4, 5]:
        pass

    实际上完全等价于:

    # 首先获得Iterator对象:
    it = iter([1, 2, 3, 4, 5])
    # 循环:
    while True:
        try:
            # 获得下一个值:
            x = next(it)
        except StopIteration:
            # 遇到StopIteration就退出循环
            break

    2.x的range()是列表,xrange()是迭代器,3.x range()就是迭代器

  • 相关阅读:
    Atitit 华为基本法 attilax读后感
    Atitit 华为管理者内训书系 以奋斗者为本 华为公司人力资源管理纲要 attilax读后感
    Atitit 项目版本管理gitflow 与 Forking的对比与使用
    Atitit 管理的模式扁平化管理 金字塔 直线型管理 垂直管理 水平管理 矩阵式管理 网状式样管理 多头管理 双头管理
    Atitit 乌合之众读后感attilax总结 与读后感结构规范总结
    深入理解 JavaScript 异步系列(4)—— Generator
    深入理解 JavaScript 异步系列(3)—— ES6 中的 Promise
    深入理解 JavaScript 异步系列(2)—— jquery的解决方案
    深入理解 JavaScript 异步系列(1)——基础
    使用 github + jekyll 搭建个人博客
  • 原文地址:https://www.cnblogs.com/ld1977/p/6123875.html
Copyright © 2020-2023  润新知