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


    一、迭代器

    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()
  • 相关阅读:
    怎样获取节点的文本值
    怎样获取节点的名称
    怎样获取节点的类型
    Sublime Text 3
    ubuntu QT安装以及配置交叉编译环境
    Ubuntu下搭建NFS,并在开发板挂载
    Tk1上搭建turtlebot环境
    sudo dpkg --configure -a无法解决的问题
    ARM TK1 安装kinect驱动
    opencv2.4.9+vs2012安装配置
  • 原文地址:https://www.cnblogs.com/yangzm/p/10921582.html
Copyright © 2020-2023  润新知