前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python3 基础(4)-装饰器、生成

python3 基础(4)-装饰器、生成

作者头像
py3study
发布2020-01-03 18:03:05
4050
发布2020-01-03 18:03:05
举报
文章被收录于专栏:python3python3

装饰器(本质:函数)

  1. 定义:为其他函数添加附加功能(装饰器对于被装饰函数是“透明的”、“不存在的”);执行过程:调用被装饰函数实际上是调用修饰函数
  2. 原则:
    1. 不能修改被装饰函数的源代码;
    2. 不能修改被装饰函数的调用方式。
  3. 实现装饰器的知识补充:
    1. 函数即“变量”:
      1. 调用前先定义(即要先有内存地址空间、函数的执行顺序)
      2. 可赋值给其他函数名
      clipboard.png
      clipboard.png

      def bar(): print("in the bar") #函数bar()在以下三处位置,只有1、2处时,调用foo()才正确执行 ------------------------------ #1# def foo(): print("in teh foo") ? ?bar() #2# foo() #3#

    2. 高阶函数
      1. 条件一:一个函数名(被装饰的函数)当作实参传给另一个函数(装饰函数);(满足原则一不修改被装饰函数的源代码)
      2. 条件二:返回值中包含函数名(装饰函数)。(满足原则二不修改被装饰函数的调用方式)
    3. 函数嵌套
  4. 举例:
    1. 统计运行时间的装饰器 带无参数、固定参数、无固定参数的函数 print("分割线".center(50,"=")) import time def timmer(func): def wrapper(*args,**kwargs): ? ? ? ?start_time=time.time() ? ? ? ?res = func(*args,**kwargs) ? #这里的参数由wrapper函数的参数传递 ? ? ? ?stop_time=time.time() print("func run time is %s"%(stop_time-start_time)) return res ? ? ? ? ? ?#返回被装饰函数的返回值 return wrapper ? #返回装饰函数wrapper的内存地址 @timmer ?#同test1=timmer(test1); ? ? ? ? ? ?# 理解:执行timmer(test1),得到返回值wrapper内存地址,再把wrapper内存地址赋值给test1 def test1(): ? ?time.sleep(1) print("my name is test1") return "test1" @timmer ? #同test2=timmer(test2); ? ? ? ? ? ?# 理解:执行timmer(test2),得到返回值wrapper内存地址,再把wrapper内存地址赋值给test2 def test2(name,age): ? ?time.sleep(1) print("my name is %s,I'm %s" %(name,age)) test1() ? #要配合上面@timmer使用,同timmer(test1)() print(test1()) test2("chen",40) ? #要配合上面@timmer使用,同timmer(test2)("chen",40) print(test2("chen",40)) #结果 my name is test1 func run time is 1.000598430633545 my name is test1 func run time is 1.0012288093566895 test1 my name is chen,I'm 40 func run time is 1.0007030963897705 my name is chen,I'm 40 func run time is 1.0007030963897705 None
    2. 页面登录认证 无参装饰器:有参装饰器:user,passwd = "chen","123456" def auth(func): def wrapper(*args,**kwargs): ? ? ? ?username = input("Username: ").strip() ? ? ? ?password = input("Password: ").strip() if user == username and passwd == password: print("\033[32;1m通过本地认证!\033[0m") return func(*args,**kwargs) else: exit("\033[31;1m错误的用户名或密码\033[0m") return wrapper 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") return "from bbs" index() home() bbs() #结果 welcome to index page Username: chen Password: 123456 通过本地认证! welcome to home page Username: chen Password: 123456 通过本地认证! welcome to bbs pageuser,passwd = "chen","123456" def auth(auth_type): def outer_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("\033[32;1m通过本地认证!\033[0m") return func(*args,**kwargs) else: exit("\033[31;1m错误的用户名或密码\033[0m") elif auth_type == "ldap": print("\033[32;1m远程认证!\033[0m") return wrapper return outer_wrapper def index(): print("welcome to index page") @auth(auth_type="local") ? #同home=auth(auth_type="local")(home) def home(): print("welcome to home page") return "from home" @auth(auth_type="ldap") ? #同bbs=auth(auth_type="ldap")(bbs) def bbs(): print("welcome to bbs page") return "from bbs" index() home() bbs() #结果 welcome to index page Username: chen Password: 123456 通过本地认证! welcome to home page 远程认证!

生成器?

????创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

????而生成器节省大量的空间,因为生成器只有在调用时才会生成相应的数据。

  1. 知识补充:
    1. 列表生成式(列表解析): a = [i*2 for i in range(10)] print(a) 输出: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
    2. 斐波那契数列: def fib(max): ? ?n, a, b = 0, 0, 1 while n < max: print(b) ? ? ? ?a, b = b, a + b ? ? ? ?n = n + 1 return 'done' fib(10) 输出: 1 1 2 3 5 8 13 21 34 55
  2. 生成器特点:
    1. 生成器只有在调用时才会生成相应的数据
    2. 只记录当前位置,用到哪记录到哪
    3. 只有一个方法__next__(),python2 为next()
  3. 生成器的实现:
    1. 方法一:列表生成式。 a = ( i*2 for i in range(10)) print(a) 输出: <generator object <genexpr> at 0x054D1F30>
    2. 方法二:在函数定义中包含yield关键字。

    这时,这个函数就不再是一个普通函数,首次调用__next__()的时候执行生成器函数,遇到yield语句时返回,再次执行(__next__()或send()或for、while等)时将从上次返回的yield语句处继续执行。

    def fib(max): ? ?n, a, b = 0, 0, 1 ? ?while n < max: ? ? ? ?# print(b) ? ? ? ?yield b ? ? ? ?a, b = b, a + b ? ? ? ?n = n + 1 ? ?return "done"fib(5)print(fib(5)) ? #此时不会获取return的值输出:<generator object fib at 0x04F8AF90>

  4. 获取生成器的值:
    1. 使用__next__() print(a.__next__()) print(a.__next__()) print(a.__next__()) f = fib(5) print(f.__next__()) print(f.__next__())
    2. for、while循环 f = fib(5) for i in f: print(i)
  5. 应用:
    1. 当使用__next__()获取生成器的值的数量超过总的数量时: def fib(max): ? ?n, a, b = 0, 0, 1 while n < max: # print(b) yield b ? ? ? ?a, b = b, a + b ? ? ? ?n = n + 1 return "done" ? #作为错误提示信息 f = fib(100) #当获取生成器的值的数量超过总的数量时会报错 while True: ? ? x = f.__next__() print('f:', x) #解决方式:捕获StopIteration错误 while True: try: ? ? ? ? x = f.__next__() print('f:', x) except StopIteration as e: print('Generator return value:', e.value) break
    2. 在单线程实现并发运算的效果(携程??) 补充:send()用于给yield传值,但是send传值时,要求生成器已执行到yield语句处(就是send前面至少要有一个__next__(),这样才能保证生成器运行到yield处 import time def consumer(name): print("%s 准备吃包子啦!" %name) while True: ? ? ? ?baozi = yield ? #这里的yield由send传值 print("[%s]包子来了,被[%s]吃了!" %(baozi,name)) def producer(name): ? ?c = consumer('A') ? ?c2 = consumer('B') ? ?c3 = consumer('C') ? ?c.__next__() ? ?c2.__next__() ? ?c3.__next__() print("师傅开始蒸包子啦!") for i in ["猪肉馅","韭菜馅","白菜馅","豆沙馅"]: ? ? ? ?time.sleep(1) print("%s包子出炉了!"%i) ? ? ? ?c.send(i) ? ? ? ?c2.send(i) ? ? ? ?c3.send(i) producer("alex")

迭代器

  1. Iterable对象:可以直接作用于for循环的对象统称为可迭代对象
    1. 集合数据类型,如list、tuple、dict、set、str等;
    2. 生成器generator,包括带yield的generator function。
    3. 内置函数:map()、filter()、zip(a,b)
  2. Iterator对象:可以被next()函数调用并不断返回下一个值的对象称为迭代器对象
    1. 生成器generator
    2. 内置函数:map()、filter()、zip(a,b)
  3. 判断一个对象是否是Iterable对象: #使用isinstance() >>> 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
  4. 判断一个对象是否是Iterator对象: #使用isinstance() >>> from collections import Iterator >>> isinstance((x for x in range(10)), Iterator) True >>> isinstance([], Iterator) False >>> isinstance({}, Iterator) False >>> isinstance('abc', Iterator) False
  5. 把Iterable对象变成Iterator对象: #使用iter()函数: >>> isinstance(iter([]), Iterator) True >>> isinstance(iter('abc'), Iterator) True #将列表变为iterator对象 a = [1,2,3,4] b=iter(a) print(type(b)) print(b.__next__()) print(b.__next__()) #输出 <class 'list_iterator'> 1 2
  6. 你可能会问,为什么list、dict、str等数据类型不是Iterator?
    1. 这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
    2. Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
  7. 补充:
    1. Python的for循环本质上就是通过不断调用next()函数实现的,例如: for x in [1, 2, 3, 4, 5]: pass #等价于 it = iter([1, 2, 3, 4, 5]) ? # 首先获得Iterator对象: while True: try: x = next(it) ? # 获得下一个值: except StopIteration: ? # 遇到StopIteration就退出循环 break
本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-09-25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com