列表和元组之间的区别是?
二者的主要区别是列表是可变的,而元组是不可变的
不同点一:不可变 VS 可变 两种类型除了字面上的区别(括号与方括号)之外,最重要的一点是tuple是不可变类型,大小固定,而 list 是可变类型、数据可以动态变化,这种差异使得两者提供的方法、应用场景、性能上都有很大的区别。
不同点二:同构 VS 异构 tuple 用于存储异构(heterogeneous)数据,当做没有字段名的记录来用,比如用 tuple 来记录一个人的身高、体重、年龄。
person = ("zhangsan", 20, 180, 80) 比如记录坐标上的某个点
point = (x, y) 而列表一般用于存储同构数据(homogenous),同构数据就是具有相同意义的数据,比如下面的都是字符串类型
["zhangsan", "Lisi", "wangwu"] 再比如 list 存放的多条用户记录
[("zhangsan", 20, 180, 80), ("wangwu", 20, 180, 80)] 数据库操作中查询出来的记录就是由元组构成的列表结构。
因为 tuple 作为没有名字的记录来使用在某些场景有一定的局限性,所以又有了一个 namedtuple 类型的存在,namedtuple 可以指定字段名,用来当做一种轻量级的类来使用。
列表和字典的区别
列表是序列,可以理解为数据结构中的数组,字典可以理解为数据结构中的hashmap
他俩都可以作为集合来存储数据
从差异特征上来说
list是有序的,dict是无需的
list通过索引访问,dict使用key访问
list随着数量的正常增长要想查找元素的时间复杂度为O(n), dict不随数量而增长而变化,时间负责都为O(1)
dict的占用内存稍比list大,会在1.5倍左右
特征决定用途:
list一般可作为队列、堆栈使用,而dict一般作为聚合统计或者快速使用特征访问等
可变与不可变
可变类型(mutable):列表,字典
不可变类型(unmutable):数字,字符串,元组
这里的可变不可变,是指内存中的那块内容(value)是否可以被改变。如果是不可变类型,在对对象本身操作的时候,必须在内存中新申请一块区域(因为老区域#不可变#)。如果是可变类型,对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的address会保持不变,但区域会变长或者变短。
copy.copy() 浅拷贝
copy.deepcopy() 深拷贝
浅拷贝是新创建了一个跟原对象一样的类型,但是其内容是对原对象元素的引用。这个拷贝的对象本身是新的,但内容不是。拷贝序列类型对象(列表\元组)时,默认是浅拷贝。
列表底层实现
实现细节 python中的列表的英文名是list,因此很容易和其它语言(C++, Java等)标准库中常见的链表混淆。事实上CPython的列表根本不是列表(可能换成英文理解起来容易些:python中的list不是list)。在CPython中,列表被实现为长度可变的数组。
从细节上看,Python中的列表是由对其它对象的引用组成的连续数组。指向这个数组的指针及其长度被保存在一个列表头结构中。这意味着,每次添加或删除一个元素时,由引用组成的数组需要该标大小(重新分配)。幸运的是,Python在创建这些数组时采用了指数过分配,所以并不是每次操作都需要改变数组的大小。但是,也因为这个原因添加或取出元素的平摊复杂度较低。
不幸的是,在普通链表上“代价很小”的其它一些操作在Python中计算复杂度相对过高。
利用 list.insert方法在任意位置插入一个元素——复杂度O(N) 利用 list.delete或del删除一个元素——复杂度O(N)
列表推导
要习惯用列表推导,因为这更加高效和简短,涉及的语法元素少。在大型的程序中,这意味着更少的错误,代码也更容易阅读。
>>>[i for i in range(10) if i % 2 == 0]
[0, 2, 4, 6, 8]
1.使用enumerate.在循环使用序列时,这个内置函数可以方便的获取其索引:
for i, element in enumerate(['one', 'two', 'three']):
print(i, element)
字典底层实现
字典是python中最通用的数据结构之一。dict可以将一组唯一的键映射到相应的值。
字典推导式
squares = {number: number**2 for number in range(10)}
print(squares)
在遍历字典元素时,有一点需要特别注意。字典里的keys(), values()和items()3个方法的返回值不再是列表,而是视图对象(view objects)。
keys(): 返回dict_keys对象,可以查看字典所有键
values():返回dict_values对象,可以查看字典的所有值
items():返回dict_items对象,可以查看字典所有的{key, value}二元元组。
CPython使用伪随机探测(pseudo-random probing)的散列表(hash table)作为字典的底层数据结构。由于这个实现细节,只有可哈希的对象才能作为字典的键。
Python中所有不可变的内置类型都是可哈希的。可变类型(如列表,字典和集合)就是不可哈希的,因此不能作为字典的键。
字典的三个基本操作(添加元素,获取元素和删除元素)的平均事件复杂度为O(1),但是他们的平摊最坏情况复杂度要高得多,为O(N).
还有一点很重要,在复制和遍历字典的操作中,最坏的复杂度中的n是字典曾经达到的最大元素数目,而不是当前的元素数目。换句话说,如果一个字典曾经元素个数很多,后来又大大减小了,那么遍历这个字典可能会花费相当长的事件。因此在某些情况下,如果需要频繁的遍历某个词典,那么最好创建一个新的字典对象,而不是仅在旧字典中删除元素。
字典的缺点和替代方案
使用字典的常见陷阱就是,它并不会按照键的添加顺序来保存元素的顺序。在某些情况下,字典的键是连续的,对应的散列值也是连续值(例如整数),那么由于字典的内部实现,元
素的实现可能和添加的顺序相同:
keys = {num: None for num in range(5)}.keys()
print(keys)
如果我们需要保存添加顺序怎么办?python 标准库的collections模块提供了名为OrderedDicr的有序字典
集合
集合是一种鲁棒性很好的数据结构,当元素顺序的重要性不如元素的唯一性和测试元素是否包含在集合中的效率时,大部分情况下这种数据结构极其有用。
python的内置集合类型有两种:
set(): 一种可变的、无序的、有限的集合,其元素是唯一的、不可变的(可哈希的)对象。 frozenset(): 一种不可变的、可哈希的、无序的集合,其元素是唯一的,不可变的哈希对象。
set里的元素必须是唯一的,不可变的。但是set是可变的,所以set作为set的元素会报错。
实现细节
CPython中集合和字典非常相似。事实上,集合被实现为带有空值的字典,只有键才是实际的集合元素。此外,集合还利用这种没有值的映射做了其它的优化。
由于这一点,可以快速的向集合中添加元素、删除元素、检查元素是否存在。平均时间复杂度为O(1),最坏的事件复杂度是O(n)。