1.python列表,元组,集合,字典之间互相转换

本文着重介绍python 列表(list),元组(tuple),集合(set),字典(dict)四种类型之间的相互转换,转换成python列表需要使用list函数,转成元组需要使用tuple函数,转成集合需要使用set函数,转成字典需要使用dict函数

1. 内置函数list

内置函数list可以将字符串,集合,元组转换为列表

a = 'python'    # 字符串
b = {1, 2, 3}   # 集合
c = (1, 2, 3)   # 元组

print(list(a))
print(list(b))
print(list(c))

输出结果是

['p', 'y', 't', 'h', 'o', 'n']
[1, 2, 3]
[1, 2, 3]

list函数能否将字典转换成列表呢,这个需要探索一下

print(list({'a': 3, 'b': 5}))

输出结果为

['a', 'b']

使用list函数转换字典,得到的是字典里所有的key,并没有获得value

2. 内置函数set

set函数可以将字符串,列表转换成集合

a = 'python'    # 字符串
b = [1, 2, 1, 2]  # 列表
c = {'a': 1, 'b': 2}  # 字典
d = (1, 2, 3, 4)  # 元组

print(set(a))
print(set(b))
print(set(c))
print(set(d))

程序输出结果

{'t', 'o', 'n', 'p', 'h', 'y'}
{1, 2}
{'b', 'a'}
{1, 2, 3, 4}

尝试转换字典时,只能获得由字典的key组成的集合

3. 内置函数tuple

a = 'python'    # 字符串
b = [1, 2, 1, 2]  # 列表
c = {'a': 1, 'b': 2}  # 字典
d = {1, 2, 3, 4}    # 集合

print(tuple(a))
print(tuple(b))
print(tuple(c))
print(tuple(d))

程序输出结果

('p', 'y', 't', 'h', 'o', 'n')
(1, 2, 1, 2)
('a', 'b')
(1, 2, 3, 4)

尝试将字典转换为元组时,只能得到由字典的key组成的元组,看来字典有些特殊

4. 内置函数dict

无法直接使用dict函数将列表,元组,集合转换成字典,这是由字典的结构决定的,字典里的元素永远以key-value对的形式存在,key-value是一个映射关系,知道key,便可以获得value,而其他3种容器类型数据不存在映射关系,因此无法转换。

dict函数有自己特殊的使用方法

my_dict = dict(a=1, b=2)
print(my_dict)

程序输出结果

{'a': 1, 'b': 2}

2.python容器类型数据对比学习

python提供了4种基础容器类型数据,他们是列表,元组,字典,集合。

这四种数据的灵活应用,可以为我们解决大部分数据处理过程中遇到的问题,通过一系列对比学习,希望你可以将这部分知识掌握的更加扎实牢固

1.4个容器类型数据的共同点

4个容器类型数据存在的目的都是为了存储数据,这是他们最大的共同点,也是我们对容器类型数据的基本认识。

这个共同点也引出了他们的不同点,既然都能存储数据,为什么要弄出4个容器类型数据,而不是用一种就可以了呢?

2. 存储数据的目的不同

容器存储目的示例
列表按顺序存储,按顺序使用,单纯的堆积数据lst = [1, 2, 3]
元组按顺序存储,按顺序使用,单纯的堆积数据tup = (1, 2, 3)
字典按关键字存储,提供key到value的映射,映射关系才是我们关心的dic = {'python': 100, 'php': '90'}
集合按关键字存储,主要目的是去重set = {1, 4, 5}

3. 创建,新增,删除,修改,查询的相同与不同

容器创建新增删除修改查询
列表用[] 创建append方法del 方法通过索引进行修改通过索引进行查询
元组用()创建无新增方法无删除方法无修改方法通过索引查询
字典用{}创建通过关键字赋值新增del 方法通过关键字赋值进行修改通过关键字查询
集合使用{}创建add 方法del 方法无修改方法无查询方法

解释一些疑问

疑问1 同样是新增,为什么列表的方法名是append,而集合的新增方法名是add?

列表新增,除了append方法,还有insert方法,append默认在列表尾部追加,insert要指定插入的索引位置,这两个方法名都比较含蓄的体现出了列表的有序特点

集合里没有索引的概念,也就没有尾部的概念,新增方法名用add比较合适,和顺序无关,就是增加一个数据

疑问2 为什么集合没有修改和查询的方法

从使用场景来分析,集合的主要作用是为了去重,不存在修改的操作,至于查询操作,没有索引,也就不能通过索引来查询数据,也不像字典那样一个key对应一个value,因此,也无法像字典那样通过key去查询value,唯一的近似查询的操作是in 这个成员操作符判断某个数据是否在集合中

4. 列表与元组,字典与集合对比

通过前面的学习,很容易发现,列表与元组有点像,字典与集合有点像,下面,对他们进行仔细的比较分析

4.1 列表与元组

列表与元组几乎一样,唯一的不同的是,元组不可以修改。元组没有新增,修改,删除这3个方法,那么元组存在的价值和意义是什么呢?

  • 可以做字典和集合的key
  • 元组可以做函数的返回值

由于元组是不可变对象,因此,元组可以用来做字典和集合的key, 使用元组做函数的返回值,则可以防止函数的使用者修改函数的返回结果,下面的这段代码像你演示这两种功能

lst1 = [10, 3, 2, 6, 7, 5]
lst2 = [3, 2, 5, 6, 10, 7]

combine_set = set()

for item1 in lst1:
    for item2 in lst2:
        if item1 + item2 == 12:
            combine_set.add((min(item1, item2), max(item1, item2)))


print(combine_set)

从两个列表里各取出一个数,令他们的和为12,请问这种组合一共有多少?

5+7= 12,从lst1中取5,lst2中取7符合要求,从lst1中取7,lst2中取5也符合要求,但这两个组合是相同的,这就存在了一个去重的操作要求,为了去重,用两个数组成元组,值小的数放前面,值大的数放后面,这样就达到了去重的目的。

4.2 字典与集合

字典和集合的创建都用{}, 我们可以将集合看成是一个特殊的字典,集合里key和value是相同的,只是集合里隐去了value部分,只保留了key的部分。

字典和集合的key都是不能重复的,因为key存在的意义就是唯一的与一个value建立起映射关系,两个相同的key,不论是映射到相同的value还是不同的value,都没有存在的意义。

3.为什么大多数编程语言中,索引都是从0开始

世界上绝大多数语言,但凡涉及到了索引这个概念,索引都是从0开始的,对于初学者,难免觉得别扭,毕竟生活中,但凡涉及到和顺序有关的概念,都是从1开始的,那么为什么绝大多数语言的索引,都喜欢从0开始呢?

关于这个问题,没有明确的答案,我搜集整理了3个我认为比较具有说服力的理论,分别是

  1. 节省空间论
  2. 硬件初始状态论
  3. 上届,下届差值论

1. 节省空间论

节省空间论强调,从0开始有利于节省内存空间,假如有4个元素,如果索引从0开始,那么最大索引就是3,如果索引从1开始,最大索引就是4

索引从1开始的十进制为:(1,2,3,4),对应的二进制为(1,10,11,100)

索引从0开始的十进制为:(0,1,2,3),对应的二进制为(0,1,10,11)

显然,从索引1开始计数,会多使用一个bit位,在过去那种内存十分昂贵的年代,这是不被接受的,因此从一开始,索引就是从0开始的,后来大家都习惯了,即便内存很便宜了,大家还是从0开始计数索引

2. 硬件初始状态论

其实和前面的节省空间论说的是一回事,不过是从硬件层面做了解释,计算机底层是0和1,0和1又依靠硬件二极管的电位变化来表示,初始状态下,用于计数的所有二极管都处于低电位,正好是0,因此索引从0开始计数

3. 上届,下届差值论

前面两个虽然有一定道理,但是理解起来有点费力,第3个理论认为,索引的上届和下届的差值应该等于数列的长度,假设一个数列有N个元素,如果索引从1开始,那么描述索引的上届和下届就得这么写

1 <= i < N+1

如果索引从0开始,就可以这样写

0 <= i < N

显然,第二种写法更美观,第一种,让人觉得很别扭

4.python当中,True==1成立以及由此引发的一系列问题

python这门语言中,True和1是相等的, False和0是相等的

print(True==1)
print(False==0)

程序输出结果

True
True

究其根本,bool类型是int类型的子类

print(issubclass(bool, int))

程序输出结果

True

看似一个普通且合理的设定,却带来一些意想不到的影响

1. Ture 和 1不能同时作为字典的key

如下面代码所展示的定义一个字典,就会出现意想不到的结果

dic = {
    1: '1',
    True: 'True'
}
print(dic)

程序输出结果

{1: 'True'}

字典不允许key重复,True: 'True'这个key-value对覆盖了前面的,不只是不能同时出现在字典里做key,他们也不能同时出现在集合中,道理是一样的

bool类型的数据,虽然可以做字典的key,但bool类型本身也只有两个常量数值,因此,很不建议你用bool类型数据做key,即便没有1做key

2. Ture 和 1相等,导致统计不准确

lst = [1, True]

for item in lst:
    if item == 1:
        print(item)

其实代码的本意只想找到1并输出,但最终连Ture也一同输出,列表的count函数也会出现相同的情况

lst = [1, True]
print(lst.count(1))

程序输出结果为2,惊不惊喜意不意外,那么有没有什么方法可以避免呢,我们自己实现函数时,还是有办法的,但对于内置函数就无能为力了

lst = [1, True]

for item in lst:
    if not isinstance(item, bool) and item == 1:
        print(item)

先判断所遍历数据不是bool类型,然后在判断是否等于1,这里千万不要判断是否为int类型,True是bool类型,int类型是bool类型的父类,因此isinstance(True, int)返回结果为True

5.不要在遍历python列表时修改列表

使用for循环遍历python列表的同时,如果增加或者删除列表里的元素将会破坏列表的迭代器,产生一些看起来很诡异的现象。

1. 使用remove 删除列表里的元素

已知一个列表,内容如下

lst = [1, 2, 3, 4, 5, 6, 7, 8]

现在要求你删除列表中的奇数,你当如何处理呢,下面是很多人想当然的一种解决方法

lst = [1, 2, 3, 4, 5, 6, 7, 8]

for i in lst:
    if i % 2 == 1:
        lst.remove(i)

print(lst)

程序输出结果

[2, 4, 6, 8]

从结果看,程序似乎是正确的,但这只是一个巧合,将列表内容替换为下面的内容

lst = [1, 3, 2, 5, 7, 9]

再次执行程序,得到的结果是

[3, 2, 7]

这究竟是为什么?

2. remove破坏了迭代器

for循环的本质是使用迭代器进行遍历,删除迭代器访问过的元素,下一次迭代中,迭代器将向后跳转1个元素。

删除元素1时,迭代器向右跳转1个元素,刚好越过了3,是remove 影响了迭代器的正常使用。

如果在for循环进行迭代时,向列表中增加元素,那么迭代器就会向左跳转1个元素,与删除的情况刚好相反。

3. 正确处理方法

想在for循环遍历列表的过程中实现删除操作,可以反向遍历列表

lst = [1, 3, 2, 5, 7, 9]

for i in reversed(lst):
    if i % 2 == 1:
        lst.remove(i)

print(lst)

第二个解决方法是复制一份列表,遍历这个复制的列表,删除原列表里的元素

import copy
lst = [1, 3, 2, 5, 7, 9]

for i in copy.copy(lst):
    if i % 2 == 1:
        lst.remove(i)

print(lst)

这样的复制是浅拷贝,因此几乎不会消耗多少内存。