数据类型&推导式&生成器&迭代器

一、元组和列表

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 个苹果(生成速度极快):要么给你拿一个苹果;要么告诉你,苹果已经卖完了。

posted @ 2019-05-22 02:46  讲明白  阅读(158)  评论(0编辑  收藏  举报