python之yield与装饰器

发布时间:2019-09-18 07:27:56编辑:auto阅读(1700)

    防伪码:忘情公子著


    python中的yield:

      在之前发布的《python之列表解析与生成器》中我们有提到过,生成器所实现的是跟列表解析近似的效果,但是我们不能对生成器做一些属于列表解析的操作

      因为生成器本身就不是一个列表,它只是模拟了一个类似列表的行为,因此,施加在列表中的很多操作,对生成器而言是无效的。

      由于生成器表达式并不会直接创建出序列形式的列表,因此不能对其进行索引、切片,不能执行任何常规的列表操作。比如:弹出元素(pop())、添加元素(append())等等。但是我们可以通过list函数将生成器转换成列表。

    In [1]: list((i**2 for i in range(1,11)))
    Out[1]: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

      很多情况下我们需要生成更为复杂的结果,又不想基于某个列表来实现,但是简单的使用一个生成器表达式很难实现此种行为。此时我们可以通过一个自定义函数来完全实现类似的效果。  

    In [2]: def genNum(x):
       ...:     y = 0
       ...:     while y <= x:
       ...:         yield y
       ...:         y += 1
       ...: 
    
    In [3]: g1 = genNum(10)
    
    In [4]: type(g1)
    Out[4]: generator
    
    In [5]: g1.next()
    Out[5]: 0
    
    In [6]: g1.next()
    Out[6]: 1
    
    In [7]: g1.next()
    Out[7]: 2
    
    In [8]: g1.next()
    Out[8]: 3
    
    In [9]: g1.next()
    Out[9]: 4
    
    In [10]: g1.next()
    Out[10]: 5
    
    In [11]: g1.next()
    Out[11]: 6
    
    In [12]: g1.next()
    Out[12]: 7
    
    In [13]: g1.next()
    Out[13]: 8
    
    In [14]: g1.next()
    Out[14]: 9
    
    In [15]: g1.next()
    Out[15]: 10
    
    In [16]: g1.next()
    ---------------------------------------------------------------------------
    StopIteration                             Traceback (most recent call last)
    <ipython-input-16-9066a8f18086> in <module>()
    ----> 1 g1.next()
    
    StopIteration:

      yield本身并不是一个返回值,却能够生成一个生成器对象。

      yield保存着一个对象的状态信息。(快照的例子:快照当中保存的是执行快照时的状态)

      如上例所看到的,当我们在函数中使用yield,会返回一个生成器对象。

      求1到20以内所有正整数的平方:

    In [17]: def genNum(n):
        ...:     count = 1
        ...:     while count <= n:
        ...:         yield count ** 2
        ...:         count += 1
        ...: 
    
    In [18]: g1 = genNum(20)
    
    In [19]: for i in g1:
        ...:     print i
        ...: 
    1
    4
    9
    16
    25
    36
    49
    64
    81
    100
    121
    144
    169
    196
    225
    256
    289
    324
    361
    400


    Python中的装饰器:

      装饰器定义:

        本质上是一个函数

        功能是用来装饰其他函数。就是为其他函数添加附加功能

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

      装饰器特定的原则:

        不能修改被装饰的函数的源代码(线上环境)

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

        不能修改被装饰的函数的返回值

      装饰器可以抽离出大量的函数中与函数无关的功能,把函数本身只作为一个核心,在必要时如果函数的核心功能不够,就用装饰器装饰一下本次调用所需要的功能,于是运行结束了,下次当需要其它功能时再用装饰器给重新装饰一下就可以了,这就是装饰器。

      装饰器需要接受一个函数对象作为其参数,而后对此函数做包装,以对此函数进行增强。


      实现装饰器的知识储备:

        1、函数即“变量“(说明变量在内存中存在的方式)

        2、高阶函数

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

    import time
    def bar():
        time.sleep(3)
        print('in the bar')
    def test1(func):
        start_time = time.time()
        func()
        stop_time = time.time()
        print('The func run time is %s'% (stop_time-start_time))
    test1(bar)

        b.返回值中包含函数名(可以实现不修改被装饰函数的调用方式)

    import time
    def bar():
        time.sleep(3)
        print('in the bar')
    def test2(func):
        print(func)
        return func
    x = test2(bar)    #此处也可以改成:bar = test2(bar)
    bar()

      当用bar = test2(bar)时,此处定义的bar变量名就会覆盖之前定义bar函数时生成的变量名bar。

      如此的话,那之前定义的bar函数进行调用时就是使用新定义的bar变量名引用其在内存中的位置,从而达到不修改bar函数调用方式的目的。

        3、嵌套函数

    import time
    def foo():
        print('in the foo')
    def bar():
        print('in the bar')
    bar()
    foo()

      不带参数的func(被装饰的函数):

    In [20]: def decorative(func):
        ...:     def wrapper():    #定义一个包装器
        ...:         print "Please say something: "
        ...:         func()    #调用func,这个func是我们自己定义的
        ...:         print "No zuo no die..."
        ...:     return wrapper
        ...:
    
    In [21]: @decorative    #使用@符号调用装饰器
        ...: def show():    #定义func,名字取什么都无所谓,它只是用来传给装饰器中的func参数
        ...:     print "I'm from Mars."
        ...: show()
        ...:
    Please say something:
    I'm from Mars.
    No zuo no die...

      如上例所示,show函数本身只有一个print语句,而使用装饰器以后,就变成了三个print,这里的print可以改成任何其它的语句,这就是函数的装饰器。

      带参数的func(被装饰的函数):

    In [22]: def decorative(func):
        ...:     def wrapper(x):
        ...:         print "Please say something...>"
        ...:         func(x)
        ...:         print "no zuo no die..."
        ...:     return wrapper
        ...:
    
    In [23]: @decorative
        ...: def show(x):
        ...:     print x
        ...: 
    
    In [24]: show("hello,mars.")
    Please say something...>
    hello,mars.
    no zuo no die...

      现在我们来写一个简单的为函数添加执行时间的装饰器函数:

    import time
    def timmer(func):
        def wrapper(*args,**kwargs):
            start_time = time.time()
            a = func()
            stop_time = time.time()
            print('The func run time is %s'% (stop_time-start_time))
            return a
        return wrapper
    @timmer
    def foo():
        time.sleep(3)
        print('in the foo')
    print(foo())

      接下来再写一个现实生活中能用得到的:

      需求如下:

       假定有三个页面,现在要实现其中2个页面验证登录之后才能访问,另一个页面不用验证即可访问

      首先定义三个页面函数:

    def index():
        print('Welcome to index page')
        return 'from index page'
    def home():
        print('Welcome to home page')
        return 'from home page'
    def bbs():
        print('Welcome to bbs page')
        return 'from bbs page'

      然后定义装饰器函数:

    user = sean
    passwd = abc123
    def auth(auth_type='local'):
        def out_wrapper(func):
            def wrapper(*args,**kwargs):
                if auth_type == 'local':
                    username == input('Username: ').strip()
                    password == input('Password: ').strip()
                    if user == username and passwd == password:
                        print('authentication passed')
                        func()
                elif auth_type == 'ldap':
                    print('This is ldap authentication')
                    func()
            return wrapper
        return out_wrapper

      接下来将装饰器分别应用于home函数与bbs函数:

    def index():
        print('Welcome to index page')
        return 'from index page'
    @auth(auth_type='local')
    def home():
        print('Welcome to home page')
        return 'from home page'
    @auth(auth_type='ldap')
    def bbs():
        print('Welcome to bbs page')
        return 'from bbs page'
    
    #调用函数   
    index()
    home()
    bbs()

关键字