五、迭代器、生成器、装饰器

一、迭代器

  1. 迭代器:

    • 可迭代对象:

      字面意思:

      • 对象:Python中一切皆对象,一个实实在在的值,对象

      • 可迭代:更新迭代,类似重复的,循环的一个过程,但是每次都有新的内容,可以进行循环更新的一个实实在在的值

      专业角度:

      • 内部含有'__iter__'方法的对象,可迭代对象

      目前学过的可迭代对象:str,list,tuple,dict,set,range,文件句柄

    • 获取一个对象的所有方法:dir()

    • 判断一个对象是否是可迭代对象


      s1 = 'fjdskl'
      print(dir(s1))
      print('__iter__' in dir(s1))     # True


      l1 = [1,2,3]
      print(dir((l1)))
      print('__iter__' in dir(l1))     # True


      print('__iter__' in dir(range(10)))     # True
    • 小结

      • 字面意思:可以进行循环更新的一个实实在在值。

      • 专业角度: 内部含有'__iter__'方法的对象,可迭代对象。

      • 判断一个对象是不是可迭代对象: '__iter__' in dir(对象)

      • str list tuple dict set range

      • 优点:

        1. 存储的数据直接能显示,比较直观。

        2. 拥有的方法比较多,操作方便。

      • 缺点:

        1. 占用内存。

        2. 不能直接通过for循环,不能直接取值(索引,key)。

  2. 迭代器定义:

    • 字面意思:更新迭代,器:工具,可更新迭代的工具

    • 专业角度:内部含有'__iter__'方法并且含有'__next__'方法的对象就是迭代器

    • 可以判断是否是迭代器:'__iter__'in dir(对象)and'__next__'in dir(对象)

    • 判断一个对象是否是迭代器


      with open('a1',encoding='utf-8',mode='w') as f1:
         print( '__iter__' in dir(f1) and '__next__' in dir(f1))
    • 迭代器的取值


      s1 = 'fjdags'
      obj = iter(s1)  # s1.__iter__()       #把可迭代对象转化成迭代器
      # print(obj)
      print(next(obj)) # print(obj.__next__())   # f
      print(next(obj)) # print(obj.__next__())   # j
      print(next(obj)) # print(obj.__next__())   # d
      print(next(obj)) # print(obj.__next__())   # a
      print(next(obj)) # print(obj.__next__())   # g
      print(next(obj)) # print(obj.__next__())   # s

      #可迭代对象有几个元素,就next几个值,多一个就会报错,next一次,取一个值


      l1 = [11,22,33,44,55,66]
      obj = iter(l1)
      print(next(obj))
      print(next(obj))
      print(next(obj))
      print(next(obj))
      print(next(obj))
      print(next(obj))
    • 可迭代对象如何转化成迭代器

      iter([1,2,3])

    • 小结

      • 字面意思:更新迭代,器:工具:可更新迭代的工具。

      • 专业角度:内部含有'__iter__'方法并且含有'__next__'方法的对象就是迭代器。

      • 优点:

        1. 节省内存。

        2. 惰性机制,next一次,取一个值。

      • 缺点:

        • 速度慢。

        • 不走回头路。

    • 可迭代对象与迭代器的对比:

      • 可迭代对象是一个操作方法比较多,比较直观,存储数据相对少(几百万个对象,8G内存是可以承受的)的一个数据集。

      • 当你侧重于对于数据可以灵活处理,并且内存空间足够,将数据集设置为可迭代对象是明确的选择。

      • 是一个非常节省内存,可以记录取值位置,可以直接通过循环+next方法取值,但是不直观,操作方法比较单一的数据集。

      • 当你的数据量过大,大到足以撑爆你的内存或者你以节省内存为首选因素时,将数据集设置为迭代器是一个不错的选择。

  3. while循环模拟for循环机制(要会默写,面试经常考)


    l1 = [11,22,33,44,55,66,77,88,99,1111,1133,15652]
    # 将可迭代对象转化成迭代器。
    obj = iter(l1)
    while 1:
       try:
           print(next(obj))
       except StopIteration:
           break

  

二、生成器

  • 生成器:Python社区,生成器与迭代器看成是一种,生成器的本质就是迭代器

    唯一的区别:生成器是我们自己用Python代码构建的数据类型,迭代器都是提供的,或者转化得来的

    • 获取生成器的三种方式:

      • 生成器函数

      • 生成器表达式

      • Python内部提供的一些

  • 生成器函数

    • yield

      函数
      def func():
         print(111)
         print(222)
         return 3
      ret = func()
      print(ret)

      生成器函数
      def func():
         print(111)
         print(222)
         yield 3
         a = 1
         b = 2
         c = a + b
         print(c)
         yield 4
      ret = func()
      # print(ret)   <generator object func at 0x000001FC4CF35A40>
      print(next(ret))
      print(next(ret))

      一个next 对应一个yield
    • yield return

      return:函数中只存在一个return,结束函数,并且给函数的执行者返回值

      yield:只要函数中有yield,那么它就是生成器函数而不是函数了。生成器函数中可以存在多个yield,yield不会结束生成器函数,一个yield对应一个next

    • 吃包子练习题:


      def func():
         l1 = []
         for i in range(1,5001):
             l1.append(f'{i}号包子')
         return l1
      ret = func()
      print(ret)

      def gen_func():
         for i in range(1,5001):
             yield f'{i}号包子'
      ret = gen_func()
      # [3号包子.]
      for i in range(200):
         print(next(ret))  #可以取到1~200的包子

      for i in range(50):
         print(next(ret))   #从201~250的包子
    • yield from


      def func():
         l1 = [1, 2, 3, 4, 5]
         yield l1
      ret = func()
      print(next(ret))



      def func():
         l1 = [1, 2, 3, 4, 5]
         yield from l1
         '''
        yield 1
        yield 2
        yield 3
        yield 4
        yield 5
        '''
         将l1这个列表变成了迭代器返回。
      ret = func()
      print(next(ret))
      print(next(ret))
      print(next(ret))
      print(next(ret))
      print(next(ret))
    • 简述一下yield 与yield from的区别。


      yield 是生成器函数的标识,函数中只要有yield那么他就是生成器函数而不是函数。

      next会从生成器函数中的yield获取值。

      yield from 从便于理解的角度讲:它会将一个可迭代对象里面的每一个值作为生成器的生成的值:

      yield from ['卫龙','老冰棍','北冰洋','牛羊配']

      等同于:

      yield '卫龙'

      yield '老冰棍'

      yield '北冰洋'

      yield '牛羊配'

      从更深层的角度去讲yield from有两个作用(一定要背过,面试题):

      1. 他可以完全代替了内层循环,提高效率,让代码读起来更顺畅(下一道题就可以验证)。
      2. 还可以创建通道,把内层生成器直接与外层生成器的客户端连接起来
  • 生成器表达式,列表推导式

    • 列表推导式

      • 用一行代码构建一个比较复杂有规律的列表

      • 列表推导式分两种:

        • 循环模式:[变量(加工后的变量)for 变量 in iterable]


          #1.将10以内所有证书的平方写入列表
          l1 = [i*i for i in range(11) ]
          print(l1)

          #2.100以内所有的偶数写入列表
          l2 = [i for i in range(101) if i % 2 == 0]
          print(l2)

          l3 = [i for i in range(0,101,2)]
          print(l3)

          #3.从python1期大Python100期写入列表lst
          lst = [f'python{i}期' for i in range(1,101)]
          print(lst)
          • 筛选模式:[变量(加工后的变量)for 变量 in iterable if 条件]


          #1. 过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母

          l1 = ['barry','ab','alex','wusir','xo']

          l1 = [i.upper() for i in l1 if len(i) >= 3 ]
          print(l1)

          #2.含有两个'e'的所有人名留下来,并全部大写
          names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
          #普通方法:
          l1 = []
          for i in names:
             for j in i:
                 count = 0
                 if j.count('e') == 2:
                     l1.append(j.upper())
          print(l1)
          #列表推导式:
          l2 = [j.upper() for i in names for j in i if j.count('e') == 2]
          print(l2)
    • 生成器表达式:

      与列表推导式的写法几乎一模一样,也有筛选功能,循环模式,多层循环构建。写法上只有一个不同:[]换成了()


      print([i for i in range(1,11)])
      print((i for i in range(1,11)))
      obj = (i for i in range(1,11))
      print(next(obj))
      print(next(obj))
      print(next(obj))
      print(next(obj))
      print(next(obj))
      for i in obj:
         print(i)
    • 总结:

      • 列表推导式:

        • 缺点:

        1. 列表推导式只能构建比较复杂并且有规律的列表

        2. 超过三层循环才能构建成功的,不建议用列表推导式

        3. 查找错误不行

        • 优点:

        1. 一行构建,简单

        2. 装逼

      • 列表推导式与生成器区别:

        • 写法上: [] ()

        • iterable interator

  • 字典推导式(了解)


    lst1 = ['jay', 'jj', 'meet']
    lst2 = ['周杰伦','林俊杰','元宝']
    dic = { lst2[i]: lst1[i] for i in range(len(lst1))}
    print(dic)
    # {'周杰伦': 'jay', '林俊杰': 'jj', '元宝': 'meet'}
  • 集合推导式(了解)


    print({i for i in range(1,11)})
    # {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

三、装饰器

  1. 开放封闭原则:

    • 开放:对代码的拓展开放的

    • 封闭:对源码的修改是封闭的

    装饰器:装饰,装修,房子就可以住,如果装修,不影响你住,而且体验更加,让你生活中增加了很多功能:洗澡,看电视,沙发。
    器:工具。

    开放封闭原则:
    开放:对代码的拓展开放的, 更新地图,加新枪,等等。
    封闭:对源码的修改是封闭的。闪躲用q。就是一个功能,一个函数。 别人赤手空拳打你,用机枪扫你,扔雷.....这个功能不会改变。

    装饰器:完全遵循开放封闭原则。
    装饰器: 在不改变原函数的代码以及调用方式的前提下,为其增加新的功能。
    装饰器就是一个函数。
  2. 装饰器的初识:

    • 版本一:写一些代码测试一下index函数的执行效率


      import time
      def index():
         '''有很多代码.....'''
         time.sleep(2) # 模拟的网络延迟或者代码效率
         print('欢迎登录博客园首页')

      def dariy():
         '''有很多代码.....'''
         time.sleep(3) # 模拟的网络延迟或者代码效率
         print('欢迎登录日记页面')

      # print(time.time()) # 格林威治时间。
      print(111)
      time.sleep(3)
      print(222)

      # 版本一有问题: 如果测试别人的代码,必须重新赋值粘贴。
      import time
      def index():
         '''有很多代码.....'''
         time.sleep(2) # 模拟的网络延迟或者代码效率
         print('欢迎登录博客园首页')


      start_time = time.time()
      index()
      end_time = time.time()
      print(end_time - start_time)


      # 测试了第一个人的代码,如果再加一个人就需要复制粘贴了
      import time
      def index():
         '''有很多代码.....'''
         time.sleep(2) # 模拟的网络延迟或者代码效率
         print('欢迎登录博客园首页')

      def dariy():
         '''有很多代码.....'''
         time.sleep(3) # 模拟的网络延迟或者代码效率
         print('欢迎登录日记页面')


      start_time = time.time()
      index()
      end_time = time.time()
      print(end_time - start_time)

      start_time = time.time()
      dariy()
      end_time = time.time()
      print(end_time - start_time)
    • 版本二:利用函数,解决代码重复使用的问题


      import time
      def index():
         '''有很多代码.....'''
         time.sleep(2) # 模拟的网络延迟或者代码效率
         print('欢迎登录博客园首页')

      def dariy():
         '''有很多代码.....'''
         time.sleep(3) # 模拟的网络延迟或者代码效率
         print('欢迎登录日记页面')

      def timmer(f): # f = index
         start_time = time.time()
         f()  # index()
         end_time = time.time()
         print(f'测试本函数执行效率{end_time - start_time}')
      timmer(index)

      #版本二还是有问题: 原来index函数源码没有变化,给原函数添加了一个新的功能测试原函数的执行效率的功能。
      #满足开放封闭原则么?原函数的调用方式改变了。
    • 版本三:不能改变原函数的调用方式


      import time
      def index():
         '''有很多代码.....'''
         time.sleep(2) # 模拟的网络延迟或者代码效率
         print('欢迎登录博客园首页')

      def timmer(f): # f = index (funciton index123)
         def inner():
             start_time = time.time()
             f()  # index() (funciton index123)
             end_time = time.time()
             print(f'测试本函数执行效率{end_time - start_time}')
         return inner  # 把inner返回给调用者ret
      ret = timmer(index)  # 调用timmer函数 # 此时ret = inner
      ret()  # inner(),调用inner函数
    • 版本四:具体研究


      import time
      def index():
         '''有很多代码.....'''
         time.sleep(2) # 模拟的网络延迟或者代码效率
         print('欢迎登录博客园首页')

      def timmer(f):
         f = index
         def inner():
             start_time = time.time()
             f()
             end_time = time.time()
             print(f'测试本函数执行效率{end_time - start_time}')
         return inner
      index = timmer(index)
      index()
    • 版本五:python做了一个优化;提出了一个语法糖的概念。 标准版的装饰器


      import time


      def timmer(f):
         def inner():
             start_time = time.time()
             f()
             end_time = time.time()
             print(f'测试本函数执行效率{end_time - start_time}')
         return inner

      @timmer  # index = timmer(index)
      def index():
         '''有很多代码.....'''
         time.sleep(2) # 模拟的网络延迟或者代码效率
         print('欢迎登录博客园首页')
         return 666

      ret = index()
      print(ret)

      #可是结果:
      欢迎登录博客园首页
      测试本函数执行效率2.000058174133301
      None
      # 加上装饰器不应该改变原函数的返回值,所以666 应该返回给我下面的ret,
      # 但是下面的这个ret实际接收的是inner函数的返回值,而666返回给的是装饰器里面的
    • 版本六:被装饰函数带返回值


      import time
      # timmer装饰器
      def timmer(f):
         # f = index
         def inner():
             start_time = time.time()
             # print(f'这是个f():{f()}!!!') # index()
             r = f()
             end_time = time.time()
             print(f'测试本函数的执行效率{end_time-start_time}')
             return r
         return inner

      @timmer # index = timmer(index)
      def index():
         '''有很多代码.....'''
         time.sleep(0.6) # 模拟的网络延迟或者代码效率
         print('欢迎登录博客园首页')
         return 666

      # f() 也就是 r,我们现在要解决的问题就是将r给inner的返回值。
      ret = index()  # inner()
      print(ret)
    • 版本七:被装饰函数带参数


      import time
      # timmer装饰器
      def timmer(f):
         # f = index
         def inner(*args,**kwargs):
             # 函数的定义:* 聚合 args = ('李舒淇',18)
             start_time = time.time()
             # print(f'这是个f():{f()}!!!') # index()
             r = f(*args,**kwargs)
             # 函数的执行:* 打散:f(*args) --> f(*('李舒淇',18)) --> f('李舒淇',18)
             end_time = time.time()
             print(f'测试本函数的执行效率{end_time-start_time}')
             return r
         return inner

      @timmer # index = timmer(index)
      def index(name):
         '''有很多代码.....'''
         time.sleep(0.6) # 模拟的网络延迟或者代码效率
         print(f'欢迎{name}登录博客园首页')
         return 666
      index('纳钦')  # inner('纳钦')

      @timmer
      def dariy(name,age):
         '''有很多代码.....'''
         time.sleep(0.5) # 模拟的网络延迟或者代码效率
         print(f'欢迎{age}岁{name}登录日记页面')
      dariy('李舒淇',18)  # inner('李舒淇',18)



      # 结果:
      欢迎纳钦登录博客园首页
      测试本函数的执行效率0.6150298118591309
      欢迎18岁李舒淇登录日记页面
      测试本函数的执行效率0.5002965927124023

      标准版的装饰器;

      def wrapper(f):
         def inner(*args,**kwargs):
             '''添加额外的功能:执行被装饰函数之前的操作'''
             ret = f(*args,**kwargs)
             ''''添加额外的功能:执行被装饰函数之后的操作'''
             return ret
         return inner
  3. 装饰器的应用:登录认证


    # 装饰器的应用:登录认证
    # 这周的周末作业:模拟博客园登录的作业。装饰器的认证功能。



    def login():
       pass


    def register():
       pass


    status_dict = {
       'username': None,
       'status': False,
    }

    def auth(f):
       '''
      你的装饰器完成:访问被装饰函数之前,写一个三次登录认证的功能。
      登录成功:让其访问被装饰得函数,登录没有成功,不让访问。
      :param f:
      :return:
      '''
       def inner(*args,**kwargs):
           '''访问函数之前的操作,功能'''
           if status_dict['status']:
               ret = f(*args,**kwargs)
               '''访问函数之后的操作,功能'''
               return ret
           else:
               username = input('请输入用户名').strip()
               password = input('请输入密码').strip()
               if username == 'taibai' and password == '123':
                   print('登录成功')
                   status_dict['username'] = username
                   status_dict['status'] = True
                   ret = f(*args, **kwargs)
                   return ret
               else:
                   print('登录失败')
       return inner
    @auth  # article = auth(article)
    def article():
       print('欢迎访问文章页面')
    @auth
    def comment():
       print('欢迎访问评论页面')
    @auth
    def dariy():
       print('欢迎访问日记页面')

    article()  # inner()
    comment()  #inner()
    dariy()

猜你喜欢

转载自www.cnblogs.com/yangzm/p/10921582.html