Python装饰器、生成器、内置函数、Json-Day05

 装饰器

装饰器本质上就是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离大量与函数功能本身无关的雷同代码并继续重用。概括讲,装饰器的作用就是为依据存在的对象添加额外的功能。

定义:本质是函数,(装饰其他函数)就是为其他函数添加附加功能
原则:
  • 不能修改被装饰的函数的源代码( 再不修改被装饰函数源代码的情况下为其添加功能)
  • 不能修改被装饰的函数的调用方式
 
实现装饰器功能知识储备:
  • 函数既“变量” 
    • 函数的参数可以接收变量,也可以接受函数,这里引用函数即变量的概念

在python里面函数就是一个变量,函数名就是一个变量,这个函数名里面存的是这个函数的内存地址,它把函数体放到内存里,在调用的时候从函数名里面的这个内存地址找到函数体然后运行这个函数。前面的博客说函数的时候,说过函数名后面加上小括号就是调用这个函数,如果只写这个函数名的话,打印一下就是这个函数的内存地址。

1 def test():
2     print('Hello Word')
3 
4 
5 print(test)  # 打印函数的内存地址 #<function test at 0x100662e18>
6 test()  # 调用函数
  • 高阶函数
    • 把一个函数名当做实参传给另外一个函数
    • 返回值中包含函数名(不修改函数的调用方式) *是不是不理解很懵逼,没事,一会用到*
    • 当传递函数作为变量时,被称为高阶函数 
 1 import time
 2 def bar():
 3     time.sleep(3)
 4     print('in the  bar !!')
 5 
 6 def test1(func):
 7     start_tiem = time.time()
 8     func()# run bar()
 9     stop_time = time.time()
10     print('the func run time is %s' %(stop_time-start_tiem))
11 test1(bar)

强行插入知识点: 嵌套函数

 1 def fun():
 2     print('in fun ')
 3     def bar(): #函数的嵌套
 4         print('in the bar ')
 5     bar()
 6 fun()
 7 #--------------------------#
 8 name = "Baylor"
 9 def change_name():
10 
11     name = "Baylor2"
12     def change_name2():
13         name = "Baylor3"
14         print("第3层打", name)
15 
16     change_name2()  # 调用内层函数
17     print("第2层打印", name)
18 
19 change_name()
20 print("最外层打印", name)

嵌套函数中的作用域顺序-由内而外的寻找符合要求的参数

第一个装饰器

但每一次都需要从新对 test1 进行函数赋值,我们只需要通过装饰器的特殊书写方式,在需要增加新功能的函数上增加“@装饰器函数名”就可以完成装饰器的使用 

 1 def timer(func):
 2     def deco():#函数的嵌套 timer(test1) func = test1
 3         start_time = time.time()
 4         func()  #run test1
 5         stop_time = time.time()
 6         print(' the func time is %s'%(stop_time - start_time))
 7     return deco  #高阶函数- 返回值中包含函数名
 8 
 9 @timer   # test1 = timer(test1)
10 def test1():
11     time.sleep(3)
12     print('in the test1')
13 @timer
14 def test2():
15     time.sleep(2)
16     print('in the test2')
17 
18 #test1 = timer(test1)
19 test1()# --->deco
20 test2()
21 print(timer(test1)) #打印test1 内存地址

所以我们说:高阶函数+嵌套函数 =>装饰器 ,我们通过高阶函数+函数嵌套实现了以上的装饰器,他符合不改变调用方式,为函数增加新功能。

问题:被装饰的函数如果有参数呢?

 1 def w1(func):
 2     def inner(arg):
 3         # 验证1
 4         # 验证2
 5         # 验证3
 6         return func(arg)
 7     return inner
 8 
 9 @w1
10 def f1(arg):
11     print ('f1')
12 
13 
14 def w1(func):
15     def inner(arg1,arg2,arg3):
16         # 验证1
17         # 验证2
18         # 验证3
19         return func(arg1,arg2,arg3)
20     return inner
21 
22 @w1
23 def f1(arg1,arg2,arg3):
24     print ('f1')

问题:可以装饰具有处理n个参数的函数的装饰器?

 1 def w1(func):
 2     def inner(*args, **kwargs):
 3         # 验证1
 4         # 验证2
 5         # 验证3
 6         print(*args,**kwargs)
 7 
 8         return func(*args, **kwargs)
 9     return inner
10 
11 @w1
12 def f1(arg1, arg2, arg3):
13     print('f1')
14 
15 f1(1,2,3)

 

如果要装饰的函数带有参数时,因为你也不知道到底被装饰的函数会传什么参数,所以可以使用可变参数和关键字参数来接收所有的参数

 1 import time
 2 def timer(func):
 3     def deco(*args,**kwargs):#函数的嵌套 timer(test1) func = test1
 4         start_time = time.time()
 5         func(*args,**kwargs)  #run test1
 6         stop_time = time.time()
 7         print(' the func time is %s'%(stop_time - start_time))
 8     return deco  #高阶函数
 9 
10 @timer   # test1 = timer(test1)
11 def test1():
12     time.sleep(3)
13     print('in the test1')
14 @timer  # test2 = timer(test2) = deco test2(name) = deco(name)
15 def test2(name,arg):
16     time.sleep(2)
17     print('in the test2 %s %s',name,arg)
18 
19 test1()# --->deco
20 test2('Baylor',28)

在函数的传递参数过程中,我们的装饰器不能满足带参数的传递,以上,实现了当函数有参数传递的过程中也可以实现装饰器的新功能

终极函数装饰-补充知识

 1 import time
 2 user,passwd = 'jinyu','abc123'
 3 def auth(auth_type):
 4     def outer_wrapper(func):
 5         print('auth func:',auth_type)
 6         def wrapper(*args,**kwargs):
 7             print('------',auth_type)
 8             if auth_type =='local':
 9                 username = input('Username:')
10                 password = input('Password')
11                 if user == username and passwd == password:
12 
13                     print('\033[31;1mUser  has password authentication\033[0m')
14                     # res = func(*args,**kwargs)
15                     res = func(*args,**kwargs) #1,2
16                     print('-----afte authentication-----',res)
17                     res.append(3)
18                     print(res)
19                     return res,'方法已运行完毕' # 结果
20                 else:
21                     print('\033[32;1m Invalid username or password\033[0m')
22             elif auth_type =='ldap':
23                 print('哈哈哈哈哈哈!!!ldap')
24         return wrapper
25     return outer_wrapper
26 
27 def index():
28     print('welcome to index page!')
29 @auth(auth_type = 'local')#home  = wraapper
30 def home():
31     print('welcome to home page!')
32     return [1,2]
33 @auth(auth_type = 'ldap')
34 def bbs():
35     print('welcome to bbs page!')
36 
37 index()
38 home()
39 # bbs()

三层函数形成了终结装饰器,通过装饰器所传的参数能够进行装饰器功能判断,提供了更好的扩展性 

 列表生成式-作用:使代码更加简洁

1 a = [i*2 for i in range(10)]   #列表生成式 3行代码一行搞定
2 print(a)
3 
4 a = []
5 for i in range(10):
6     a.append(i*2)
7 print(a)

生成器

  • 只有在调用时才会生成相应的数据----生成器:你用到这个里面的数据的时候它才会生成,这样就比较省内存,因为是需要这个值的时候,才会在内存里面产生
  • 只记录当前位置,只有一个__next__()方法 2.7next() 
1 a =(i * 2 for i in range(10)) #如果要打印generator中的元素需要借助next方法  需要循环才能取出
2 # a =[i * 2 for i in range(10)] # list的元素我们可以一个个打印出,
3 print(a) #<generator object <genexpr> at 0x10228f2b0>
4 print(next(a))
5 print(next(a))
6 print(a.__next__())
7 print(a.__next__())

 小练习-斐波那契数列 

 1 def fib(max):
 2     n, a, b = 0, 0, 1
 3     while n < max:
 4         print(b)
 5         a, b = b, a + b  # t(b,a+b) a=t[0]-1 b=t[1]-2    a+b等同于 t的下标相加
 6         n = n + 1
 7     return 'done'  # 无用
 8 
 9 
10 fib(10)
11 
12 # 如果把这个改进成 yield 生成器,通过 __next__调用 只需要 把print(b) 改成 yield b 即可
13 temp = []
14 
15 
16 def fib(max):
17     n, a, b = 0, 0, 1
18     while n < max:
19         yield b
20         a, b = b, a + b  # t(b,a+b) a=t[0]-1 b=t[1]-2    a+b等同于 t的下标相加
21         n = n + 1
22     return 'done'  # 无用
23 
24 x = fib(10)
25 print(x)
26 print(x.__next__())
27 print(x.__next__())
28 print(x.__next__())
29 print(x.__next__()) 

匿名函数 ---也叫 lambda函数

 1 #匿名函数就是不需要显式的指定函数
 2 
 3 # 这段代码
 4 def calc(n):
 5     return n * n
 6 
 7 print(calc(10))
 8 
 9 # 换成匿名函数
10 calc = lambda n: n * n
11 print(calc(10))
12 #你也许会说,用上这个东西没感觉有毛方便呀, 。。。。呵呵,如果是这么用,确实没毛线改进,不过匿名函数主要是和其它函数搭配使用的呢,如下
13 #x=[1, 5, 7, 4, 8]
14 res = map(lambda x: x * 2, [1, 5, 7, 4, 8]) #map会根据提供的函数对指定序列做映射。
15 for i in res:
16     print(i)
#匿名函数只能处理比较简单的处理逻辑,只能写简单的表达式,不能写循环 判断,比如三元运算符

 json 处理

json是一种所有语言中都通用的key-value数据结构的数据类型,很像python中的字典,json处理使用json模块,json模块有下面常用的方法:

 1 import json
 2 
 3 dic = {"name": "niuniu", "age": 18}
 4 print(json.dumps(dic))  # 把字典转成json串
 5 fj = open('a.json', 'w')
 6 print(json.dump(dic, fj))  # 把字典转换成的json串写到一个文件里面
 7 s_json = '{"name":"niuniu","age":20,"status":true}'
 8 print(json.loads(s_json))  # 把json串转换成字典
 9 
10 # 先创建b.json文件 运行运行此代码*{"name": "chenjianguo", "age": 18}*
11 fr = open('b.json', 'r')
12 print(json.load(fr))  # 从文件中读取json数据,然后转成字

首先说明基本功能:
  • dumps是将dict转化成str格式,loads是将str转化成dict格式。
  • dump和load也是类似的功能,只是与文件操作结合起来了。
  • dump 是把字典转成son串格式写   load是把json串转成字典读-(json必须双引号)

  内置函数详细介绍 https://docs.python.org/3/library/functions.html?highlight=built#ord 

资料补充

生成器并行

 1 import time
 2 def consumer(name):
 3     print('%s 我准备吃包子了'%name)
 4     while True:
 5         baozi = yield #保存当前状态返回,
 6         print('包子[%s]来了,被[%s]吃了'%(baozi,name))
 7 # c = consumer('houzi')
 8 # c.__next__()
 9 #b = '韭菜馅'
10 #c.send(b) # 调用yield  并传值
11 #c.__next__()
12 def producer(name):
13     c = consumer('A')  #生成
14     c2 = consumer('B')
15     c.__next__()
16     c2.__next__()
17     print('开始准备做包子了。。。。。。')
18     for i in range(10):
19         time.sleep(1)
20         print('做了2个包子!!!!')
21         c.send(i)
22         c2.send(i)
23 
24 producer('houzi')

 使用类来写装饰器-强势插入新知识

分享一下,需要用类里面的__call__方法,__call__方法就是可以把这个实例当成一个函数来调用,如果正常使用类里面的方法的话,实例方法要先实例化类,然后才能调用,静态方法、类方法则需要用类名或者实例化之后的对象来调用,而实例化之后的这个对象,是不能被调用的,__call__方法就是把这个实例变成一个可以调用的对象,也就是说实例化之后,这个对象就可以和一个普通函数一样被调用。

1 class Foo:
2     def __call__(self, *args, **kwargs):
3         print('call....')
4     def test(self):#
5         print('test....')
6 if __name__ == '__main__':
7     t = Foo()#实例化类
8     t.test()#正常调用实例方法
9     t()#直接调用实例化之后的对象

理解了上面的之后,就可以使用class来写一个装饰器了,计算程序的运行时间,当然思想和以前用函数写装饰器是一样的
 1 class Fuck(object):
 2     def __init__(self, func):
 3         self.func = func
 4     def __call__(self, *args, **kwargs):
 5         import time
 6         start_time = time.time()
 7         res = self.func(*args, **kwargs)
 8         end_time = time.time()
 9         print('the function "%s" run time is %s' % (self.func.__name__,
10                                                     (end_time - start_time)))
11         return res
12 @Fuck
13 def run(name):
14     import time
15     time.sleep(1)
16     return 'sb_%s' % name
17 print(run('hyf'))

 

posted on 2018-05-16 16:26  Baylor_Chen  阅读(231)  评论(0编辑  收藏  举报

导航