引言
我们前面的文章介绍了数字和字符串,比如我计算今天一天的开销花了多少钱我可以用数字来表示,如果是整形用 int ,如果是小数用 float ,如果你想记录某件东西花了多少钱,应该使用 str 字符串型,如果你想记录表示所有开销的物品名称,你应该用什么表示呢?
可能有人会想到我可以用一个较长的字符串表示,把所有开销物品名称写进去,但是问题来了,如果你发现你记录错误了,想删除掉某件物品的名称,那你是不是要在这个长字符串中去查找到,然后删除,这样虽然可行,那是不是比较麻烦呢。
这种情况下,你是不是需要Python给我们提供一种新的数据结构,可以存储很多个字符串,能让我们方便的添加修改和删除,就完美了。
列表(list)同字符串一样都是有序的,因为他们都可以通过切片和索引进行数据访问,列表是可变(mutable)的,你可以修改、更新和删除。
列表是一组有序项目的集合 ,可变的数据类型可 进行增删改查 ; 列表中可以包含Python中任何数据类型和对象,也可包含另一个列表 可任意组合嵌套 列表是以方括号 [] 包围的数据集合,不同成员以 , 分隔,列表可通过序号访问其中成员。
列表可以装入Python中所有的对象,看下面的例子就知道:
列表的定义和创建
定义: [] 内以逗号分隔,按照索引,存放各种数据类型,每个位置代表一个元素
列表的创建:
第一种:
fruit = ['pineapple', 'pear']
第二种:
fruit = list(['pineapple', 'pear'])
其他数据类型转为列表:
1、把一个字符串转化成列表
list在把字符串转换成列表的时候,会把字符串用for循环迭代一下,然后把每个值当作list的一个元素。
2、把元组转换成列表
3、把字典转成列表
list在把字典转换成列表的时候,默认循环的是字典的key,所以会把key当作列表的元素;如果指定循环的是values,那么就会把values当作列表的元素。
列表的特点和常用方法
特征:
- 多值: 可存放多个值
- 有序: 按照从左到右的顺序定义列表元素,下标从0开始顺序访问
3.可变: 可修改指定索引位置对应的值
列表的增删改查:
增加操作:
在使用 insert 方法的时候,必须要指定列表中要插入的新元素的位置,插入元素的实际位置是在 指定位置元素的前面的
位置 ,如果指定插入的位置在列表中不存在,实际上也就是超出指定列表的长度,程序运行不会报错,但是这个元素一定会被放到这个列表的最后位置。
删除操作:
remove方法删除一个元素,必须是在列表中的,否则会报错,del利用下标来删除元素,pop默认删除最后一个元素,也可以指定元素下标来删除。
修改操作:
查询操作:
列表索引:
索引下标,只会返回第一个元素的下标,如果元素不在列表中,会报错,我们可以利用 in 这个关键之来判断元素是否在列表中。
列表切片:
利用下标取出的一个单独元素是str类型,而利用分片取出的是一个list类型。
列表元素统计:
列表排序和翻转:
sort() 方法会修改原列表,而不是创建一个新的有序列表, reverse() 也会修改原列表,但是你希望排序,但是又不希望修改原列表,你只能利用Python中一个名为 sorted() 的内置函数来操作:
列表拷贝:
从上面可以看出列表的copy方法是一个浅copy的栗子,只会拷贝第一次,而多层嵌入的话,会随着源列表的变化为变化,关于深拷贝和浅拷贝后面详细介绍。
列表所有的方法如下:
列表推导式:
列表推导式(又称列表解析式)提供了一种简明扼要的方法来创建列表,它的语法简单,很有实用价值。
它的结构是在一个中括号里包含一个表达式,然后是一个for语句,然后是0个或多个for或者if语句。那个表达式可以是任意的,意思是你可以在列表中放入任意类型的对象。返回结果将是一个新的列表,在这个以if和for语句为上下文的表达式运行完成之后产生。
列表解析的一般形式:
列表解析返回的是列表, 列表的内容是表达式执行的结果.
列表解析用于对可迭代对象做过滤和转换,返回值是列表.
特性一:代码变短,可读性更好
从上图代码示例中我们明显可以看出,列表推导式相比常规方法,写出来的代码更加符合pythonic,更加简短,可读性更好。
有些人甚至更喜欢使用它而不是filter函数生成列表,但是当你使用列表推导式效果会更加,列表推导式在有些情况下超赞,特别是当你需要使用for循环来生成一个新列表.
特征二:推导式速度更快
结果:
如果你使用map或者filter结合lambda生成列表,也是没有列表推导式速度快的,有兴趣的可以自己Coding一下。
列表的遍历
在Python中常用循环对象来遍历列表,在这里for循环自动调用 next() 方法,将该方法的返回值赋予给循环对象。循环检测到StopIteration的时候才结束。相对于序列,用循环对象的好处在于:不用在循环还没有开始的时候,就生成好要使用的元素。所使用的元素可以在循环过程中逐次生成。这样,节省了空间,提高了效率,编程更灵活。
1. for循环遍历
2. while循环遍历
3. 拉链(zip)方法遍历
4. 利用Python内置函数 enumerate() 列举
enumerate(iterable [, start ]) 返回枚举对象, 参数:
5. 使用 iter() 迭代器
iter(collection [, sentinel ]) 函数用来生成迭代器,返回迭代对象, 参数:
collection: 支持迭代的集合对象
sentinel: 如果传递了第二个参数,则参数 object 必须是一个可调用的对象(如,函数),此时, iter 创建了一个迭代器对象,每次调用这个迭代器对象的 __next__() 方法时,都会调用object。
由于列表在Python内部的组成方式不同于C语言等,其索引的效率相对较为低下。因此在使用python的过程中,如果需要同时用到序号和元素,最好使用enumerate();当我们不需要使用序号时,在列表上直接进行迭代效率最高。
元组
元组其实跟列表差不多,也是存一组数,只不是它一旦创建,便不能再修改,所以又叫只读列表。
语法: names = ('tom', 'jack', 'andy')
它只有2个方法,一个是count,一个是index:
列表几种高阶常用场景
1. 解压列表赋值给多个变量
现在有一个包含 N个元素 的元组或者列表,怎样将它里面的值解压后同时赋值给 N 个变量?
任何的序列(或者是可迭代对象)可以通过一个简单的赋值语句解压并赋值给多个变量。 唯一的前提就是变量的数量必须跟序列元素的 数量一致 的。
代码示例:
如果变量个数和列表元素的个数不匹配,会产生异常的哦。
代码示例:
实际上,这种解压赋值可以用在任何可迭代对象上面,而不仅仅是列表或者元组。 包括字符串,文件对象,迭代器和生成器。
代码示例:
有时候,你可能只想解压一部分,丢弃其他的值。对于这种情况 Python 并没有提供特殊的语法。 但是你可以使用任意变量名去占位,到时候丢掉这些变量就行了。
你必须保证你选用的那些占位变量名在其他地方没被使用到。
2. 删除列表中相同元素并保持顺序
怎样让一个列表保持元素顺序的同时消除重复的值,如果列表上的值都是 hashable 类型,那么可以很简单的利用集合或者生成器来解决这个问题,比如:
下面是使用上述函数的例子:
这个方法仅仅在序列中元素为 hashable 的时候才管用。 如果你想消除元素不可哈希(比如 dict 类型)的序列中重复元素的话,你需要将上述代码稍微改变一下,就像这样:
这里的key参数指定了一个函数,将序列元素转换成 hashable 类型。下面是它的用法示例:
如果你想基于单个字段、属性或者某个更大的数据结构来消除重复元素,第二种方案同样可以胜任。
如果你仅仅就是想消除重复元素,通常可以简单的构造一个集合。比如:
然而,这种方法不能维护元素的顺序,生成的结果中的元素位置被打乱,而上面的方法可以避免这种情况。
我们使用了生成器函数让我们的函数更加通用,不仅仅是局限于列表处理。 比如,如果如果你想读取一个文件,消除重复行,你可以很容易像这样做:
上述key函数参数模仿了 sorted() , min() 和 max() 等内置函数的相似功能。
3. 统计列表中出现次数最多的元素
怎样找出一个列表中出现次数最多的元素呢, collections.Counter 类就是专门为这类问题而设计的, 它甚至有一个有用的 most_common() 方法直接给了你答案。
为了演示,先假设你有一个单词列表并且想找出哪个单词出现频率最高。你可以这样做:
作为输入, Counter 对象可以接受任意的由可哈希( hashable )元素构成的序列对象。 在底层实现上,一个 Counter 对象就是一个字典,将元素映射到它出现的次数上, 比如:
如果你想手动增加计数,可以简单的用加法:
或者你可以使用 update() 方法:
word_counts.update(morewords)
Counter 实例一个鲜为人知的特性是它们可以很容易的跟数学运算操作相结合,比如:
毫无疑问, Counter 对象在几乎所有需要制表或者计数数据的场合是非常有用的工具。 在解决这类问题的时候你应该优先选择它,而不是手动的利用字典去实现。
4. 过滤列表元素
你有一个数据列表,想利用一些规则从中提取出需要的值或者是缩短列表,最简单的过滤序列元素的方法就是使用列表推导,比如:
使用列表推导的一个潜在缺陷就是如果输入非常大的时候会产生一个非常大的结果集,占用大量内存。 如果你对内存比较敏感,那么你可以使用生成器表达式迭代产生过滤的元素,比如:
有时候,过滤规则比较复杂,不能简单的在列表推导或者生成器表达式中表达出来。 比如,假设过滤的时候需要处理一些异常或者其他复杂情况。这时候你可以将过滤代码放到一个函数中, 然后使用内建的 filter() 函数,示例如下:
filter() 函数创建了一个迭代器,因此如果你想得到一个列表的话,就得像示例那样使用 list() 去转换。
列表推导和生成器表达式通常情况下是过滤数据最简单的方式。 其实它们还能在过滤的时候转换数据,比如:
过滤操作的一个变种就是将不符合条件的值用新的值代替,而不是丢弃它们。 比如,在一列数据中你可能不仅想找到正数,而且还想将不是正数的数替换成指定的数。 通过将过滤条件放到条件表达式中去,可以很容易的解决这个问题,就像这样:
另外一个值得关注的过滤工具就是 itertools.compress() , 它以一个 iterable 对象和一个相对应的 Boolean 选择器序列作为输入参数。 然后输出 iterable 对象中对应选择器为 True 的元素。 当你需要用另外一个相关联的序列来过滤某个序列的时候,这个函数是非常有用的。 比如,假如现在你有下面两列数据:
现在你想将那些对应 count 值大于5的地址全部输出,那么你可以这样做:
这里的关键点在于先创建一个 Boolean 序列,指示哪些元素符合条件。 然后 compress() 函数根据这个序列去选择输出对应位置为 True 的元素。
和 filter() 函数类似, compress() 也是返回的一个迭代器。因此,如果你需要得到一个列表,那么你需要使用 list() 来将结果转换为列表类型。
5. 列表上索引值迭代
你想在迭代一个列表的同时跟踪正在被处理的元素索引,内置的 enumerate() 函数可以很好的解决这个问题:
为了按传统行号输出(行号从1开始),你可以传递一个开始步长值:
这种情况在你遍历文件时想在错误消息中使用行号定位时候非常有用:
enumerate() 对于跟踪某些值在列表中出现的位置是很有用的。 所以,如果你想将一个文件中出现的单词映射到它出现的行号上去,可以很容易的利用 enumerate() 来完成:
如果你处理完文件后打印 word_summary ,会发现它是一个字典(准确来讲是一个 defaultdict ), 对于每个单词有一个 key ,每个 key 对应的值是一个由这个单词出现的行号组成的列表。 如果某个单词在一行中出现过两次,那么这个行号也会出现两次, 同时也可以作为文本的一个简单统计。
当你想额外定义一个计数变量的时候,使用 enumerate() 函数会更加简单。你可能会像下面这样写代码:
但是如果使用 enumerate() 函数来代替就显得更加优雅了:
enumerate() 函数返回的是一个 enumerate 对象实例, 它是一个迭代器,返回连续的包含一个计数和一个值的元组, 元组中的值通过在传入序列上调用 next() 返回。
还有一点可能并不很重要,但是也值得注意, 有时候当你在一个已经解压后的元组序列上使用 enumerate() 函数时很容易调入陷阱。 你得像下面正确的方式这样写:
6. 同时迭代多个列表
你想同时迭代多个列表,每次分别从一个序列中取一个元素, 为了同时迭代多个序列,使用 zip() 函数,比如:
zip(a, b) 会生成一个可返回元组 (x, y) 的迭代器,其中x来自a,y来自b。 一旦其中某个序列到底结尾,迭代宣告结束。 因此迭代长度跟参数中最短序列长度一致。
如果这个不是你想要的效果,那么还可以使用 itertools.zip_longest() 函数来代替,比如:
当你想成对处理数据的时候 zip() 函数是很有用的。 比如,假设你headers列表和一个values列表,就像下面这样:
使用zip()可以让你将它们打包并生成一个字典:
或者你也可以像下面这样产生输出:
虽然不常见,但是 zip() 可以接受多于两个的序列的参数。 这时候所生成的结果元组中元素个数跟输入序列个数一样, 比如:
最后强调一点就是, zip() 会创建一个迭代器来作为结果返回。 如果你需要将结对的值存储在列表中,要使用 list() 函数,比如:
7. 展开嵌透的列表
你想将一个多层嵌套的列表展开成一个单层列表, 可以写一个包含 yield from 语句的递归生成器来轻松解决这个问题。比如:
在上面代码中, isinstance(x, Iterable) 检查某个元素是否是可迭代的。 如果是的话 yield from 就会返回所有子例程的值。最终返回结果就是一个没有嵌套的简单列表了。
额外的参数 ignore_types 和检测语句 isinstance(x, ignore_types) 用来将字符串和字节排除在可迭代对象外,防止将它们再展开成单个的字符。 这样的话字符串数组就能最终返回我们所期望的结果了。比如:
语句 yield from 在你想在生成器中调用其他生成器作为子例程的时候非常有用。 如果你不使用它的话,那么就必须写额外的 for 循环了,比如:
尽管只改了一点点,但是 yield from 语句看上去感觉更好,并且也使得代码更简洁清爽。
之前提到的对于字符串和字节的额外检查是为了防止将它们再展开成单个字符。 如果还有其他你不想展开的类型,修改参数 ignore_types 即可。
最后要注意的一点是 yield from 在涉及到基于协程和生成器的并发编程中扮演着更加重要的角色。
8. 映射名称到列表元素
你有一段通过下标访问列表或者元组中元素的代码,但是这样有时候会使得你的代码难以阅读, 于是你想通过名称来访问元素。
・collections.namedtuple()・函数通过使用一个普通的元组对象来帮你解决这个问题。 这个函数实际上是一个返回Python中标准元组类型子类的一个工厂方法。 你需要传递一个类型名和你需要的字段给它,然后它就会返回一个类,你可以初始化这个类,为你定义的字段传递值等。 代码示例:
尽管 namedtuple 的实例看起来像一个普通的类实例,但是它跟元组类型是可交换的,支持所有的普通元组操作,比如索引和解压。 比如:
命名元组的一个主要用途是将你的代码从下标操作中解脱出来。 因此,如果你从数据库调用中返回了一个很大的元组列表,通过下标去操作其中的元素, 当你在表中添加了新的列的时候你的代码可能就会出错了。但是如果你使用了命名元组,那么就不会有这样的顾虑。
为了说明清楚,下面是使用普通元组的代码:
下标操作通常会让代码表意不清晰,并且非常依赖记录的结构。 下面是使用命名元组的版本:
命名元组另一个用途就是作为字典的替代,因为字典存储需要更多的内存空间。 如果你需要构建一个非常大的包含字典的数据结构,那么使用命名元组会更加高效。 但是需要注意的是,不像字典那样,一个命名元组是不可更改的。比如:
如果你真的需要改变属性的值,那么可以使用命名元组实例的 _replace() 方法, 它会创建一个全新的命名元组并将对应的字段用新的值取代。比如:
_replace() 方法还有一个很有用的特性就是当你的命名元组拥有可选或者缺失字段时候, 它是一个非常方便的填充数据的方法。 你可以先创建一个包含缺省值的原型元组,然后使用 _replace() 方法创建新的值被更新过的实例。比如:
下面是它的使用方法:
最后要说的是,如果你的目标是定义一个需要更新很多实例属性的高效数据结构,那么命名元组并不是你的最佳选择。 这时候你应该考虑定义一个包含 slots 方法的类.
总结
以上所述是小编给大家介绍的Python数据类型之列表和元组的方法实例详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!