zoukankan      html  css  js  c++  java
  • Python 简明教程 --- 14,Python 数据结构进阶

    微信公众号:码农充电站pro
    个人主页:https://codeshellme.github.io

    如果你发现特殊情况太多,那很可能是用错算法了。
    —— Carig Zerouni

    目录

    在这里插入图片描述

    前几节我们介绍了Python 中四种数据结构的特性和基本用法,本节介绍与数据结构相关的高级特性。

    • 序列
    • 迭代器
    • 列表生成式
    • 生成器
    • 强制类型转换

    1,序列

    Python 序列是指,其中存放的元素是有序排列的,可用下标访问,字符串列表元组都是序列。

    字典集合中的元素是无序排列的,因此一般不归在序列中。

    Python 序列有如下特点:

    • 序列中的元素是不可变类型
    • 序列中的元素可用下标访问,下标可正可负
    • 可通过切片访问部分连续元素
    • 可进行相加相乘in 运算
    • 可通过for 循环遍历所有元素

    可以使用collections 模块中的Sequence 类来查看一个对象是否是一个序列:

    >>> isinstance('', collections.Sequence) # 字符串是序列
    True
    >>> isinstance([], collections.Sequence) # 列表是序列
    True
    >>> isinstance((), collections.Sequence) # 元组是序列
    True
    >>> isinstance({}, collections.Sequence) # 字典不是序列
    False
    >>> isinstance(set(), collections.Sequence) # 集合不是序列
    False
    

    提示:

    1,isinstance 函数用于查看一个对象属于某个类

    2,在使用模块时,要先import 该模块

    2,迭代器

    可迭代类型

    我们知道strlisttupledictset 都可用for 循环来遍历,这个遍历的过程就是一个迭代过程,这些类型都是可迭代类型。

    可迭代的类型,都实现了__iter__ 方法,我们通过dir(可迭代对象),可以看到,可迭代对象的魔法方法中都有一个__iter__ 方法。

    我们也可以通过collections 模块中的Iterable 类型来查看一个对象是不是可迭代对象:

    >>> isinstance('', collections.Iterable)
    True
    >>> isinstance([], collections.Iterable)
    True
    >>> isinstance((), collections.Iterable)
    True
    >>> isinstance({}, collections.Iterable)
    True
    >>> isinstance(set(), collections.Iterable)
    True
    

    迭代器

    迭代器是一种可迭代的对象。

    迭代器一定是可迭代的,可迭代的对象不一定是迭代器。

    迭代器要实现两个魔法方法:__iter____next__

    通过collections 模块中的Iterator 类型来查看这两个方法:

    >>> dir(collections.Iterator)
    

    判断一个对象是不是迭代器:

    >>> isinstance('', collections.Iterator) # 字符串不是迭代器
    False
    >>> isinstance([], collections.Iterator) # 列表不是迭代器
    False
    >>> isinstance((), collections.Iterator) # 元组不是迭代器
    False
    >>> isinstance({}, collections.Iterator) # 字典不是迭代器
    False
    >>> isinstance(set(), collections.Iterator) # 集合不是迭代器
    False
    

    迭代器通用函数

    迭代器有一些通用函数,下面我们介绍一些常用的。

    1.enumerate 函数

    在Python3 中,enumerate 实际上是一个,可通过help(enumerate) 查看,也可以把它当做函数来使用。

    其经常被用在for 循环中,即可遍历下标,又能遍历数据。

    作用: 用于给一个可迭代的对象,添加下标
    原型: enumerate(iterable[, start]) -> iterator
    参数 iterable: 一个可迭代的对象
    参数 start: 下标起始位置
    返回值: 一个enumerate 对象,同时也是一个迭代器

    示例:

    >>> l = enumerate(['a', 'c', 'b']) # 参数是一个列表
    >>> type(l)
    <class 'enumerate'>
    >>> isinstance(l, collections.Iterator) # 是一个迭代器
    True
    >>> for index, item in l:           # for 循环遍历,能遍历出下标
    ...     print(index, item)
    ... 
    0 a
    1 c
    2 b
    

    2.iter 函数

    作用:将一个可迭代的序列iterable 转换成迭代器
    原型:iter(iterable) -> iterator
    参数:iterable 是一个可迭代的序列
    返回值:一个迭代器

    示例:

    >>> iter('123')            # 参数是字符串
    <str_iterator object at 0x7fcb7dd320b8>     # str 迭代器
    >>> iter([1, 2, 3])        # 参数是列表
    <list_iterator object at 0x7fcb7dd4a0b8>    # list 迭代器
    >>> iter((1, 2, 3))        # 参数是元组
    <tuple_iterator object at 0x7fcb7dd4a0b8>   # tuple 迭代器
    >>> iter(set([1, 2, 3]))   # 参数是集合
    <set_iterator object at 0x7fcb7d2c5e10>     # set 迭代器
    >>> iter({'a':1, 'b':2})   # 参数是字典
    <dict_keyiterator object at 0x7fcb7f467098> # dict 迭代器
    

    3.next 函数

    作用:返回迭代器的下一个元素
    原型:next(iterator[, default]) -> item
    参数 iterator:是一个迭代器类型
    参数 default:任意类型数据,可省
    返回值

    迭代器中有元素时:返回迭代器中的下一个元素
    迭代器中没有元素没有default 参数时:抛出StopIteration 异常
    迭代器中没有元素有default 参数时:返回default

    示例:

    >>> i = iter([1, 3, 5])  # i 是一个迭代器
    >>> next(i)              # 返回 1
    1
    >>> next(i)              # 返回 3
    3
    >>> next(i)              # 返回 5
    5
    >>> next(i)              # i 中没有元素,抛出异常
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    >>> 
    >>> next(i, 7)           # i 中没有元素,返回第二个参数 7
    7
    

    4.len 函数

    作用:用于计算一个对象obj中的元素的个数
    原型:len(obj, /)
    参数:一般obj 是一个可迭代类型对象,实际上,只要实现了__len__ 方法的对象,都可以使用该函数
    返回值:一个整数

    示例:

    >>> len('abc')
    3
    >>> len([1, 2, 3])
    3
    

    5.max 函数

    作用:返回可迭代对象iterable 中的最大元素
    原型:max(iterable)
    参数iterable 是一个可迭代的对象,并且iterable 中的元素可比较
    返回值:最大值

    示例:

    >>> max([1, 2])
    2
    >>> max([1, 2, 3])
    3
    >>> max('b', 'a')
    'b'
    >>> max('abc')
    'c'
    >>> max(1, 'a')     # 整数与字符串不是同类型,不可比较
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: '>' not supported between instances of 'str' and 'int'
    

    6.min 函数

    作用:返回可迭代对象iterable 中的最小元素
    原型:min(iterable)
    参数iterable 是一个可迭代的对象,并且iterable 中的元素可比较
    返回值:最小值

    示例:

    >>> min([1, 2])
    1
    >>> min([2, 3])
    2
    >>> min('abc')
    'a'
    >>> min('b', 'c')
    'b'
    >>> min('b', 2)  # 整数与字符串不是同类型,不可比较
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: '<' not supported between instances of 'int' and 'str'
    

    7.sum 函数

    作用:计算可迭代对象iterable 中的数据之和,最后在加上 start
    原型:sum(iterable, start=0, /)
    参数iterable 是一个可迭代对象,其中的元素是数字类型
    返回值:所有元素之和

    示例:

    >>> sum([1, 2, 3])     # 计算 1,2,3 之和
    6
    >>> sum([1, 2, 3], 5)  # 计算 1,2,3 之和,再加上 5
    11
    

    8.reversed 函数

    reversed 实际上是一个类,可用help(reversed) 查看。

    作用:将一个序列sequence 反转
    原型:reversed(sequence)
    参数sequence 是一个序列类型
    返回值:一个reversed 对象,该对象也是一个迭代器,可被迭代

    示例:

    >>> r = reversed('abcde')
    >>> r                                    # 一个 reversed 对象
    <reversed object at 0x7fcb79970518>
    >>> isinstance(r, collections.Iterator)  # 也是一个迭代器
    True
    >>> [i for i in r]                  # 转换成列表,查看其中的元素
    ['e', 'd', 'c', 'b', 'a']
    >>>
    >>> r = reversed([1, 3, 5, 7])
    >>> [i for i in r]
    [7, 5, 3, 1]
    

    3,列表生成式

    列表生成式,又叫列表推导式,用于从一个可迭代对象生成一个列表,它是一种代码简写形式,其优点是代码简洁优雅。

    比如,我们有一个列表 [1, 3, 5],想求其中每个元素的平方,再将结果放入列表中,最终的结果是[1, 9, 25]

    如果是一般的方式,我们写出来的代码是这样的:

    l = [1, 3, 5]
    
    l2 = []
    for i in l:
        item = i * i 
        l2.append(item)
    
    print(l2)   
    

    如果用列表生成式的方式,代码是这样的:

    l = [1, 3, 5]
    
    l2 = [i * i for i in l]
    
    print(l2)
    

    可以看到列表生成式比普通的形式要简洁许多。

    列表生成式语法

    最简单的列表生成式的语法,就像上面的代码一样:

    在这里插入图片描述

    列表生成式由三部分组成:

    • 列表符号:中括号[]
    • 表达式:用于生成新列表中的每个元素,一般与item 有关,也可无关
    • for 循环:用于迭代原始可迭代对象

    列表生成式中的if 判断

    列表生成式中也可以有if 判断,当判断条件成立时,才会执行表达式,并将该表达式的结果append 到新的列表中。

    这里的if 判断没有else 部分,判断条件一般与item 有关。

    如下:

    在这里插入图片描述

    示例:

    l = [1, 3, 5]
    
    # 只有当 l 中的元素大于 1 时,才算平方
    l2 = [i * i for i in l if i > 1]
    
    print(l2)
    

    range 函数

    Python2.x 中range 是一个函数

    Python3.x 中,range 是一个,可用help(range) 查看其手册。

    >>> range
    <class 'range'>
    

    下面介绍Python3.xrange 的用法,有两种参数形式:

    作用:生成一个从startstop 的,步长为step 的,可迭代的整数序列,该序列包含start,不包含stop,即遵循左开右闭原则
    原型

    range(stop) -> range object
    range(start, stop[, step]) -> range object

    参数

    当只有stop 参数时,该序列从0 开始到stop,步长为1
    当有startstop 参数时,该序列从start 开始到stop,步长为1
    当有step 参数时,步长为step

    返回值:一个range 对象

    示例:

    >>> range(5)
    range(0, 5)
    >>> range(1, 5)
    range(1, 5)
    >>> range(1, 5, 2)
    range(1, 5, 2)
    

    range 对象是一个序列,而不是一个迭代器:

    >>> isinstance(range(5), collections.Sequence)
    True
    >>> isinstance(range(5), collections.Iterator)
    False
    

    可以使用列表生成式来查看range 中的内容:

    >>> [i for i in range(5)]       # 从 0 到 5
    [0, 1, 2, 3, 4]
    >>> [i for i in range(1, 5)]    # 从 1 到 5
    [1, 2, 3, 4]
    >>> [i for i in range(1, 5, 2)] # 步长为 2
    [1, 3]
    

    4,生成器

    生成器也是一个迭代器。生成器跟列表有点像,但优点是比列表节省内存

    对于列表,Python 会为列表中的每个元素都分配实实在在的内存空间,如果列表中的元素很多,那么列表将消耗大量内存。

    而对于生成器,Python 并不会为其中的每个元素都分配内存。

    生成器记录的是一种算法,是如何生成数据的算法,在每次用到数据时,才会去生成数据,而不会一开始就将所有的数据准备好。

    因此,对于相同的功能,生成器列表都能完成,而生成器可以节省大量的内存。

    创建生成器有两种方式:

    • 列表生成式中的中括号[]改为小括号()
    • 在函数中使用yield 关键字

    使用列表生成式

    如下代码可以生成一个列表:

    >>> [i for i in range(5)]
    [0, 1, 2, 3, 4]
    

    将中括号[] 改为小括号(),就是一个生成器:

    >>> l = (i for i in range(5))
    >>> l
    <generator object <genexpr> at 0x7f3433d2d9e8>
    >>> type(l)
    <class 'generator'>
    >>> isinstance(l, collections.Iterator) # 生成器也是一个迭代器
    True
    

    其中的generator 就是生成器的意思,它也是一个

    使用yield 关键字

    比如,我们想计算列表[1, 3, 5] 中每个元素的平方,使用yield 关键字,代码如下:

    #! /usr/bin/env python3
    
    def test():
        print('test...')
        l = [1, 3, 5]
    
        for i in l:
            tmp = i * i 
            print('yield tmp:%s' % tmp)
            yield tmp 
    
    t = test()
    print(t)       # <generator object test at 0x7fde1cdaa3b8> 
    print(type(t)) # <class'generator'>
    

    注意,我们定义了一个函数,这里我们只是为了演示如何使用yield 来创建生成器,而不用过多关注如何定义函数。

    函数的概念我们会在后续章节详细介绍。

    执行以上代码,输出如下:

    <generator object test at 0x7fde1cdaa3b8> 
    <class'generator'>
    

    你会发现,字符串test...并没有被打印出来。

    你可能会有疑问,既然代码t = test() 已经执行了,那整个test() 函数中的代码都应该执行完了才对,那字符串test... 怎么会没有打印呢?

    那是因为,生成器中代码的执行是惰性的。当执行t = test() 这行代码时,test() 函数并没有被执行。

    前边我们说过,生成器记录的是一个算法,而不是真正的数据,数据只有当使用到的时候,才会去生成。

    所以,变量 t 中只是一个算法,而没有数据。

    因为,生成器也是一个迭代器,所以生成器可以使用next() 函数来访问其中的数据:

    i = next(t)
    print('i value is', i) 
    

    执行上面这行代码后,程序会输出:

    test...
    yield tmp:1
    i value is 1
    

    字符串test... 被打印,说明test() 执行了。

    当代码执行到yield 时,变量i 会接收到yield 返回的数据,此时,代码会从yield 处跳出test() 函数。

    如果接下来,不再遍历变量t 中的数据,那么整个代码就执行结束了。

    如果我们再次执行代码:

    j = next(t)
    print('j value is', j) 
    

    程序会输出:

    yield tmp:9
    j value is 9
    

    可见代码会在上次yield 的地方,再次向下执行。

    我们再次执行代码:

    k = next(t)
    print('k value is', k) 
    

    程序会输出:

    yield tmp:25
    k value is 25
    

    t 中的元素被遍历完后,如果再次执行next(t),则会抛出StopIteration 异常。

    使用next() 函数来遍历生成器中的所有元素是很麻烦的,我们可以像遍历列表一样,用for 循环来遍历生成器中的元素:

    for i in t:
        print(i) 
    

    输出如下:

    test...
    yield tmp:1
    1
    yield tmp:9
    9
    yield tmp:25
    25
    

    整个遍历过程中,字符串test... 只被输出了一次,并没有被输出三次。

    说明当代码执行到yield 时,并没有从函数test() 中返回,代码只是暂停在了yield 处,等下次需要数据时,会接着从上次的yield 处接着执行。

    生成器比列表节省内存

    如果我们想生成一个从09999 的数字序列,用列表的话,是这样的:

    >>> l = [i for i in range(10000)]
    >>> sys.getsizeof(l)  # 内存占用 87624 字节
    87624
    

    sys 模块的getsizeof 方法可以查看一个对象的大小,单位是字节

    用生成器来实现的话,是这样的:

    >>> l = (i for i in range(10000))
    >>> sys.getsizeof(l)  # 内存占用 88 字节
    88
    

    可以看到09999 这样的整数序列,使用列表的话,占用 87624 字节;使用生成器的话,只占用 88 字节

    5,强制类型转换

    我们已经知道了,Python3 中的strlisttupledictset 都是一个类:

    >>> type('')
    <class 'str'>
    >>> type([])
    <class 'list'>
    >>> type(())
    <class 'tuple'>
    >>> type({})
    <class 'dict'>
    >>> type(set())
    <class 'set'>
    

    每个类都有构造方法,这些构造方法可以将其它类型的数据,转换成该类型数据,我们也可以将这种转换称为强制类型转换

    提示:

    构造方法是一个的初始化方法,就是用什么样的数据去构造一个特定类的对象。

    当介绍到类与对象时,我们会详细介绍。

    str强制转换

    作用: 将其它类型数据转换成字符串
    原型: str(object='') -> str
    参数: 任意类型数据
    返回值: 字符串

    示例:

    >>> str(1)        # 将数字转换成字符串
    '1'
    >>> str([1, 2])   # 将列表转换成字符串
    '[1, 2]'
    >>> str((1, 2))   # 将元组转换成字符串
    '(1, 2)'
    

    list强制转换

    作用: 将一个可迭代类型转成列表
    原型: list(iterable) -> list
    参数: 任意可迭代类型数据
    返回值: 列表

    示例:

    >>> list((1, 2))   # 将一个元组转成列表
    [1, 2]
    >>> list(range(3)) # 将一个 range 对象转成列表
    [0, 1, 2]
    

    tuple强制转换

    作用: 将一个可迭代类型转成元组
    原型: tuple(iterable) -> tuple
    参数: 任意可迭代类型数据
    返回值: 元组

    示例:

    >>> tuple([1, 2])    # 将一个列表转成元组
    (1, 2)
    >>> tuple(range(3))  # 将一个 range 对象转成元组
    (0, 1, 2)
    
    

    dict强制转换

    作用: 将一个可迭代类型数据转成字典
    原型: dict(iterable) -> dict
    参数: iterable 是一个可迭代对象,并且该对象中的元素是元组
    返回值: 字典

    示例:

    >>> t = ((1, 2), (2, 3))  # t 是一个元组,其中的元素也是元组
    >>> dict(t)
    {1: 2, 2: 3}
    >>> 
    >>> l = [(1, 2), (2, 3)] # l 是一个列表,其中的元素是元组
    >>> dict(l)
    {1: 2, 2: 3}
    

    set强制转换

    作用: 将一个可迭代类型数据转成集合
    原型: set(iterable) -> set
    参数: 一个可迭代对象
    返回值: 集合

    其实,我们在介绍集合时,都是用的这种方式来声明的集合,示例:

    >>> set('abc')      # 将一个字符串转成集合
    {'b', 'c', 'a'}
    >>> set([0, 1])     # 将一个列表转成集合
    {0, 1}
    >>> set((0, 1))     # 将一个元组转成集合
    {0, 1}
    

    (完。)


    推荐阅读:

    Python 简明教程 --- 9,Python 编码

    Python 简明教程 --- 10,Python 列表

    Python 简明教程 --- 11,Python 元组

    Python 简明教程 --- 12,Python 字典

    Python 简明教程 --- 13,Python 集合


    欢迎关注作者公众号,获取更多技术干货。

    码农充电站pro

  • 相关阅读:
    [BZOJ] 3191 [JLOI2013]卡牌游戏
    [LUOGU] P1466 集合 Subset Sums
    [LUOGU] P1113 杂物
    [BZOJ] 1003 [ZJOI2006]物流运输
    poj 2479 最大连续子段和
    C#学习第九弹之委托
    C#学习第八弹之线程基础理解
    C#学习第七弹之WPF
    hdu 2030 汉字的编码方式
    hdu 1559 暴力
  • 原文地址:https://www.cnblogs.com/codeshell/p/13040449.html
Copyright © 2011-2022 走看看