• 迭代器与生成器 (02)


    这个周末就有点颓... 一方面是手里的工作问题 , 接了个大项目, 急着要上线, 搞得有点崩溃. 其实是想了关于自身职业, 工作技能, 已经再思考回老家的事情, 总是漂泊似乎有些难受的. 北京一年, 深圳两年, 倒是对漂泊有了一定的理解, 同时对自身工作技能, 技术, 行业, 兴趣上也都有一定认知. 都是快速学习的三年哇... 工作上其实做了一件事就是转行, 从文科转到理科, 并将其作为自己的工作, 这着实, 是一条艰辛的路, 而且还要一直走下去, 并一直践行着 "学无止境'', 也深信 "气有浩然"...

    不扯了, 继续抄抄书, 很多技能, 知识, 其实都是重复, 反复加深认知的过程.

    自定义迭代器协议

    需求

    构建一个支持迭代操作的对象, 并能自定义实现迭代协议, 即遍历元素的时候有指定的顺序来弄

    方案

    通过 生成器 函数来写逻辑, 数据结构用 list, 遍历协议(顺序) 以 深度优先 的方式遍历树形节点.

    class Node:
        def __init__(self, value):
            self.value = value, 
            self.items = []
            
        def __repr__(self):
            return f'Node({self.value})'
        
        def add_node(self, node):
            self.items.append(node)
            
        def depth_first(self):
            """深度优先-遍历"""
            yield self.items
            for node in self.items:
                yield from node.depth_first()
                
    # test
    root = Node(0)
    node_01 = Node(1)
    node_02 = Node(2)
    
    root.add_node(node_01)
    root.add_node(node_02)
    
    # 子节点, 嵌套, 树形结构
    node_01.add_node(Node(3))
    node_01.add_node(Node(4))      
    node_02.add_node(Node(5))     
          
    for node in root.depth_first():
        # 每个节点是一个对象, 但只是想打印其值, 因此 __ repr__ 很关键
        print(node)
                     
    
    
    [Node((1,)), Node((2,))]
    [Node((3,)), Node((4,))]
    []
    []
    [Node((5,))]
    []
    

    我总是感觉, 这个深度优先写得有些问题, yield self 和 yield from ..我从来没有这样写过, 也没怎么见过, 果然还是我的道行太浅哇. 算了, 先不纠结了, 就当做伪代码来看了.

    之前有谈过, 要实现迭代协议, 金属必须要实现 __ iter __ 方法来返回自身的迭代器对象 和 实现 __ next __ 方法 并通过 StopIteration 来捕捉异常,表示迭代的结束. 这里呢就来对上面代码进行改造, 即用一个关联迭代器类, 重新实现 depth_frist() 深度优先的 协议.

    class Node2:
        def __init__(self, value):
            self.value = value
            self.children = []
            
        def __repr__(self):
            return f'Node({self.value})'
        
        def add_child(self, node):
            self.children.append(node)
            
        def __iter__(self):
            return iter(self.children)
        
        # 类之间的调用
        def depth_first(self):
            return DepthFirstIterator(self)
        
    class DepthFirstIterator:
        """深度优先-遍历"""
        def __init__(self, start_node):
            self.node = start_node
            self.children_iter = None
            self.child_iter = None
            
        def __iter__(self):
            return self 
        
        def __next__(self):
            if self.children_iter is None:
                self.child_iter = iter(self.node)
                return self.node
            
            elif self.child_iter:
                try: 
                    next_child = next(self.child_iter)
                    return next_child
                except StopIteration:
                    self.child_iter = None
                    return next(self)
                
            else:
                self.child_iter = next(self.children_iter).depth_first()
                return next(self)
        
            
    

    我平时也是很少这样用类之间的通信, 主要平时写的脚本太多了.... 面向对象都没怎么用过了. DepthFirstIterator 类和上面使用生成器的基本原理是差不多的, 但明显下面这个, 写起来就优先不那么直观了. 我自己都不太看得懂...所以可见, 熟练使用迭代器是多么的 简洁和优雅呢.

    反向迭代序列

    需求

    反方向迭代一个序列, 类似字符串反转这种.

    方案

    通过用内置的 reversed( ) 函数, 或者切片 [:: -1] , 或者自己写一个都行的.

    很奇怪的一点, 很多面试, 笔试就老喜欢考这种字符串反转, 或者字典按值排序, 我也真是服了...他们不知道 Python在这方面的轮子已经造好了嘛, 优雅而又简洁.

    # 好多笔试都挺喜欢这样玩
    
    text = "hello, world!"
    
    # 字符串反转
    print(text[::-1])
    
    # 通常来个词频统计
    words = {'abc':25, 'cdg': 14, 'mv': 1234, 'youge':666}
    
    # 字典按值降序, 不用内置方法, 就自己写个排序, 粗暴写冒泡, 讲究写快排
    sorted(words.items(), key=lambda arr: arr[1], reverse=True)
    
    !dlrow ,olleh
    
    [('mv', 1234), ('youge', 666), ('abc', 25), ('cdg', 14)]
    

    用其他语言, 如我之前用 javascript 来做的, 确实有些麻烦的, 但是用 Python, 这种问题都是秒杀的. 当然这里还是为了突出 reversed ( ) 函数

    lst = [1, 3, 4, 5]
    
    for i in reversed(lst):
        print(i)
    
    5
    4
    3
    1
    

    其实, reversed( ) 也只是适用于实现了 __ reversed __ 的特殊方法才能生效的呢. 如果不是这样, 那就必须转换为一个序列呀, 比如, 转为一个列表就可以了.

    with open('test.txt') as f: 
        for line in reversed(list(f)):
            print(line)
    
    do you want to drink with me ? 
      nice to see you 
    
    hello, 
    

    如果可迭代对象元素很多的话, 将其转为一个列表需要消耗大量的内存. 比如我最近做的一个列转行, 最后来了一个大list, 几 GB 呢, 我内存立马就飚上去了... 其实..还可以自定义 重写 __ reversed __ 方法来实现反向迭代.

    class CountDown:
        def __init__(self, start):
            self.start = start 
        
        # 前向迭代. 前向, 后向, 似乎在写神经网络的 BP...
        def __iter__(self):
            n = self.start 
            while n > 0:
                yield n 
                n -= 1
                
        # 反向迭代
        def __reversed__(self):
            n = 1 
            while n <= self.start:
                yield n 
                n += 1
            
    # test 
    print()
    for i in CountDown(10):
        print(i, end=' ')
       
    print()
    for j in reversed(CountDown(10)):
        print(j, end=' ')
    
    10 9 8 7 6 5 4 3 2 1 
    1 2 3 4 5 6 7 8 9 10 
    

    小结

    • 对迭代的 __ iter __ 和 __ next ___ 结合 StopIteration 异常进行了回顾
    • 用迭代器 yield 写出来的代码会更加简洁和优雅, 可读性也很高, 首选哦
    • 反向迭代 revered() 函数, 的应用和重写, 用得多, 了解的少, 今天才真正去尝试重写 __ reversed __ 方法
  • 相关阅读:
    Iphone 启动图的尺寸
    Xcode 7真机测试详解
    android 设置textview中划线效果
    IOS应用在iPhone5和iPhone5s上不能全屏显示,应用画面上下各有1条黑色的解决方案
    配置ant编译时的jdk版本
    mac系统下配置aapt环境变量
    iOS中的2x,3x问题
    Android 字体设置-Typeface讲解
    android:json解析的两个工具:Gson和Jackson的使用小例子
    Android App监听软键盘按键的三种方式
  • 原文地址:https://www.cnblogs.com/chenjieyouge/p/13128577.html
Copyright © 2020-2023  润新知