zoukankan      html  css  js  c++  java
  • 疫情环境下的网络学习笔记 python 3.24

    3.24

    上节课复习

    1. 无参装饰器

    2. 模板

      def outter(func):
      	def wrapper(*args,**kwargs):
      		res = func(*args,**kwargs)
      		return res
      	return wrapper
      
    3. 开放封闭原则

      对拓展新功能开放,对修改源代码封闭

    4. 装饰器

      装饰器就是再不修改装饰对象源代码与调用方式的前提下为其添加新功能

    5. 需求:不改变源代码和调用方式,为原函数加上统计运行时间的功能

    问题出现:wrapper(*args,**kwargs)中接收的参数只能是被装饰对象的参数,而其包里面已经接收了func函数体,要是想给wrapper加参数,就需要别的方法

    @ 只要碰到 @符号,就会在他后面加括号,当作一个函数运行,并且将其下方的函数当作参数传入这个函数

    @print  # 相当于print(home),将返回值赋值给 home
    def home(a,b,c):
    	pass
    

    补充

    functools wraps

    对被装饰的对象偷梁换柱,应该将原函数名字指向的内存地址转而指向wrapper,wrapper的参数和返回值都应该和原函数一模一样。一模一样要包括原函数的属性,如注释信息等等

    # 应该在写装饰器的时候做以下类似的事情
    wrapper.__doc__ = index.__doc__  # 查看注释信息
    wrapper.__name__ = index.__name__ 
    

    python提供了一种功能,将原函数的所有属性复制给wrapper:

    from functools import wraps

    @wraps

    from functools import wraps
    
    def outter(funcs):
    	@wraps(func)  # 将func的所有属性,如__name__,__doc__等等,都覆盖给wrappe,相当于上面一行行 =赋值
    	def wrapper(*args,**kwargs):
    		...
    		return res
    	return wrapper
    

    实际上 @wraps(func) 就是一个有参装饰器,一个嵌套三层定义的函数

    课堂笔记

    1. 有参装饰器

    2. 迭代器

      for循环的工作原理

    3. 生成器 yield

    4. 三元表达式

    5. 生成式

    有参装饰器

    • 实际上是在原装饰器的基础上,再套一层装饰器,并在这一层加入需要的参数

    装饰器中,wrapper传入的参数只能是被装饰对象的格式,且由于语法糖 @ 的限制,outter函数只能有一个参数,用来接收被装饰函数的内存地址

    wrapper参数不能动,outter参数也不能动,在我们遇到需要为装饰器新增参数的时候,就需要一种新的方法为装饰器传参

    例子:为函数加一个认证功能的装饰器

    def auth(func):
    	def wrapper(*args,**kwarg):
    		name = input('input name:').strip()
    		pwd = input('input pwd:').strip()
    		if name == 'deimos' and pwd = '123':  # 应该基于文件验证
    			print('succ')
    			res = func(*args,**kwargs)
    			return res
    		else:
    			print('fail')
    	return wrapper
    

    实际上,用于验证的账号密码来源不一定来源于文件,也可能来自数据库或着其他,应该对不同的数据来源加以区分,这就需要数据来源作为一个参数输入装饰器函数

    为此,不用语法糖@语法,可以在outter的形参中传入除被装饰函数以外别的参数,可以解决,就是有点low

    index = outter(index,'file')  # 装饰index,验证信息来自文件
    myspql = outter(index,'sql')  # 装饰index,验证信息来自sql
    ...
    

    可以再在原装饰器外面加一个装饰器,把需要的参数在最外层传入

    def auth(db_type):
    	def deco(func):
    		def wrapper(*args,**kwargs):
    			res = func(*args,**kwargs)
    			return res
    		return wrapper
    	return deco
    	
    # deco = auth(db_type)
    # @deco
    @auth(db_type)  # 直接用auth
    
    
    第一层:调用auth,返回deco,相当于
    @auth(db_type)
    

    总结

    • 装饰器最里一层wrapper需要与被装饰函数绑定,形参是固定的,第二层outter需要传入被装饰的函数体,且有@语法的限制,形参也是固定的,固定是被装饰函数的函数体

    • 最外一层装饰器是为了往函数内传新功能需要的参数,不与任何别的函数绑定,想写几个参数就写几个参数

    • 最终模板

      def 有参装饰器(x,y,z):
          def outter(func):
              def wrapper(*args, **kwargs):
                  res = func(*args, **kwargs)
                  return res
              return wrapper
          return outter
      
      @有参装饰器(新传入的参数)
      def 被装饰的函数():
      	...
      
    • 上面加参,底下返回, 缩进定义

    迭代器

    什么是

    迭代器是迭代取值的工具,迭代是一个重复的过程,每次重复都是基于上一次结果而继续,单纯的重复不是迭代

    为何要有

    迭代器是用来迭代取值的工具,而涉及到把多个值循环取出来的类型有:列表,字符串,元组,字典,集合,文件

    其中具有索引的类型可以用

    i = 0
    while i in l:
    	print(l[i])
    	i += 1
    

    但是对没有索引的类型,如集合,文件等,需要一种不依赖于索引的取值方式:迭代器

    如何用

    • 可迭代对象:可以转换为迭代器对象的对象

      • 但凡内置有 _iter_ 方法的都叫做可迭代对象:str,list,tuplue,dict,set,with open(‘a.txt’) as f
    • 调用可迭代对象下的_iter_() 方法,会将其转换为迭代器对象

    • 转换成迭代器对象后,就可以不依赖索引对其取值:使用iterator.next()方法

      d = {'a':1,'b':2,'c':3}
      d_iterator = d.__iter__()  # 调用__iter__功能,得到迭代器
      
      print(d_iterator.next())
      # a
      print(d_iterator.next())
      # b
      print(d_iterator.next())
      # c
      print(d_iterator.next())
      # 报错:迭代器取值结束
      

    迭代器相当于一个母鸡,能够下固定个鸡蛋

    try , except

    用于捕捉异常

    with open('test.txt',mode='rt',encoding='utf-8') as f:
        while True:
            try:
                f_iter = f.__iter__()
                print(f_iter.__next__(),end='')
            except:
                break
    
    # 不报错则取值打印,产生异常则break
    

    对有索引的对象,也可以通过iter,next取值

    l=[1,2,3,4,5]
    l_iterator=l.__iter__()
    
    while True:
        try:
            print(l_iterator.__next__())
        except StopIteration:
            break
    

    在一个迭代器取值完成后,再对其取值则取不到。再取值,就要再声明一次d_iterator = d.__iter__()

    iter 和 next详解

    ._next_():得到迭代器的下一个值,迭代结束会报错提示

    ._iter_():对不是迭代器的对象,得到他的迭代器对象,对迭代器对象,得到迭代器本身

    对迭代器对象使用iter方法,是用于统一for循环的工作原理,有这个方法,for循环可以不加区分地循环可迭代对象和迭代器对象

    for 循环的工作原理

    for k in dic:
    	print(k)
    
    1. d._iter_() 得到一个迭代器对象

    2. 迭代器对象._next_() 拿到一个返回值,将返回值赋值给k

    3. 循环往复步骤2,直到出现异常,for会捕捉异常,结束循环

      for循环也被称作迭代器循环

      相当于

      while True:
          try:
              print(l_iterator.__next__())
          except StopIteration:
              break
      
    4. 之前讲的list方法,底层是用的for循环,一样是用的iter,next的方法

    迭代器总结

    迭代器将迭代的状态保存在迭代器中,每次使用next方法取值

    • 优点:
      • 对有索引,无索引的对象提供统一的方法来取值
      • 节省内存:同一时间,在内存里只有一个值,不用next就不会给新值
    • 缺点:
      • 对有索引的类型,取值不如索引方便
      • 具有声明周期,取完要再定义迭代器

    目前 ._iter_(),方法只能够把原有的可迭代类型转成迭代器类型,我们还需要一种方法能够直接生成迭代器类型:生成器

    生成器

    generator,其实就是一种自定义的迭代器,也可以调用iter,next 方法

    如何自定义迭代器

    再函数内一旦存在yield关键字,调用函数并不会执行函数体代码,会返回一个迭代器对象,调用函数._next_() 方法 才会运行

    •   def func():
        	print('first')
        	yield 1
        	print('2')
        	yield 2
        	print('3')
        	yield 3
        
        g = func()
        res = g.__next__()
        # first
        print(g)
        # 1
      
    • yield:类似于return,但是函数不会结束,可以返回多次值

    • 每次使用next 调用生成器,遇到yield 会暂停,并返回 yield 后面的值

    注意:使用_next_ 一类的方法,都有相应地 next() 对应,方便书写

    生成器示例

    自定义range函数

    def my_range(start,stop,step):
    	print('start')
    	while start < stop:
    		yield start
    		start += step
    	print('end')
    	
    g = my_range(1,5,2)
    print(next(g))
    # 1
    print(next(g))
    # 3
    print(next(g))
    # 报错
    

    生成器更高级的用法:将函数暂停到一个位置

    课后总结

    装饰器

    1. 无参装饰器模板

      def outer(func):  # 传入被装饰的函数体
      	def wrapper(*args,**kwargs):  # 用*,**,将装饰器输入参数格式原封不动传给func()
      		res = func(*args,**kwargs)
      		return res
      	return wrapper
      
      @outer  # 相当于 index = outer(index)
      def index(x,y)
      

      在新增功能没有新增参数的情况下,无参装饰器就够用了。但是若想要再新增参数,因为@outer 语法只能用来传入 func,wrapper为了保持和原函数输入参数一致必须用*,所以在outer外再加一层函数用来放新增的参数

    2. 有参装饰器模板

      def auth(x,y):
      	def outer(func):
      		def wrapper(*args,**kwargs):
      			print('wrapper:',x,y)
      			res = func(*args,**kwargs)
      			return res
      		return wrapper
      	return outer
      
      @auth(1,2)
      # index = auth(1,2) ==> outer(index),x=1,y=2 ===>wrapper(*args,**kwargs)
      # 只是在原来的无参装饰器的基础上,增加了一个变量的入口,这个入口可以传入很多参数,供outer和wrapper使用
      # 使用装饰器语法糖的时候,使用最外层的函数auth + (参数)
      def index(x,y):
          return x+y
      
      @auth(3,4)  # 多个函数使用同一个装饰器,根据需求,对不同的参数仅需改变有参装饰器中传入的值
      def onload(q,p,r):
          return 'onload'
      
      @auth(3,4)
      def onshow(u,v,w):
          return 'onshow'
      
      
      
      res = index(10,12)
      print(res)
      #  wrapper: 1 2
      #  22
      

      建议在@中使用关键字参数

    3. from functiontools import wraps

      在使用装饰器之后返回原函数的返回值,不更改调用方式,且没有改原函数的代码,我们自以为已经万无一失,实际上这些函数的.__name__ .__doc__一类属性已经不再指向原来的函数,而是指向wrapper。

      应该使用

      from functiontools import wraps
      
      def outer(func):
      	@wraps(func)
          def wrapper(*args,**kwargs):
              ...
          return wrapper
      

      将被装饰函数的所有属性的赋值给装饰器函数

    4. 总结装饰器就是上面加参,底下返回, 缩进定义

    迭代器

    1. 可迭代对象:可以执行 .__iter__() 方法转换为迭代器对象的对象,有:

      • dict,str,tuple,set,list,open(‘a.txt’) as f
      • 字典,字符串,元组,集合,列表,打开的文件

      对可迭代对象执行 .__iter__() 方法,可以将其转为迭代器对象

      对迭代器对象执行,不会有任何改变,是为了让 for 循环能够对两种对象都能够做到循环取值

    2. 其实不用前后输入两个下划线这么麻烦,直接使用函数名 + 括号的方法

      • iter_item = iter(item):将item转换为迭代器对象赋值给iter_item

      • next(iter_item):取出迭代器对象的下一个值

        d = {'1':1,'2':2,'3':3}
        d_iter = iter(d)
        
        print(next(d_iter))
        print(next(d_iter))
        print(next(d_iter))
        
        # 1
        # 2
        # 3
        

        迭代器走到尽头会报错:StopIteration

    3. try + execpt

      用于异常的检测,以打开文件读取为例

      with open('test.txt',mode='rt',encoding='utf-8') as f:
          f_iter = iter(f)
          while True:
              try:
                  print(next(f_iter),end='')
              except StopIteration:
                  print('读取结束')
                  break
      

      注意:iter(item) 只是转换格式,将可迭代对象转换为迭代器对象,迭代器的迭代进程不会被改变,使用完iter()后,再用next 还是从上次迭代位置开始

    4. for 循环的原理其实就是这个iter() + next() + while 循环,如下

      goods=['mac','lenovo','acer','dell','sony']
      i=iter(goods) #每次都需要重新获取一个迭代器对象
      while True:
          try:
              print(next(i))
          except StopIteration: #捕捉异常终止循环
              break
      
    5. list() ,将元素转换成列表一类的函数其实也是用的这个方法

    6. 迭代器有优点有缺点

      • 优点:
        • 相当于能下蛋的鸡,同一时刻内存中一次只有一个值,只能取下一个值,省空间(反例:列表)
        • 为所有的可迭代对象提供统一的取值方式
      • 缺点:
        • 无法获取长度
        • 一次性取值,只能取到尽。若要再取同一个值,只能再生成一个迭代器,再迭代一次

    生成器

    1. 生成器相当于用定义函数的形式,自定义一个迭代器

    2. 当函数体包含yield关键字,再调用函数,并不会执行函数体代码,得到的返回值即生成器对象

      def func():
          print('first')
          yield 1
      
      print(func())
      
      # <generator object func at 0x000002406359E350> 不会执行打印操作,而是得到一段内存地址
      
    3. 调用这个生成器,不会执行其中的代码,而是返回一个迭代器,可以使用next() 触发生成器的执行

      def func():
          print('first')  # 
          yield 1
          print('2nd')
          yield 2
          print('3rd')
          yield 3
      
      g = func()  # 并不会执行func中的代码,而是把生成器绑定给了g
      g.__next__()  # 触发函数执行直到遇到yield则停止,将yield后的值返回,并在当前位置挂起函数
      g.__next__()  # 再次调用next(g),函数从上次暂停的位置继续执行,直到重新遇到yield
      g.__next__()
      g.__next__()  # 触发函数执行没有遇到yield则无值返回,即取值完毕抛出异常 StopIteration 结束迭代
      
      
      # first  
      # 2nd
      # 3rd
      
    4. yield 与 return的区别

      yield可以在当前位置挂起函数,而return直接结束函数

  • 相关阅读:
    DockerCompose安装与快速体验
    Nginx:Docker部署与负载均衡开发实践
    JAVA基础:反射基础
    JVM:类加载机制
    JAVA基础:注解应用示例
    JAVA基础:注解机制
    JAVA并发(五):关键词final
    JAVA并发(四):关键词volatile
    Linux虚拟机配置作为旁挂路由器
    Linux起不来,如何修复
  • 原文地址:https://www.cnblogs.com/telecasterfanclub/p/12561481.html
Copyright © 2011-2022 走看看