本章涉及到的常用的数据结构:元组,列表,字典和集合。
3.1数据结构和序列
3.1.1元组(小括号)
(1)创建元组
创建元组最简单的方式是用逗号隔开
tup = 4,5,6
更复杂就用中括号将值包起来
tup = (4, 5 , 6), (7, 8)
(2)用tuple()函数转换成元组
可用tuple()将任意序列或迭代器转换为元组
tuple([4, 5 , 6])
tuple('string')
(3)获取:元组的元素
可以使用中括号来[ ]来获取
tup[0]
(4)位置对象不可更改
对象元组中存储的对象其自身是可变的,但元组一旦创建,各个位置上的对象是无法被修改了
tup = tuple(['foo', [1, 2], True])
tup[2] = False
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
C:\Users\ADMINI~1\AppData\Local\Temp/ipykernel_1156/211141333.py in <module>
1 tup = tuple(['foo', [1, 2], True])
----> 2 tup[2] = False
TypeError: 'tuple' object does not support item assignment
(5)对象内部可变
例如列表,可以在内部进行修改
tup[1].append(3)
用“+”联接元组来生成更长的元组
('foo', [1, 2], True) + (7, 8)
元组乘以整数会像列表一样,生成含有多份拷贝的元组
(7, 8) * 3
(6)元组拆包
如果想要将元组型的表达式赋值给变量,Python会对等号右边的值进行拆包
tup = 4,5,6
a, b, c = tup
即使嵌套元组也可以拆包
tup = 4, 5, (6, 7)
a, b, (c, d) = tup
因些在python中,交换可以简单完成
a, b = 1, 2
b, a = a, b
拆包:遍历元组或列表组成的序列
sep = ((1, 2, 3), (4, 5, 6), (7, 8, 9))
for a, b, c in sep:
print('a={0}, b={1}, c={2}'.format(a, b, c))
(7)拆包特殊语法 *rest
利用*rest,调用任意长度的位置
sep = (1, 2, 3, (4, 5, 6), (7, 8, 9))
a, b, *rest = sep
a, b
rest
*rest有时是不想的数据,也可以用下划线(_)来代替rest不想要的数据
a, b, *_ = sep
(8)元组方法
常用的就count() 统计数量
a = (1, 2, 2, 2, 3)
a.count(2)
3.1.2列表[中括号]
(1)定义列表
可使用中括号[ ]或者list类型函数来定义列表
a_list = [1, 2, 3, None]
tup = ('foo', [1, 2], True)
b_list = list(tup)
(2)迭代器或生成器转化为列表
list函数在数据处理中常用于迭代器或生成器转化为列表
gen = range(10)
list(gen)
(3)增加与移除元素
增加:append将元素添加到列表的尾部
list = ['foo', [1, 2], True]
list.append('ok')
插入:insert方法将元素插入到指定的列表位置
list = ['foo', [1, 2], True]
list.insert(1,'no')
删除:pop,insert的反向操作
list = ['foo', [1, 2], True]
list.pop(1)
移除: remove方式移除,定位第一个符合要求的值并移除它
list = ['foo', [1, 2], True, 'foo']
list.remove('foo')
(4)检查值是否在列表中
使用 in 或 not in ,检查值是否在列表中
list = ['foo', [1, 2], True]
'yes' in list
'yes' not in list
(5)连接和联合列表
可像元组用“+”连接
['foo', [1, 2], True] + [3, 4]
extend() 增加多个元素
list = ['foo', [1, 2], True]
list.extend([3, 4])c
注:使用extend()来联接比用“+”号速度要快,因为extend()是把元素添加在原有的列表中
(6)排序
调用列表中sort() 的方法给列表进行内部排序(不需要新建一个对象)
a = [5, 8, 6, 2, 4]
a.sort()
传递二级排序key,用于生成排序值的函数
b = ['sort', 'saw', 'long', 'hello']
b.sort(key=len)
(7)二分搜索和已排序列表的维护
import bisect
a = [2, 4, 5, 6, 8]
bisect.bisect(a, 3) #插的位置
bisect.insort(a, 7) #进行插入
注:要先排列再使用bisect函数,不排列虽不会报错,但会导致不正确的结果
(8)切片
切片是start:stop 包括start不包括stop,数量是stop - start,使用中括号,
a[1:3]
可以给序列赋值给变量
a[1:3] = [5, 6]
start,stop是可以省略的,省略则是传入起始位置或结束位置
a[:3]
a[3:]
负索引可以从序列的尾部进行索引
a[-2:]
a[-4:-2]
步进值step可以在第二个冒号中使用,且当为-1是对列表或元组进行倒序
a[::2]
a[::-1]
3.13内建序列函数
(1)enumerate()
利用enumerate函数,返回(i,value) 元组的序列,前者是元素的索引,后者是元素的值
for i, value in enumerate(collection):
#使用值做点事
(2)sorted()
sorted函数返回一个根据任意序列中的元素新建的已排序列表
sorted([7, 5, 9,6, 8])
sorted('house is big') #空格也会被拆开
(3)zip
zip将列表、元组或其他序列的元素配对,新建一个元组构成的列表
a = [1, 2, 4, 3]
b = ['sort', 'saw', 'long', 'hello']
zipped = zip(a, b)
list(zipped)
zip可以处理任意长度的序列,生成列表长度由最短的序列决定
c = ['flase', 'ture']
ziepped_2 = zip(a, b, c)
list(ziepped_2)
zip的常用场景为同时遍历多个序列,有时候会和enunerate同时使用
for i, (n, m) in enumerate(zip(a, b)):
print('{0} {1} {2}'.format(i, n, m))
zip函数的另一个用法:拆分数列
a = [1, 2, 4, 3]
b = ['sort', 'saw', 'long', 'hello']
zipped = zip(a, b)
c = list(zipped)
a, b = zip(*c)
(4)reversed
reversed函数将序列的元素倒序排列:
list(reversed(range(10))
注:reversed是一个生成器,如果用list函数或进行for循环的时间,它并不会产生一个倒序的列表
3.1.4字典(大括号)
(1)创建字典
dict(字典)非常重要,键和值都是python对象。
empty_dict = {}
d1 = {'a':'value', 'b':[1, 2, 3, 4]}
d1
(2)访问,插入元素
#插入元素
d1[7] = 'other'
#查询元素
d1['b']
(3)检查字典是否有一个键(注是键非值)
'b' in d1
(4)删除值
列表使用remove,字典使用del关键字或pop方法删除值,pop方法会在删除的同时返回被删的值并删除键
del d1[7]
d1.pop('c')
(5)键与值输出
字典中的键与值没有固定排序,但函数输出的键、值都是按照相同的顺序
list(d1.keys())
list(d1.values())
(6)update方法将两个字典合并
d1.update({'b':'换了', 'c':'hello'})
注:重复的键,会被新增的值, 所替代
(7)从序列生成字典
通常的写法
mapping = {}
for key, value in zip(key_list, value_list):
mapping[ke] = value
由于字典本质是2-元组(含有两个元素的元组)的集合,字典可以接受一个2-元组的列表作为参数的
(8)默认值
通常情况下的,代码逻辑
if key in some_dict:
value = some_dict[key]
else:
value = defult_value
使用get函数简化上面的代码化
value = some_dict.get(key, defult_value)
常见的场景,把字典中的值集合通过设置,成为另一种集合,比如列表:把字词组成的列表根据首字母分类为包含列表的字典
words = ['apple', 'all', 'bat', 'bar', 'book']
by_dict = {}
for word in words:
value = word[0]
if value not in by_dict:
by_dict[value] = [word]
else:
by_dict[value].append(word)
字典的setdefault方法是为了上面的目的而产生的。for循环语句可以被写成
for word in words:
value = word[0]
by_dict.setdefault(value, []).append(word)
(9)有效的字典键类型
字典的值可以是任何python对象,但键必须是不可变的对象,通过hash函数可以检查一个对象是否可以哈希化(不可变)
hash('string')
hash(((1,2), 3))
hash([1.2], 3) #这个是错的,列表是可变,不可当键,
如果列表需要当键,把他转成元组
d = {}
d[tuple([1, 2, 3]) = 5
3.1.5集合(无序,元素唯一)
(1)创建集合
有两种办法一种是用set函数,一种是用字面值集与大括号的语法:
set([1, 2, 2, 2, 3])
{1, 2, 2, 2, 3}
(2)联合、交集、差集、对称差集
a = {1, 2, 3, 4, 5}
b = {3, 4, 5, 6, 7, 8}
#合集
a.union(b)
a | b
#并集
a.intersection(b)
a & b
Python 集合操作
方法 | 替代方法 | 描述 |
N/A | 为集合a添加元素x | |
N/A | 拷贝一个集合 | |
N/A | 移除指定元素x | |
a.clear() | N/A | 移除集合中的所有元素 |
N/A | 随机移除元素 | |
N/A | 删除集合中指定的元素 | |
N/A | 集合a移除指定元素x | |
a -= b | 将a设为a不在b的元素(a = a - b) | |
a - b | 返回多个集合的差集(a不在b的元素) | |
a & b | 返回集合的交集 | |
a &= b | 将集合a,返回集合的交集。 | |
a | b | 返回两个集合的并集 | |
a |= b | a更新为两个集合的并集 | |
N/A | 判断两个集合是否包含相同的元素,如果没有返回 True,否则返回 False。 | |
N/A | 如果a是b的子集返回True | |
N/A | 如果a是b的超集返回True | |
N/A | 返回两个集合中不重复的元素集合。 | |
N/A | a更新为,两个集合中不重复的元素集合 |
替代的方法,使代码效率更高。
检查,一个集合是否是另一个集合的子集或超集:
my_date = {1, 2, 3}
print(my_date.issubset({1, 2, 3, 4, 5}))
print({1, 2, 3, 4, 5}.issuperset(my_date))
(3)列表转化为集合
和字典类似,集合的元素是不可变的。列表转集合,要先转为元组。
my_date = (1, 2, 3)
my_set = {tuple(my_date)}
print(my_set)
3.1.6列表、集合和字典的推导式
(1)普通推导式
a.列表推导式
用简明的表达式转换传递给过滤器的元素,从而生成一个新的列表,列表推导式的基本形式为:
[expr from val in collection if condition] # expr:表达式
等价于:
result = [ ]
for val in collection:
if condition:
result.append(expr)
eg:若给定一个字符串,可以过滤出大于2的,并且将字母改为大写:
strings = ('bat', 'not', 'ok', 'hello', 'look')
[x.upper() for x in strings if len(x) > 2]
print(strings)
推导式不会更改来的原来变量,可以给变量重赋值
strings = [x.upper() for x in strings if len(x) > 2]
过滤条件是可以忽略的,只保留表达式。
[x.upper() for x in strings]
b.集合与字典推导式
集合与字典推导式是列表推导式的自然拓展,有相同方式生成的集合与字典
字典推导式:
dict_comp = {key-expr : value-expr for value in collection if condition}
集合推导式:是列表推导式中括号换成大括号
set_comp = {expr for value in collection if condition}
eg:假设要一个集合要字符串的长度,可以用集合推导式很方便实现
strings = ('bat', 'not', 'ok', 'hello', 'look')
set_comp = {len(x) for x in strings}
但也只可以用map函数更简化:
set_comp = set(map(len, strings))
eg:创建一个将字符串和其位置匹配的字典(用字典推导式)
strings = ('bat', 'not', 'ok', 'hello', 'look')
dict_comp = {value : index for index, value in enumerate(strings)}
strings = ('bat', 'not', 'ok', 'hello', 'look')
dict_comp = {value : index for index, value in enumerate(strings)}
(2)嵌套列表推导式
嵌套列表推导式与for表达式的顺序是一致的
eg:一个包含列表的列表,想获得含两个及以上字母e的名字
all_data = [['John', 'Emily', 'Michael', 'Mary', 'Steven'],
['Maria', 'Juan', 'Javier', 'Natalia', 'Pilar']]
name = [name for names in all_data for name in names if name.count('e') >= 2]
print(name)
eg:将元组的列表,扁平化为简单的列表:
nums = [(1, 2, 3),(4, 5, 6), (7, 8, 9)]
num_list = [x for tup in nums for x in tup]
print(num_list)
还可以换成列表的列表
num_lists = [[x for x in tup]for tup in nums]
print(num_lists)
3.2函数
函数声明时用def关键字,返回时使用return关键词
def my_fumction(x, y, z = 1.5):
if z > 1:
return z * (x + y)
else:
return z / (x + y)
可以有多条返回语句,如果不返回语句,刚自动返回None。
上面中,x, y 是位置参数, z 是关键字参数,因此函数可以通过以下任意方式调用
my_fumction(5, 6, 0.7)
my_fumction(5, 6, z = 2.8)
my_fumction(5, 6)
也可以使关键字参数向位置参数传参
my_fumction(x = 5, y = 6, z = 2.8)
my_fumction(y = 6, x = 5, z = 2.8)
(1)命名空间、作用域和本地函数
函数有两种联接变量的方式 :全局,本地
在函数内部,任意变量都默认分配到本地命空间的。
本地的命名空间被调用时生成的,并立即由函数的参数填充,但函数执行结束后,本地命名空间一般就会被销毁。
def func():
a = []
for i in range(5):
a.append(i)
当调用func()时,空的列表a会被创建,五个元素被添加到列表中,之后a会在函数退出时被销毁。但如果像下面这样声明a:
a = []
def func():
for i in range(5):
a.append(i)
print(func())
转出的仍是空集,但函数外部给变量赋值是可以的,那些变量必须使用global关键字声明为全局变量。
a = []
def func():
global a
for i in range(5):
a.append(i)
func()
print(a)
3.2.2返回多值
返回什么的值(多个值)都是看需要,返会的多个值,实质是返回一个元组,而元组之后又被拆包为多个结果变量。
def func():
a = 2
b = 5
c = 9
return a, b, c
a, b, c = func()
print(a, b, c)
3.2.3函数是对象
由于Python的函数是对象,在其它语言中比较难的构造在Python中非常容易被实现。
eg:对字符串进行清理
states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda',
'south carolina##', 'West virginia?']
import re
def clean_strings(strs):
result = []
for i in strs:
i = i.strip()
i = re.sub('[!#?]', '', i)
i = i.title()
result.append(i)
return result
print(clean_strings(states))
上面for里面有3行,略有重复,更为优雅一些
import re
states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda',
'south carolina##', 'West virginia?']
def remove_punctuation(value):
return re.sub('[!#?]', '', value)
ops = (str.strip, remove_punctuation, str.title)
def clean_strings(strs):
result = []
for i in strs:
for function in ops:
i = function(i)
result.append(i)
return result
print(clean_strings(states))
像上面这样更为函数化的可以在更高层次上方面也修改字符串方法,clean_strings函数现在也具有更强的复用性和通用性。
3.2.4匿名(Lambda)函数
匿名函数的是一个通过单个语句生成的函数的方式,其结果是返回值。匿名函数使用lambda关键字定义。
def short_function(x): return x * 2
equiv_anon = lambda x: x * 2
3.2.5柯里化:部分参数应用
柯里化是计算机科学术语(数学家的名字),表示通过部分参数应用的方式从已有的函数中衍生出新的函数。eg:原有两个相加的函数
def add_nums(x, y):
return a + b
使用这个函数衍生出一个变量的新函数,add_five函数:
add_five = lambda y : add_nums(5, y)
对于函数add_nums()就是柯里化了,新的函数调用了已经存在的函数。内键fuctools模块可以使用pratial函数简化这种处理
from functools import partial
add_five = partial(add_nums, 5)
3.2.6生成器
(1)迭代器
在python中通过迭代器协议来实现一致的方法遍历序列,迭代器协议是一种令人对象可遍历的通用方式。eg:遍历一个字典,获得的字典的键
some_dict = {'a': 1, 'b': 2, 'c': 3}
for key in some_dict:
print(key)
在for key in some_dictr的语句,Python解释器首先尝试根据some_dict生成一个迭代器:
dict_iterator = iter(some_dict)
dict_iterator
迭代器是一种用于在上下文中(比如for循环)向Python解释器生成对象的对象。大部份以列表或列表型对象为参数的方法都可以接受任意的迭代器对象。包括内建方法比如min、max和sum,以及类型构造函数比如list和tuple。
list(dict_iterator)
(2)生成器
生成器是构造新的可遍历对象的一种非常简洁的方法。普通函数执行并一次返回单个结果,而生成器则“惰性”地返回一个多结果序列,在每一个元素产生之后暂停,直到下一个请求。需要创建一个生成器,只需要在函数中将返回关键字return替换为yield关键字。
def squares(n=10):
print('Generating squares from 1 to {0}'.format(n ** 2))
for i in range(1, n + 1):
yield i ** 2
实际调用生成器时,代码并不会立即执行:
gen = squares()
gen
要请求生成器中的元素时,它才会执行它的代码
for x in gen:
print(x, end=' ')
(3)生成器的表达式
生成器的表达式创建生成器更为简单,它与列表 、字典、集合的推导式很类似,创建一个生成器表达式,只需要将推导式的中括号替换成小括号:
gen = (x ** 2 for x in range(100))
gen
很多时候生成器可以作为函数参数用于列表的推导式
sum(x ** 2 for x in range(100))
dict((i, i **2) for i in range(5))