30天Python入门到进阶——第4天:数据类型(Ⅲ)

30天Python入门到进阶——第4天:数据类型(Ⅲ)

随着我分享我每天学习python的进度,我越来越清楚和明显地看到,同时学习和分享解释概念有助于巩固知识,也是提升自己能力的一种有效方式。

从第 3 天结束的地方开始,我今天继续探索列表和剩余的数据类型。

列表

列表函数

就像字符串一样,Python为我们提供了一些内置方法来对列表数据类型执行一些操作。同样,在对象上的​​.​​运算符之后调用方法。可以根据动作类型对动作进行分类。

下图中的方法是列表专有的内置方法,请熟记于心。

方法

作用

append(obj)

在列表末尾添加新的对象

count(obj)

统计某个元素在列表中出现的次数

extend(seq)

在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)

index(obj)

从列表中找出某个值第一个匹配项的索引位置

insert(index, obj)

将对象插入列表

pop(obj=list[-1])

移除列表中的一个元素(默认最后一个元素),并且返回该元素的值

remove(obj)

移除列表中某个值的第一个匹配项

reverse()

反向列表中元素

sort([func])

对原列表进行排序

copy()

复制列表

clear()

清空列表,等于del lis[:]

注意:其中的类似 append,insert, remove 等方法会修改列表本身,并且没有返回值(严格的说是返回None)。

添加列表(append, insert, extend)

scores = [44,48,55,89,34]
scores.append(100) # Append adds a new item to the end
print(scores) # [44, 48, 55, 89, 34, 100]
scores.insert(0, 34) # Inserts 34 to index 0
scores.insert(2, 44) # Inserts 44 to index 2
print(scores) # [34, 44, 44, 48, 55, 89, 34, 100]
scores.extend([23]) # Extend takes an iterable (loopable items) and adds to end of list
print(scores) # [34, 44, 44, 48, 55, 89, 34, 100, 23]
scores.extend([12,10])
print(scores) # [34, 44, 44, 48, 55, 89, 34, 100, 23, 12, 10]

这里有一个小问题。这些方法将元素添加到列表中,不返回任何值。

scores = [44,48,55,89,34]
newScores = scores.append(100)
print(newScores) # None
newScores = scores.insert(0,44)
print(newScores) # None

从列表中删除项目(pop, remove, clear)

languages = ['C', 'C#', 'C++']
languages.pop()
print(languages) # ['C', 'C#']
languages.remove('C')
print(languages) # ['C#']
languages.clear()
print(languages) # []

获取索引和计数(index, count)

alphabets = ['a', 'b', 'c']
print(alphabets.index('a')) # 0 (Returns the index of the element in list
print(alphabets.count('b')) # 1 (counts the occurence of an element

排序、反转和复制列表(sort,reverse,copy)

numbers = [1,4,6,3,2,5]
numbers.sort() # Sorts the list items in place and returns nothing
print(numbers) # [1, 2, 3, 4, 5, 6]

#Python also has a built in sorting function that returns a new list
sorted_numbers = sorted(numbers) # note - this is not a method
print(sorted_numbers) # [1, 2, 3, 4, 5, 6]

numbers.reverse() # reverse the indices in place
print(numbers) # [6, 5, 4, 3, 2, 1]

numbers_clone = numbers.copy() # another approach is numbers[:]
print(numbers_clone) # [6, 5, 4, 3, 2, 1]

列表模式

最后,我探讨了一些经常与列表一起使用的常见模式,例如我已经提到的反转、将列表加入字符串和复制。

avengers = ['ironman', 'spiderman', 'antman', 'hulk']
cloned_avengers = avengers[::1] # very commonly used pattern
reversed_avengers = avengers[::-1] # discussing again because it is also very common
merge_avengers = ' '.join(avengers) # used to join list into string
print(cloned_avengers) # ['ironman', 'spiderman', 'antman', 'hulk']
print(reversed_avengers) # ['hulk', 'antman', 'spiderman', 'ironman']
print(merge_avengers) # ironman spiderman antman hulk

range_of_numbers = list(range(10)) # quickly generates a list of specific range
print(range_of_numbers) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
another_range = list(range(0,5)) # with start stop
print(another_range) # [0, 1, 2, 3, 4]

列表解析

列表解析是一个不错的功能。它让我想起了 JavaScript 中的数组解构,这也非常酷。

first,second,third = ['tesla','ford','ferarri']
print(first) # tesla
print(second) # second
print(third) # ferarri

a,*others = [1,2,3,4,5] # remaining values are stored in others
print(a) # 1
print(others) # [2, 3, 4, 5]

first,*others,last= [?,?,?,?,?]
print(first) # ?
print(others) # ['?', '?', '?']
print(last) # ?

堆栈

Python的列表特别适合也很方便作为一个堆栈来使用。堆栈是一种特定的数据结构,最先进入的元素最后一个被释放(后进先出)。将列表的表头作为栈底,表尾作为栈顶,就形成了一个堆栈。用列表的append()方法可以把一个元素添加到堆栈顶部(实际上就是在列表的尾部添加一个元素)。用不指定索引的pop()方法可以把一个元素从堆栈顶释放出来(也就是从列表尾部弹出一个元素)。例如:

>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]

列表在内存内部是顺序存储结构的,所以在其尾部的添加和删除动作,也就是append和pop方法的效率非常高,具备随机存取速度,也就是O(1)的时间复杂度,因此用作堆栈是再合适不过了。

队列

也可以把列表当做队列用。队列是一种先进先出的数据结构。但是用Python的列表做队列的效率并不高。因为,虽然在列表的最后添加或者弹出元素速度很快,但在列头部弹出第一个元素的速度却不快(因为所有其他的元素都得跟着一个一个地往左移动一位)。通常我们使用queue.Queue作为单向队列,使用collections.deque作为双向队列。

空值:None

空值不是布尔类型,只不过和布尔关系比较紧密。空值是Python里一个特殊的值,用None表示(首字母大写)。None不能理解为0,因为0是整数类型,而None是一个特殊的值。None也不是布尔类型,而是NoneType。在大多数其他编程语言中,它通常被称为null。

>>> bool(None)
False
>>> type(None)
<class 'NoneType'>

我们平时最容易犯的错误就是获得了一个None值,却对它进行各种方法调用,例如:

list1 = ["a", "b", None]

for char in list1:
print(char.join("A"))

######################################

Traceback (most recent call last):
File "F:/Python/pycharm/201705/test.py", line 7, in <module>
print(char.join("A"))
AttributeError: 'NoneType' object has no attribute 'join'

字典

字典​​dict​​​是Python中的一种数据类型,它在键值对中包含无组织的数据集合。所以字典是一种以特定格式存储数据的数据结构。在我的认知模型中,我将其与JavaScript​​object​​​进行了比较,在JavaScript​​object​​​中,我们将数据存储在键值对中。​​dict​​的键由字符串表示,值可以包含任何数据类型。可以使用相应的键访问这些值。因为字典没有任何顺序,所以它们分散在内存中,不像列表那样,它们在内存中按顺序存储。

字典可精确描述为不定长、可变、散列的集合类型。字典元素在内存中的存储方式是不连续的,也没有链接关系,所以千万不要用列表的序列性质来套字典的性质。

字典的每个键值对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中 ,例如:

​d = {key1 : value1, key2 : value2 }​

注意:从Python3.6开始,字典是有序的!它将保持元素插入时的先后顺序!请务必清楚!

user = {'name': 'Max', 'age': 40, 'married': False}
print(user['name']) # Max
print(user['married']) # False

字典键

我提到字典中的键必须是​​string​​​数据类型。嗯,这并不完全正确。​​dict​​键可以是任何不可变的数据类型。此外,键必须是唯一的。如果字典有多个相同的键,则这些值将被覆盖。这也称为冲突

abstract = {
'first': 123,
True: 'hello',
777: [1,3,4,5]
}

print(abstract['first']) # 123
print(abstract[True]) # 'hello
print(abstract[777]) # [1,3,4,5]

sample = {
'username': 'hisenberg',
'username': 'james'
}
print(sample['username']) # james

字典函数

检查错误是一种很好的编程习惯,因为错误会破坏程序的执行。在字典的上下文中,如果我们尝试访问一个不存在的键,Python 将抛出错误并停止程序执行。这不是我们通常想要的,所以有一个内置的字典方法来处理这个:

house = {
'rooms' : 4,
'occupants': 2,
'doors': 6
}
print(house['windows']) # KeyError: 'windows'
#instead
print(house.get('windows')) # None
print(house.get('windows', 5)) # 5 (This sets a default value if no value is found)

还有一些其他方法可以检查字典中是否存在特定的键或值。

user = {'name': 'Raghav', 'age': 20, 'country': 'India'}
print('name' in user.keys()) # True
print('gender' in user.keys()) # False
print('Raghav' in user.values()) # True

其他一些有用的字典方法是复制清除、弹出、更新。

cat = {
'name': 'Tom',
'greet': 'meow',
'health': 100
}
cat_copy = cat.copy()
print(cat_copy) # {'name': 'Tom', 'greet': 'meow', 'health': 100}

cat.pop('name')
print(cat) # {'greet': 'meow', 'health': 100}

cat.clear()
print(cat) # {}

cat_copy.update({'name': 'Polo'})
print(cat_copy) # {'name': 'Polo', 'greet': 'meow', 'health': 100}
cat_copy.update({'color': 'Black'}) # adds key value if not present
print(cat_copy) # {'name': 'Polo', 'greet': 'meow', 'health': 100, 'color': 'Black'}

下表中列出了字典的重要内置方法。其中的​​get​​​、​​items​​​、​​keys​​​和​​values​​是核心中的核心,必须熟练掌握!

方法

作用

clear()

删除字典内所有元素

copy()

返回一个字典的浅复制

fromkeys()

创建一个新字典,以序列seq中元素做字典的键

get(key)

返回指定键的值,如果键不在字典中,则返回default值

items()

以列表返回可遍历的(键, 值) 元组对

keys()

以列表返回字典所有的键

values()

以列表返回字典所有的值

pop(key)

删除并返回指定key的值

popitem()

删除并返回字典的最后一个键值对,不接受参数。

setdefault(key, default=None)

和get()类似,但如果键不存在于字典中,将会添加键并将值设为default

update(dict2)

把字典dict2的键/值对更新到dict里

我们来看一些例子:

>>> dic = {'Name': 'Jack', 'Age': 7, 'Class': 'First'}
>>> dic.get('sex') # 访问不存在的key,没有报错,但是IDLE不会显示None
>>> dic['sex'] # 报错
Traceback (most recent call last):
File "<pyshell#44>", line 1, in <module>
dic["sex"]
KeyError: 'sex'
>>> dic.items()
dict_items([('Name', 'Jack'), ('Age', 7), ('Class', 'First')])
>>> dic.values()
dict_values(['Jack', 7, 'First'])
>>> dic.keys()
dict_keys(['Name', 'Age', 'Class'])
>>> dic.pop("Name")
'Jack'
>>> dic
{'Age': 18, 'Class': 'First', 'address': 'Beijing'}
>>> dic.popitem()
('address', 'Beijing')
>>> dic
{'Age': 18, 'Class': 'First'}

遍历字典

从Python3.6开始遍历字典获得的键值对是有序的!以下的遍历方法必须全部熟练掌握。

dic = {'Name': 'Jack', 'Age': 7, 'Class': 'First'}

# 1 直接遍历字典获取键,根据键取值
for key in dic:
print(key, dic[key])

# 2 利用items方法获取键值,速度很慢,少用!
for key,value in dic.items():
print(key,value)

#3 利用keys方法获取键
for key in dic.keys():
print(key, dic[key])

#4 利用values方法获取值,但无法获取对应的键。
for value in dic.values():
print(value)

元组

元组数据类型与列表非常相似,但它们是不可变的,这意味着它们的值不能修改,也不能像列表一样排序。

my_tuple = (1,2,3) # Can be any no of items
print(my_tuple[1]) # 2 (Values can be accessed just like lists)
print(1 in my_tuple) # True (Checks if element is present)

由于元组是不可变的,因此它们也可以用作字典中的键。

元组函数

就像列表一样,我们可以对元组进行切片,因为切片会返回一个新副本并且不会更改原始数据。

元组与列表相同的操作

  • 使用方括号加下标访问元素
  • 切片(形成新元组对象)
  • count()/index()
  • len()/max()/min()/tuple()

元组中不允许的操作,确切的说是元组没有的功能

  • 修改、新增元素
  • 删除某个元素(但可以删除整个元组)
  • 所有会对元组内部元素发生修改动作的方法。例如,元组没有remove,append,pop等方法。

元组与列表类似的特殊操作:

30天Python入门到进阶——第4天:数据类型(Ⅲ)

colors = ('red', 'orange', 'blue', 'yellow')
new_colors = colors[1:4]
print(new_colors) # ('orange', 'blue', 'yellow')

color_1,*others = colors # unpacking!
print(color_1) # 'red'
print(others) # ['orange', 'blue', 'yellow']

print(len(colors)) # 4
print(colors.count('red')) # 1
print(colors.index('orange')) # 1

>>> tup1 = () # 创建空元组
>>> tup1 = (50,) # 创建只包含一个元素的元组时,要在元素的后面跟个逗号
>>> tup1 = ('physics', 'chemistry', 1997, 2000)
>>> tup2 = (1, 2, 3, 4, 5 )
>>> tup3 = "a", "b", "c", "d"
>>> tup = (1, 2, 3, 4)
>>> tup[2]
3
>>> tup[3] = "a"
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
tup[3] = "a"
TypeError: 'tuple' object does not support item assignment

元组看来是很安全的。但真的吗?

元组只保证它的一级子元素不可变,对于嵌套的元素内部,不保证不可变!

>>> tup = ('a', 'b', ['A', 'B'])
>>> tup[2][0] = 'X'
>>> tup[2][1] = 'Y'
>>> tup
('a', 'b', ['X', 'Y'])

Python为什么总是有这种幺蛾子呢???所以,在使用元组的时候,请尽量使用数字、字符串和元组这种不可变的数据类型作为元组的元素,这样就能确保元组不发生变化。

集合

最后是最后一种数据类型:集合set。

集合是一种数据结构,用于存储唯一对象的无序集合。基本功能包括关系测试和消除重复元素。集合使用大括号({})框定元素,并以逗号进行分隔。但是注意:如果要创建一个空集合,必须用 set() 而不是 {} ,因为后者创建的是一个空字典。集合除了在形式上最外层用的也是花括号外,其它的和字典没有一毛钱关系。

set_of_numbers = {1,2,3,4,5,5}
print(set_of_numbers) # {1,2,3,4,5} (Only unique values are stored)

集合数据类型的核心在于自动去重。很多时候,这能给你省不少事。这对于从电子邮件列表中删除重复的电子邮件地址非常有帮助。

emails = ['samantha@hey.com', 'rock@hey.com', 'samantha@hey.com']
emails_set = set(emails)
unique_emails = list(emails_set)
print(unique_emails) # ['rock@hey.com', 'samantha@hey.com']

集合函数

那么集合支持哪些操作呢?全在这里:

>>> dir(set)
['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__iand__', '__init__', '__init_subclass__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']

除了add、clear、copy、pop、remove、update等集合常规操作,剩下的全是数学意义上的集合操作,交并差等等。我觉得没有必要记住它们,这些方法可以随时用在线搜索。

对集合进行交并差等,既可以使用union一类的英文方法名,也可以更方便的使用减号表示差集,“&”表示交集,“|”表示并集。看看下面的例子:

>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket) # 删除重复的
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket # 检测成员
True
>>> 'crabgrass' in basket
False
>>> # 以下演示了两个集合的交、并、差操作
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a # a 中唯一的字母
{'a', 'r', 'b', 'c', 'd'}
>>> a - b # 在 a 中的字母,但不在 b 中
{'r', 'd', 'b'}
>>> a | b # 在 a 或 b 中的字母
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b # 在 a 和 b 中都有的字母
{'a', 'c'}
>>> a ^ b # 在 a 或 b 中的字母,但不同时在 a 和 b 中
{'r', 'd', 'b', 'm', 'z', 'l'}

set_a = {1,2,3,4,5}
set_b = {4,5,6,7,8}
print(set_a.union(set_b)) # {1, 2, 3, 4, 5, 6, 7, 8}
print(set_a | set_b) # same as above just a compact syntax

print(set_a.intersection(set_b)) # {4, 5}
print(set_a & set_b) # same as above

set_a.discard(1)
print(set_a) # {2,3,4,5}

集合数据类型属于Python内置的数据类型,但不被重视,在很多书籍中甚至都看不到一点介绍。其实,集合是一种非常有用的数据结构,它的去重和集合运算是其它内置类型都不具备的功能,在很多场合有着非常重要的作用,比如网络爬虫。

总结

终于完成了 Python 的数据基本构建块,并对其数据类型有基本的了解。希望这几天的内容能够帮助你更好的了解python数据类型,相信在你以后的学习道路上,这些内容有着不可磨灭的功劳。

发表评论

相关文章