一、装饰器前戏-闭包

     简单来说,python中函数的闭包就是在内部函数里对外部作用域(但不是全局作用域)的变量进行引用,这么说,不太好理解,下面的示例帮助理解
def outer():
    a = 1
    def inner():    # 内部函数inner
        print(a)    # 调用外部环境变量
    return inner

f = outer()
f()

上面的示例就是一个闭包,inner是个内部函数,inner里调用外部作用域变量a,a不是全局变量;这样构成了一个闭包。上面这个例子外部变量是给定的,那么我们通过传参来给定外部变量

def outer(a):
    def inner():    # 内部函数inner
        print(a)    # 调用传入的外部环境变量
    return inner

f = outer(10)
f()

二、装饰器

	装饰器也是函数,它实现的功能是在不改动原有函数代码的条件下,添加新的功能,装饰器的返回值也是函数对象。
	先来看一个简单的例子
def fun1():
    print("111222333")

这是甲写的一个函数,我们要记录执行函数的日志

def fun1():
    print("111222333")
    logging.info("Program is running")

如上,我们导入了logging模块,在fun1函数中加入日志记录,简单实现了功能,但是如果这样需要记录日志的函数有很多个,我们在一个个的加的话,就会有大量的重复,这种情况下,我们就需要使用装饰器,来抽离出与函数功能无关的重复代码。 下面是一个简单的装饰器:

import logging
def outer(f):
    def inner():
        f()
        logging.warn("Program is running")
    return inner
def fun1():
    print("111222333")
fun1 = outer(fun1)
fun1()

函数outer就是一个装饰器函数,他把fun1包裹起来,但是fun1 = outer(fun1),这句需要在每一个需要被装饰的函数下重新赋值,这个怎们办呢,可以用装饰器特有的语法来解决

import logging

def outer(f):
    def inner():
        f()
        logging.warn("Program is running")
    return inner

@outer
def fun1():
    print("111222333")

@outer
def fun2():
    print("444555666")

fun1()
fun2()

三、带参数的装饰器

上面的例子中,被装饰的函数不带参数,实际上更多情况下函数需要参数,我们来修改一下

import logging

def outer(f):
    def inner(*args,**kwargs):
        f(*args,**kwargs)
        logging.warn("Program is running")
    return inner

@outer
def fun1(*args,**kwargs):
    print(args)

fun1(1,2,3,4,5)

上面的例子使被装饰的函数带上了参数,其实,装饰器还有更大的灵活性,装饰器本身也可以带参数传递

import logging
def outer(log_level):
    def decorator(f):
        def inner(*args,**kwargs):
            f(*args,**kwargs)
            if log_level == "warn":
                logging.warn("Program is running")
        return inner
    return decorator
@outer(log_level="warn")
def fun1(*args,**kwargs):
    print(args)

fun1(1,2,3,4,5)

四、列表生成式

列表生成式是python内置的用来创建列表的强大工具,举例来说,不使用列表生成式创建列表如下
L = []
for i in range(10):
    L.append(i)

可以看出,要创建一个1到10的列表,不使用生成式需要3行,再来看看列表生成式

L = [i for i in range(10)]  #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

一行搞定,而且列表生成式还可以配合if进行判断

L = [i for i in range(10) if i > 5]  #[6, 7, 8, 9]

列表生成式还可以两层甚至多层循环使用

L = [i+j for i in "123"  for j in "456"]  #['14', '15', '16', '24', '25', '26', '34', '35', '36']

五、生成器(Generator)

通过学习列表生成式,我们可以快捷创建一个列表,但是,这样生成的列表如果很大,将会占用太多的存储空间,造成资源浪费;python中对于这类边循环边计算的机制,就是生成器了。 ** 生成器表达式:类似于列表生成式**

L = (i**2 for i in range(5))   #<generator object <genexpr> at 0x0000000002161FC0>

这时L就是一个生成器对象了,那么,怎么调用它呢

通过生成器的内置next方法
In [2]: L.__next__()
Out[2]: 0

In [3]: L.__next__()
Out[3]: 1

In [4]: L.__next__()
Out[4]: 4

In [5]: L.__next__()
Out[5]: 9

In [6]: L.__next__()
Out[6]: 16

In [7]: L.__next__()
----------------------------------------------------------------------
StopIteration                        Traceback (most recent call last)
<ipython-input-7-98b0c0707a44> in <module>()
----> 1 L.__next__()

StopIteration:

这里抛出一个Stoplteration异常,这是因为生成器是边计算边使用的机制,每计算出一个值就调用一个值,直到生成器中最后一个值调用完毕后,再次调用就会抛出异常。 生成器的调用也可以使用python内置的next函数,它等同于生成器的__next__()方法

next(L)  # L.__next__()

这类调用方式因为不知道生成器中元素个数,而不好掌握next的次数,所以不便于使用;我们知道for循环的是可迭代的对象,生成器便是一个可迭代的对象,所以我们可以使用for循环。

In [8]: L = (i**2 for i in range(5))

In [9]: L.__iter__()
Out[9]: <generator object <genexpr> at 0x7fa30cb04fc0>

In [10]: for i in L:
   ...:     print(i)
   ...:     
0
1
4
9
16

对比以上两种方式,基本上永远不会在使用next函数了,for循环不需要关心会有抛出的异常,而且代码简洁。

生成器函数:yield 举例来说,我们要使用生成器返回一组连续数字的平方

In [11]: def Gen_test(n):
    ...:     for i in range(n):
    ...:         yield i ** 2
    ...: 
In [12]: for i in Gen_test(10):
    ...:     print(i)
    ...:     
0
1
4
9
16
25
36
49
64
81

这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。 网上关于生成器的例子都是以斐波那契数列举例,是因为斐波那契数列的计算方式最适合生成器,我们这里再来看看这个例子

def fib(max):
    n,before,after = 0,0,1
    while n<max:
        #print(after)        #这是传统的斐波那契数列,使用的print输出
        yield after           #yield生成器
        before,after = after,before+after
        n = n + 1

f = fib(10)
for i in f:
    print(i)

生成器小结: 1、生成器表达式类似于列表生成式,生成器返回的是一个生成器对象,生成器对象是可迭代的,生成器的处理机制是边循环边计算。 2、生成器函数语法上与普通函数几乎是一样的,这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator 3、状态挂起:生成器使用yield语句返回一个值。yield语句挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行

六、迭代器

可迭代对象(Iterable):列表,字符串,元组,字典

In [1]: [].__iter__()
Out[1]: <list_iterator at 0x7f2cb7187358>

In [2]: ().__iter__()
Out[2]: <tuple_iterator at 0x7f2cb7055940>

In [3]: "str".__iter__()
Out[3]: <str_iterator at 0x7f2cb70cdf28>

In [4]: {'str':'11'}.__iter__()
Out[4]: <dict_keyiterator at 0x7f2cb6400f48>

In [5]: 123.__iter__()
  File "<ipython-input-5-3f844d9820a0>", line 1
    123.__iter__()
               ^
SyntaxError: invalid syntax

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

In [14]: L = (i for i in range(5))

In [15]: next(L)
Out[15]: 0

In [16]: next(L)
Out[16]: 1

In [17]: next(L)
Out[17]: 2

In [18]: next(L)
Out[18]: 3

In [19]: next(L)
Out[19]: 4

In [20]: next(L)
----------------------------------------------------------------------
StopIteration                        Traceback (most recent call last)
<ipython-input-20-26788c61f395> in <module>()
----> 1 next(L)

StopIteration: 

通过以上的两个例子,我们可以发现__iter__()方法返回迭代器对象本身,next()方法返回容器的下一个元素,直到结尾抛出StopIteration异常。 可迭代对象可以通过iter()函数转换为迭代器对象

In [23]: L = [1,2,3]
In [24]: l1 =iter(L)

In [25]: next(l1)
Out[25]: 1

In [26]: next(l1)
Out[26]: 2

In [27]: next(l1)
Out[27]: 3

迭代器小结: 1、可迭代对象是实现了__iter__()方法的对象,iter()可以返回一个迭代器对象 2、迭代器对象是实现了__next__()方法的对象,其中他的__iter__()返回的是创建迭代器对象本身

In [28]: L = (i for i in range(5))

In [29]: L.__iter__()
Out[29]: <generator object <genexpr> at 0x7f2cb6420a98>

In [30]: [].__iter__()
Out[30]: <list_iterator at 0x7f2cb512fda0>