码迷,mamicode.com
首页 > 编程语言 > 详细

python学习笔记-(八)装饰器、生成器&迭代器

时间:2016-08-18 12:32:54      阅读:255      评论:0      收藏:0      [点我收藏+]

标签:

本节课程内容概览:

1.装饰器

2.列表生成式&迭代器&生成器

3.json&pickle数据序列化

 

1. 装饰器

1.1 定义:

本质上是个函数,功能是装饰其他函数—就是为其他函数添加附加功能 

1.2 装饰器原则:

1)  不能修改被装饰函数的源代码;

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

1.3 实现装饰器知识储备:

1.3.1 函数即“变量”

定义一个函数相当于把函数体赋值给了函数名

变量可以指向函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> def func():
......    print("这是一个函数")
>>> print(func)
>>> func()
<function func at 0x0000000000D2CD08>
这是一个函数
#func()是函数调用,而func是函数本身。
 
#要获得函数调用结果,我们可以把结果赋值给变量:
>>> a = func()
这是一个函数
 
#函数本身赋值给变量
>>> a = func
<function func at 0x0000000000D2CD08>
 
#结论:函数本身也可以赋值给变量,即:变量可以指向函数。
 
#猜想:是否可以通过变量直接调用函数?
>>> a=func
>>> a()
这是一个函数
#说明变量指向了func函数本身,直接调用a()和func()完全相同

1.3.2 高阶函数

高阶函数:能接收函数作为参数的函数。

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

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

    1
    2
    3
    4
    5
    6
    7
    >>> def bar():
        ......print(‘in the bar‘)
    >>> def foo(func):
        ......res=func()
        ......return res
    >>> foo(bar)
    in the bar
  2. 函数的返回值包含n个函数,n>0(用处:不修改函数调用方式)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    >>>def bar():
        ......time.sleep(3)
        ......print("in the bar")
    >>> def test2(func):
        ......print(func)
        ......return func
    >>> print(test2(bar))
    >>> bar = test2(bar)
    >>> bar()
    <function bar at 0x0000000000D6CD08>
    <function bar at 0x0000000000D6CD08>
    <function bar at 0x0000000000D6CD08>
    in the bar 

高阶函数示例

  • map()

map函数会根据提供的函数对指定序列做映射。

map函数的定义:
map(function, sequence[, sequence, ...]) -> list
通过定义可以看到,这个函数的第一个参数是一个函数,剩下的参数是一个或多个序列,返回值是一个集合。
function可以理解为是一个一对一或多对一函数,map的作用是以参数序列中的每一个元素调用function函数,返回包含每次function函数返回值的list。

1
2
3
4
5
>>> def f(x):
   ...... return x*x
>>> r = map(f,[1,2,3,4,5,6,7,8,9])
>>> print(list(r))
[149162536496481]
  • reduce()

reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算。效果就是:

1
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
1
2
3
4
5
>>> from functools import reduce
>>> def add(x, y):
    ......return + y
>>> print(reduce(add, [13579]))
25
  • filter()

filter函数会对指定序列执行过滤操作。
filter函数的定义:
filter(function or None, sequence) -> list, tuple, or string
function是一个谓词函数,接受一个参数,返回布尔值True或False。
filter函数会对序列参数sequence中的每个元素调用function函数,最后返回的结果包含调用结果为True的元素。
返回值的类型和参数sequence的类型相同。

1
2
3
4
>>> def is_odd(n):
        ...return % 2 == 1
>>> print(list(filter(is_odd, [1245691015])))
[15915]
  • sorted()

 对列表内容进行正向排序,即可以保留原列表,又能得到已经排序好的列表;

1
2
3
4
5
>>> a = {6:2,8:0,1:4,-5:6,99:11,4:22}
>>> print(sorted(a.items())) #按key排序
[(-56), (14), (422), (62), (80), (9911)]
>>> print(sorted(a.items(),key=lambda x:x[1])) #按value排序
[(80), (62), (14), (-56), (9911), (422)]

1.3.3 嵌套函数

定义:在一个函数体内用def去声明一个新函数

1
2
3
4
5
6
7
8
>>> def foo():         #定义函数foo()
     ...m=3            #定义变量m=3;
     ...def bar():     #在foo内定义函数bar()
         ...n=4        #定义局部变量n=4
         ...print(m+n)  #m相当于函数bar()的全局变量
     ...bar()         #foo()函数内调用函数bar()
>>> foo()             #调用foo()函数
7  

1.3.4 装饰器

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

不带参数的装饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#装饰器
import time
def timer(func):
    def deco():
        start_time=time.time()
        func()                    #执行形参func()
        end_time=time.time()
        print("func runing time is %s"%(end_time-start_time))
    return deco                #返回函数deco的内存地址
def test1():
    print("in the test1")
    time.sleep(1)
  
test1 = timer(test1)            #重新赋值test1  此时test1=deco的内存地址
test1()                         #执行test1
###########打印输出###########
#in the test1
#func runing time is 1.0000572204589844

带固定参数的装饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#装饰器
import time
def timer(func):
    def deco(name):
        start_time=time.time()
        func(name)                    #执行形参func()
        end_time=time.time()
        print("func runing time is %s"%(end_time-start_time))
    return deco                #返回函数deco的内存地址
@timer                          #test1 = timer(test1)  test1=deco
def test1(name):
    print("in the test1 name %s"%name)
    time.sleep(1)
  
test1("cc")                         #执行test1
###########打印输出###########
#in the test1 name cc
#func runing time is 1.0000572204589844

带返回值的装饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#装饰器
import time
def timer(func):
    def deco(*args,**kwargs):
        start_time=time.time()
        res = func(*args,**kwargs)                    #执行形参func()
        end_time=time.time()
        print("func runing time is %s"%(end_time-start_time))
        return res
    return deco                #返回函数deco的内存地址
@timer                          #test1 = timer(test1)  test1=deco
def test1(name):
    print("in the test1 name %s"%name)
    time.sleep(1)
    return "return form test1"
  
print(test1("cc"))                  #执行test1
###########打印输出###########
#in the test1 name cc
#func runing time is 1.0000572204589844
#return form test1

通过以上我们会发现一个问题,选用的装饰器只能选择统一带形参或者统一不带形参;那么问题来了,我想要用一个装饰器,带形参的能调用,不带形参的也能调用,可不可以呢?

带不固定参数的装饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 装饰器
import time
 
def timer(func):
    def deco(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)  # 执行形参func()
        end_time = time.time()
        print("func runing time is %s" % (end_time - start_time))
    return deco  # 返回函数deco的内存地址
@timer  # test1 = timer(test1)  test1=deco
def test1(name):
    print("in the test1 name %s" % name)
    time.sleep(1)
 
@timer
def test2():
    print("in the test2 no name")
    time.sleep(1)
 
test1("cc")  # 执行test1
test2()        # 执行test2
###########打印输出###########
in the test1 name cc
func runing time is 1.0010571479797363
in the test2 no name
func runing time is 1.0000572204589844

终极版装饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import time
user,passwd = ‘cc‘,‘123123‘
def auth(auth_type):
    print(‘auth func:‘,auth_type)
    def outer_wrapper(func):
        def wrapper(*args,**kwargs):
            print("wrapper func args:",*args,**kwargs)
            if auth_type == ‘local‘:
                username = input("Username:").strip()
                password = input("Password:").strip()
                if user == username and passwd == password:
                    print("User has passed authentication!")
                    res = func(*args,**kwargs)
                    print("---after authentication")
                    return res
                else:
                    exit("Invalid username or password!")
            elif auth_type == "ldap":
                print("搞毛线ldap,不会。。。")
        return wrapper
    return outer_wrapper
def index():
    print("welcome to index page")
@auth(auth_type=‘local‘)#home = wrapper()
def home():
    print("welcome to home page")
    return  "from home"
@auth(auth_type="ldap")
def bbs():
    print("welcome to bbs page")
index()
print(home()) #wrapper()
bbs()

2. 迭代器&生成器

2.1 列表生成式

列表生成式,是Python内置的一种极其强大的生成list的表达式。

如果要生成一个list [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9] 可以用 range(1 , 10):

1
2
#print(list(range(1,10)))
[123456789]

如果要生成[1x1, 2x2, 3x3, ..., 10x10]怎么做?

1
2
3
4
5
6
= []
for in range(1,10):
    l.append(i*i)
print(l)
####打印输出####
#[1, 4, 9, 16, 25, 36, 49, 64, 81]

而列表生成式则可以用一行语句代替循环生成上面的list:

1
2
>>> print([x*for in range(1,10)])
[149162536496481]

for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:

1
2
>>> print([x*for in range(1,10if %2 ==0])
[4163664]

2.2 生成器

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

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

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

1
2
3
4
5
6
>>> L = [x * for in range(10)]
>>> print(L)
[0149162536496481]
>>> g = (x * for in range(10))
>>> print(g)
<generator object <genexpr> at 0x1022ef630>

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

我们可以直接打印出list的每一个元素;而generator里的每一个元素我们可以通过next()函数获取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
>>> g = (x * for in range(10))
>>> print(g)
>>> print(next(g))
>>> print(next(g))
>>> print(next(g))
>>> print(next(g))
>>> print(next(g))
>>> print(next(g))
>>> print(next(g))
>>> print(next(g))
>>> print(next(g))
>>> print(next(g))
>>> print(next(g))
0
1
4
9
16
25
36
49
64
81
Traceback (most recent call last):
  File "XXX", line 32in <module>
    print(next(g))
StopIteration

上面我们可以看到,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

当然,我们也可以通过for循环去调取元素值:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> g = (x * for in range(10))
>>> for in g:
>>>    print(i)
0
1
4
9
16
25
36
49
64
81

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#斐波拉契数列
def fib(max):
    n, a, b = 001
    while n < max:
        print(b)
        a, b = b, a + b
        += 1
    return ‘done‘
  
fib(5)
###########打印输出###########
# 1
# 1
# 2
# 3
# 5

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#斐波拉契数列
def fib(max):
    n, a, b = 001
    while n < max:
        yield(b)
        a, b = b, a + b
        += 1
    return ‘done‘
  
= fib(5)
print(f)
for in f:
    print(i)
###########打印输出###########
#<generator object fib at 0x000000000110A468>
#1
#1
#2
#3
#5

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def fib(max):
    n, a, b = 001
    while n < max:
        yield b
        a, b = b, a + b
        = + 1
    return ‘done‘
= fib(6)
while True:
    try:
        = 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

生成器的特点:

1)生成器只有在调用时才会生成相应的数据;

2)只记录当前位置;

3)只有一个__next__()方法;

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

import time
def consumer(name):
    print("%s 准备吃包子啦!" %name)
    while True:
       baozi = yield
 
       print("包子[%s]来了,被[%s]吃了!" %(baozi,name))
 
 
def producer(name):
    = consumer(‘A‘)
    c2 = consumer(‘B‘)
    c.__next__()
    c2.__next__()
    print("老子开始准备做包子啦!")
    for in range(10):
        time.sleep(1)
        print("做了2个包子!")
        c.send(i)
        c2.send(i)
 
producer("cc")

2.3 迭代器

3.json&pickle数据序列化

python学习笔记-(八)装饰器、生成器&迭代器

标签:

原文地址:http://www.cnblogs.com/wangsen-123/p/5783344.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!