zoukankan      html  css  js  c++  java
  • 数据类型&推导式&生成器&迭代器

    一、元组和列表

    1.元祖和列表的性能分析

    • 测试环境:ipython 中使用 timeit模块
    • 计算时间模块介绍:
    # ipython环境下
    In [1]: timeit list1=[1,2,3]
    42.3 ns ± 1.2 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
    
    In [2]: timeit tuple1=(1,2,3)
    12.3 ns ± 0.0096 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
    
    # 总结:可以看到初始化列表一千万次是初始化一千万次元组时间的三倍多,所以在数据确认的情况下尽可能使用元组储存数据
    
    import timeit  # python下用来性能分析
    
    
    def func():
        for i in range(10):
            print(i)
    
    
    # 函数等需要传函数名 
    times = timeit.timeit(func, number=5)
    print(times)
    # 数据类型需要传字符串,number默认值一千万次
    times=timeit.timeit("[1,2,3]")
    print(times)
    

    2.命名元组

    正常元组的取值方式是通过下标,不够人性化,命名元组就实现可以像字典那样取值

    • 方式一(没事找事)
        # 方式一 ,先把先把下标赋值给key,然后就可以通过key取值了 太low
        tu=["小明",18,"男"]
        name=0
        age=1
        gender=2
        print(tu[name])
        print(tu[age])
        print(tu[gender])
    
    • 方式二 :collections 模块里的 namedtuple方法
        from collections import namedtuple
    
        # typename:类型名字 ,field_names:这个元组元素的名称,传一个列表
        info_tuple = namedtuple(typename="info_tuole", field_names=["name", "age", "gender"])
        # 单个元组
        tu = info_tuple("小明", 18, "男")
        print(tu)
        print(tu.age)
    

    二、字典和集合的原理和应用

    dict和set实现原理是一样的都是无序的,都是将实际的值放到list中,唯一不通的在于hash函数操作对象不同,对于dict,hash函数操作的是其key,而set是直接操作的它的元素,假设操作元素为“X”,其作为变量,放入hash函数,通过运算后取list的余数,作为list的下标存储起来,set就直接存放set本身元素,叫做hash set,而对于dict则是创建两个list,一个list存key,一个list存value,叫做hash map(map就是通过key找value的过程)

    • 定义集合set
      • 最大作用自动去重
      • 关系性测试数据:交集,并集,差集,对称差集
        # 定于空集合
        se = set()
        se.add(1)  # 单个添加
        se.update([1, 2, 3, 4, 2, 2])  # 添加多个
        se.remove(1)
        print(se)
    
        a=set([1,2,3,4,5,6])
        b=set([4,5,7,8,9])
        # 交集intersection
        print(a.intersection(b)) #>>>{5, 6}
    
        # 并集union
        print(a.union(b))  # >>>{1, 2, 3, 4, 5, 6, 7, 8, 9}
    
        # 差集:在a中不在b中的
        print(a.difference(b))  # >>>{1, 2, 3, 4}
    
        # 对称差集:反向交集
        print(a.symmetric_difference(b))  # >>>{1, 2, 3, 4, 7, 8, 9}
    
        # 超集:判断a是不是完全包含b  a>=b
        print(a.issuperset(b))
    
        # 子集:判断a是不是b的子集 a<=b
        print(a.issubset(b))
    
    • 字典

    • 性能分析
      • 从时间上(快-慢):集合、字典、元组、列表
      • 从内存上(少-多):元组、列表、集合、字典

    三、推导式

    • 列表推导式
        # 如生成0-100的单数的平方列表
        list1 = [i*i for i in range(1, 101) if i % 2 == 1]
        print(list1)
    
    • 字典推导式
        cook_str = 'BIDUPSID=D0727533D7147B7;PSTM=1530348042;BAIDUID=B1005C9BC2EB28;sugstore=0'
    
        dict_str = {i.split("=")[0]: i.split("=")[1] for i in cook_str.split(";")}
    

    四、生成器

    • 生成器表达式
        # 生成器表达式 内部实现了生成器协议-有__next__方法
        gen = (i for i in range(20))
        print(gen)
        print(dir(gen))
        # 通过next()方法生成一个取一个,取玩在取报错StopIteration
        print(next(gen))
        print(next(gen))
        print(next(gen))
        # 可以通过for 循环取值
        for i in gen:
            print(i)
    
    • 函数生成器
      • send方法
      • close方法
      • throw方法
        def gen_fun():
            for i in range(1,10):
                send_value = yield i
                print(send_value)
    
        # 赋值生成器对象
        res = gen_fun()
        print(res)
        # 执行到yield 返回1
        print(next(res))
        # 继续执行 给send_value赋值 因为不没有传 所以是None 执行打印None 在继续执行循环到yield,返回2
        print(next(res))
        # 继续执行 给send_value赋值 send的值100 打印100 执行打印100 在继续执行循环到yield,返回3
        # send方法在去过一次值后,程序执行到yield后菜能使用
        print(res.send(100)) 
        #继续执行 给send_value赋值 因为不没有传 所以是None 执行打印None 在继续执行循环到yield,返回2
        print(next(res))
    
    
        res_1=gen_fun()
        print(next(res_1))
        res_1.close()  # close 方法关闭生成器
        print(next(res_1))  # >>>StopIteration 报错
    
    
        res_2=gen_fun()
        # 让生成器抛错机制 第一个参数:异常类型 ;第二个参数:异常信息
        res_2.throw(ValueError,"value异常")  # >>>ValueError: value异常
    

    五、迭代器

    • 可迭代对象是序列不是迭代器,可被for循环遍历取值,内部有__iter__()方法,没有__next__()方法
    • 可迭代对象可以通过iter方法实现可迭代协议,将可迭代对象转换成迭代器,内部有__next__()
    • 生成器是一种特殊的迭代器
    • 生成器相对迭代器多了几种方法(__del__,__qualname__,'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw')
       list_1=[1,2,3,4,5]
       iterator=iter(list_1)
       print(iterator)
       print(next(iterator))
       print(next(iterator))
    

    六、案例应用

    1、用生成器来观测一波数学公式

    示例,数学中有一个恒等式,(1 + 2 + 3 + ... + n)^2 = 1^3 + 2^3 + 3^3 + ... + n^3 的证明

    def generator(k):
        i = 1
        while True:
            yield i ** k
            i += 1
    
    gen_1 = generator(1)
    gen_3 = generator(3)
    print(gen_1)
    print(gen_3)
    
    def get_sum(n):
        sum_1, sum_3 = 0, 0
        for i in range(n):
            next_1 = next(gen_1)
            next_3 = next(gen_3)
            print('next_1 = {}, next_3 = {}'.format(next_1, next_3))
            sum_1 += next_1
            sum_3 += next_3
        print(sum_1 * sum_1, sum_3)
    
    get_sum(8)
    
    ########## 输出 ##########
    # <generator object generator at 0x10c30d3d0>
    # <generator object generator at 0x10c6d61d0>
    # next_1 = 1, next_3 = 1
    # next_1 = 2, next_3 = 8
    # next_1 = 3, next_3 = 27
    # next_1 = 4, next_3 = 64
    # next_1 = 5, next_3 = 125
    # next_1 = 6, next_3 = 216
    # next_1 = 7, next_3 = 343
    # next_1 = 8, next_3 = 512
    # 1296 1296
    
    
    2、判断子序列:给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

    你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。

    字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

    示例 1:
    s = "abc", t = "ahbgdc"

    返回 true.

    示例 2:
    s = "axc", t = "ahbgdc"

    返回 false.

    方法一: 双指针,逻辑清晰,代码多

    思路:建立s,t序列得指针计数器,然后对t序列一路扫过去(每次指针+1),如果某个数字和s指针指的一样,那么就把s指针前进一步

    def is_subsequence(s, t):
        index_1 = 0
        index_2 = 0
        while index_1 < len(s) and index_2 < len(t):
            if s[index_1] == t[index_2]:
                index_1 += 1
            index_2 += 1
        if index_1 == len(s):
            return True
        else:
            return False
    

    方法二: 迭代器,两行代码

    
    def is_subsequence(s, t):
        t = iter(t)
        return all(i in t for i in s)
    
    # 说明:
    
    # i in t 每次对比会去取一个数据出来。t中数据自动减少,不需要维护指针,也没有索引得概念
    
    # all:用于判断给定的可迭代参数 iterable 中的所有元素是否都为 True,如果是返回 True,否则返回 False
    

    七、总结

    1、可迭代对象

    所有的容器都是可迭代的(iterable)。这里的迭代,和枚举不完全一样。迭代可以想象成是你去买苹果,卖家并不告诉你他有多少库存。这样,每次你都需要告诉卖家,你要一个苹果,然后卖家采取行为:要么给你拿一个苹果;要么告诉你,苹果已经卖完了。你并不需要知道,卖家在仓库是怎么摆放苹果的。

    2、迭代器

    严谨地说,迭代器(iterator)提供了一个 next 的方法。调用这个方法后,你要么得到这个容器的下一个对象,要么得到一个 StopIteration 的错误(苹果卖完了)。你不需要像列表一样指定元素的索引,因为字典和集合这样的容器并没有索引一说。比如,字典采用哈希表实现,那么你就只需要知道,next 函数可以不重复不遗漏地一个一个拿到所有元素即可。

    而可迭代对象,通过 iter() 函数返回一个迭代器,再通过 next() 函数就可以实现遍历。for in 语句将这个过程隐式化,所以,你只需要知道它大概做了什么就行了。

    3、生成器

    这里,你只需要记着一点:生成器是懒人版本的迭代器

    生成器可以想象成是你去买苹果,卖家并没有库存。这样,每次你都需要告诉卖家,你要一个苹果,然后卖家采取行为,立马生成 1 个苹果(生成速度极快):要么给你拿一个苹果;要么告诉你,苹果已经卖完了。

  • 相关阅读:
    五:DockerFile制作Docker镜像
    六:Docker生产案例
    集群基础知识及haproxy负载均衡
    nfs服务部署记录
    haproxy 1.8.X版本编译安装教程
    Centos7.4安装kvm虚拟机
    什么是Docker
    Python—操作redis
    Python—redis
    机器学习之梯度下降法
  • 原文地址:https://www.cnblogs.com/jiangmingbai/p/10903609.html
Copyright © 2011-2022 走看看