读一读我——无废话Python(二)容器类型

Lists

list 容器类型存放的是序列数据。之前说过 str 是不可变类型,list 是可变类型,也就是说我们可以修改 list 里面存放的内容。

a = [1, 2, 3]
print(len(a)) #3

上面的代码展示了如何创建一个 list,list 可以存放重复的元素;len 函数可以返回 list 中含有的元素个数。

在 Python 中有个关键字 is,它是用来判断两个变量是否在内存中的地址是相同的。让我们看看下面的代码:

a = b = 11
print(a is b) # True
print([1] is [1]) # False

上面的代码明确地告诉了我们,即使我们创建了两个 list,里面存放的内容一样,那它们也是两个不一样的 list,也就是说,它们俩在内存中的地址是不一样的;或者说,改变其中一个 list 的内容不会影响另一个 list。

a 和 b 既然有同样的内存地址,如果改变了 a 的值,会不会也把 b 改了呢?当然不会,因为 int 是不可变类型,如果改变了 a 的值,那么 a 就有了新的内存地址,当然和 b 不一样了。

为 list 添加元素,Python 提供了三个常用的方法:append,insert 以及列表连接或者说相加操作。

# append
list1 = [1, 2, 3]
list1.append(4)
print(list1) # [1, 2, 3, 4]

# insert
list2 = [1, 2, 4]
list2.insert(2, 3)
print(list2) # [1, 2, 3, 4]

# list + list
print([1, 2, 3] + [4]) # [1, 2, 3, 4]

三个方法创建了三个内容一样的 list。但这里面 append 是最快的。因为 insert 需要先找到待插入的索引位置,再插入元素;list 连接操作需要创建一个新的 list 然后再插入待连接的两个 list 的内容。

Python 还提供了另一个方法,extend 函数,它允许你一次添加多个元素到给定的 list 中,但它的内部实现机制更加有效。

下面我们来看看如何删除一个元素:

a = [1, 2, 3, 4]
a.remove(1)
print(a) # [2, 3, 4]

remove 函数不会创建一个新的 list,也就是说,remove 是在原有的 list 上进行删除操作,不需要拷贝原有内容,也就减少了内存的开销。

下面看看如何反转一个 list:

a = [1, 2, 3, 4]
a.reverse()
print(a) # [4, 3, 2, 1]

reverse 函数和 remove 函数一样,是在原来的 list 上直接操作,不会创建新的 list。

下面看看排序:

a = [4, 2, 1, 3]
a.sort()
print(a) # [1, 2, 3, 4]

sort 函数也一样,在原来的 list 进行修改排序,排序结果为升序。如果 list 里面存放的是 str 类型,那么将按照字母升序方式进行排序。sort 函数假定认为 list 中的元素是可比较的,也就是说,如果任意的对象都能比较大小,那就可以使用 list 的 sort 方法。

查找元素:通过 index 函数我们可以找到指定元素的索引

print([1, 1, 3].index(1)) # 0
print([2, 2, 6].index(2, 1)) # 1

index 一个参数的版本将返回所要查找元素第一次出现的索引值。像大多数编程语言一样,index 返回的索引值的基数是从 0 开始的。

Stacks

stack 也就是栈结构,它的特性就是后进先出。想象一份报纸一份报纸的堆叠起来,最后放上去的报纸一定先被拿走。

Python 中的可以用 list 来实现 stack。append 函数添加元素到栈顶,pop 函数移除最后添加的元素。

s = [1]
s.append(2) # [1, 2]
s.pop() # 2 s的值为:[1]
s.pop() # 1 s的值为:[]

由于 Python 实现 list 的机制很高效,所以完全没有必要再去创建或者引用第三方实现的 stack,用 list 即可。

Sets

set 是一个基本的数据结构,大多数流行的编程语言中都有。在 MapReduce 和 Apache Spark 中甚至还把 set 当作了基本类型来使用。set 的特性有两个,1.元素无序,2.元素无重复。

set 像 list 和 tuple 一样,是个容器类型,里面可以存放基本类型,或者 objects,tuples。然而有一个必要条件就是,存放在 set 中的元素必须是可哈希的,也就是说每个元素都有一个关联的哈希值。一个对象的哈希值是永远不会变得,哈希值被用来比较两个对象。

s1 = "abc"
s2 = "def"
s3 = "ghi"
print(hash(s1)) # -2789987385810387064
print(hash(s2)) # -1296788039880291611

set1 = {s1, s2, s3}
print(set1) # {'abc', 'ghi', 'def'}

l1 = [s1, s2]
l2 = [s3]
set2 = {l1, l2} # TypeError: unhashable type: 'list'

上面的代码很清楚地解释了 set 里面可以存放什么东西,str 是可哈希的,所以可以放进 set 中,list 是不可哈希的,所以 set 中不能存放 list。那为什么 list 是不可哈希的呢,因为 list 是可变数据结构,如果一个 list 改变了,那它的哈希值必须跟着变。

上面代码的 set1 变量,虽然我们按顺序加入了 s1,s2,s3,但打印的结果确是无序的,这恰恰反映了 set 的无序特性。

下面看看唯一性:

set3 = {s1, s1, s1, s1, s2, s3}
print(set3) # {'abc', 'ghi', 'def'}

记住,不能哈希的不能存,同样哈希的只存一个。

Dictionaries

字典是一个十分有用的数据结构,它的存储方式是键值对。下面是创建,取值,添加等基本操作:

dic1 = {'a': 123, 'b': 456, 'c': 789}

print(dic1['a'] < dic1['b']) # True

dic1['d'] = 321

keys 函数返回所有 key 的集合,values 函数返回所有值的集合:

print('a' in dic1.keys()) # True
print(123 in dic1.values()) # True

同时访问 key 和 value 使用 items 函数:

for k, v in dic1.items():
  print(k) if v > 456 else None # 'c'

下面我们再来看看 set,list 和 dictionary 共有的一些操作:

print(1 in [1, 2, 3]) # True
print("a" in {"a", "b", "c"}) # True
print("set" in {"list": [1, 2, 3], "set": {1, 2, 3}}) #True

in 操作可以用来检查某元素是否在容器里。

set 的检查操作比 list 的快,这是因为,检查一个元素是否在 list 中,需要遍历整个 list;而检查一个元素是否在 set 中,Python 的内部实现只执行了一个操作,a[hash(b)](a 为集合,b为要检查的元素),查看返回值是否为 None 来判断是否包含此元素。

最后来简单说下 list comprehension,列表解析。它的结构为 [ 表达式 + 环境 ]。

表达式:告诉 Python 你想对 list 中的每个元素做什么。

环境:告诉 Python 你的 list 是哪个 list

举个栗子:

[a for a in range(10)]

表达式:a,只是简单地把 list 中的元素拿出来,不做任何操作。

环境:a in range(10),a的取值范围是 range(10) 产生的 list。

set comprehension 的使用方式和 list 一致。

请关注公众号“读一读我”

你可能感兴趣的:(读一读我——无废话Python(二)容器类型)