3 第三章 内建数据结构、函数及文件
元组
可以使用tuple函数将任意序列或迭代器转换为元组;
可以使用+号连接元组来生成更长的元组;
将元组乘以整数,则会生成含有多份拷贝的元组
元组拆包
In[15]: tup = (4, 5, 6)
In [16]: a, b, c = tup
In[17]: b
0ut[17]: 5
交换变量名:
In[21]:a, b = 1, 2
In [24]:b, a = a, b
遍历元组或列表组成的序列:
seq = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
for a, b, c in seq:
print(f'a={a}, b={b}, c={c}')
多个元素:
values = 1, 2, 3, 4, 5
a, b, *rest = values
a
b
rest
Python编程者会使用下划线来表示不想要的变量:
a, b, *_ = values
一个常见的有用方法是count(列表中也可用),用于计量某个数值在元组中出现的次数:
a = (1, 2, 2, 2, 3, 4, 2)
a.count(2)
列表
list函数在数据处理中常用于将迭代器或者生成器转化为列表:
gen = range(10)
gen
list(gen)
与元组类似,两个列表可以使用+号连接:
[4, None, "foo"] + [7, 8, (2, 3)]
可以用extend方法向原列表添加多个元素:
x = [4, None, "foo"]
x.extend([7, 8, (2, 3)])
x
在你需要构建一个大型列表时,使用extend将元素添加到已经存在的列表是更好的方式。
soxt有一些选项偶尔会派上用场。
其中一项是传递一个二级排序key一个用于生成排序值的函数。
例如,我们可以通过字符串的长度进行排序:
b = ["saw", "small", "He", "foxes", "six"]
b.sort(key=len)
b
bisect.bisect会找到元素应当被插入的位置,并保持序列排序;
bisect.insort将元素插入到相应位置:
import bisect
c = [1, 2, 2, 2, 3, 4, 7]
bisect.bisect(c,2)
bisect.bisect(c, 5)
bisect.insort(c, 6)
c
切片:
切片可以将序列赋值给变量:
seq[3:5] = [6, 3]
seq
# 将位置为3处的值换为6, 3
元素的数量是stop-start
步进值step可以在第二个冒号后面使用,意思是每隔多少个数取一个值:
seq[::2]
当需要对列表或元组进行翻转时,可将步进值设置为-1:
seq[::-1]
内建序列函数
enumerate
索引, 值
some_list = ['foo', 'bar', 'baz']
mapping = {}
for i,v in enumerate(some_list):
mapping[v] = i
mapping
sorted
sorted([7, 1, 2, 6, 0, 3, 2])
sorted('horse race')
zip
zip将列表、元组或其他序列的元素配对,新建一个元组构成的列表:
seq1 = ["foo", "bar", "baz"]
seq2 = ["one", "two", "three"]
zipped = zip(seq1, seq2)
list(zipped)
output:[('foo', 'one'), ('bar', 'two'), ('baz', 'three')]
zip可以处理任意长度的序列,它生成列表长度由最短的序列决定
zip的常用场景为同时遍历多个序列,有时候会和enumerate同时使用:
seq1 = ["foo", "bar", "baz"]
seq2 = ["one", "two", "three"]
for index, (a, b) in enumerate(zip(seq1, seq2)):
print(f"{index}: {a}, {b}")
output:
0: foo, one
1: bar, two
2: baz, three
将行的列表转换为列的列表:
pitchers = [('Nolan','Ryan'),('Roger','Clemens'),
('Schilling','Curt')]
first_names,last_names = zip(*pitchers)
first_names
output: ('Nolan', 'Roger', 'Schilling')
last_names
output: ('Ryan', 'Clemens', 'Curt')
reversed
reversed函数将序列的元素倒序排列:
list(reversed(range(10)))
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
字典
dict(字典)的常用的名字是 哈希表 或者 关联数组
可以使用update方法将两个字典合并:
d1 = {"a": "some value", "b": [1, 2, 3, 4]}
d1.update({"b": "foo", "c": 12})
d1
若有相同的键,则原有的值会被覆盖
从序列生成字典, 可向字典传递元组:
tuples = zip(range(5), reversed(range(5)))
list(tuples)
mapping = dict(tuples)
mapping
默认值
words = ["apple", "bat", "bar", "atom", "book"]
by_letter = {}
for word in words:
letter = word[0]
if letter not in by_letter:
by_letter[letter] = [word]
else:
by_letter[letter].append(word)
by_letter
可使用字典的setdefault方法替换:
words = ["apple", "bat", "bar", "atom", "book"]
by_letter = {}
for word in words:
letter = word[0]
by_letter.setdefault(letter, []).append(word)
by_letter
还可使用defaultdict类:
from collections import defaultdict
by_letter = defaultdict(list) # 传入类型
words = ["apple", "bat", "bar", "atom", "book"]
for word in words:
by_letter[word[0]].append(word)
有效的字典键类型
字典中的键必须是不可变的对象,比如标量类型(整数、浮点数、字符串)或元组(且元组内对象也必须是不可变对象);
通过hash函数可以检查一个对象是否可以哈希化(即是否可以用作字典的键):
为了将列表作为键,一种方式就是将其转换为元组
集合
集合是一种无序且元素唯一的容器。
set([2, 2, 2, 1, 3, 3])
{2, 2, 2, 1, 3, 3}
集合支持数学上的集合操作,例如联合、交集、差集、对称差集。
并集:
a.union(b)
a | b
交集:
a.intersection(b)
a & b
Python集合操作:
集合的元素必须是不可变的,如果想要包含列表型的元素,必须先转换为元组。
检查一个集合是否是另一个结合的子集(包含于)或超集(包含):
a_set = {1, 2, 3, 4, 5}
{1, 2, 3}.issubset(a_set)
a_set.issuperset({1, 2, 3})
当且仅当两个集合的内容一模一样时,两个集合才相等:
{1, 2, 3} == {3, 2, 1}
列表、集合和字典的推导式
给定一个字符串列表,我们可以过滤出长度大于2的,并且将字母改为大写:
strings = ["a", "as", "bat", "car", "dove", "python"]
[x.upper() for x in strings if len(x) > 2]
如果有一个字符串的列表,假设我们想要一个集合,集合里包含列表中字符串的长度,我可以通过集合推导式很方便地实现:
unique_lengths = {len(x) for x in strings}
unique_lengths
也可以使用map函数:
strings = ["a", "as", "bat", "car", "dove", "python"]
set(map(len, strings))
创建一个将字符串与其位置相匹配的字典:
strings = ["a", "as", "bat", "car", "dove", "python"]
loc_mapping = {value: index for index, value in enumerate(strings)}
loc_mapping
嵌套列表推导式
想要获得一个列表包含所有含有2个以上字母的名字:
for循环:
all_data = [["John", "Emily", "Michael", "Mary", "Steven"],
["Maria", "Juan", "Javier", "Natalia", "Pilar"]]
names_of_interest = []
for names in all_data:
enough_as = [name for name in names if name.count("a") >= 2]
names_of_interest.extend(enough_as)
names_of_interest
可使用嵌套列表推导式替换:
all_data = [["John", "Emily", "Michael", "Mary", "Steven"],
["Maria", "Juan", "Javier", "Natalia", "Pilar"]]
result = [name for names in all_data for name in names
if name.count("a") >= 2]
result
将含有整数元组的列表扁平化为一个简单的整数列表:
some_tuples = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
flattened = [x for tup in some_tuples for x in tup]
flattened
写开之后:
flattened = []
for tup in some_tuples:
for x in tup:
flattened.append(x)
创建了一个包含列表的列表:
some_tuples = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
[[x for x in tup] for tup in some_tuples]
函数
返回多个值
实质上是返回了一个元组,而元组之后又被拆包为多个结果变量:
def f():
a = 5
b = 6
c = 7
return a,b,c
a, b, c = f()
def f():
a = 5
b = 6
C = 7
return {'a':a, 'b':b, 'c':c}
函数是对象
去除空格,移除标点符号,调整适当的大小写:
states = [" Alabama ", "Georgia!", "Georgia", "georgia", "FlOrIda",
"south carolina##", "West virginia?"]
import re
def clean_strings(strings):
result = []
for value in strings:
value = value.strip()
value = re.sub("[!#?]", "", value)
value = value.title()
result.append(value)
return result
clean_strings(states)
将特定的列表操作应用到某个字符串的集合上:
states = [" Alabama ", "Georgia!", "Georgia", "georgia", "FlOrIda",
"south carolina##", "West virginia?"]
def remove_punctuation(value):
return re.sub("[!#?]", "", value)
clean_ops = [str.strip, remove_punctuation, str.title]
def clean_strings(strings, ops):
result = []
for value in strings:
for func in ops:
value = func(value)
result.append(value)
return result
clean_strings(states, clean_ops)
可将函数作为一个参数传给其他的函数,比如内建的map函数,它会将一个函数应用到一个序列上:
states = [" Alabama ", "Georgia!", "Georgia", "georgia", "FlOrIda",
"south carolina##", "West virginia?"]
def remove_punctuation(value):
return re.sub("[!#?]", "", value)
for x in map(remove_punctuation, states):
print(x)
匿名(Lambda)函数
def short_function(x):
return x * 2
equiv_anon = lambda x: x * 2
# 根据字符串中的不同字母个数进行排序
strings = ["foo", "card", "bar", "aaaa", "abab"]
strings.sort(key=lambda x: len(set(x)))
strings
柯里化:部分参数应用
def add_numbers(x,y):
return x +y
add_five = lambda y: add_numbers(5,y)
add_five(10)
# functools模块可以使用pratial函数简化这种处理:
def add_numbers(x,y):
return x +y
from functools import partial
add_five partial(add_numbers,5)
生成器
def squares(n=10):
print(f"Generating squares from 1 to {n ** 2}")
for i in range(1, n + 1):
yield i ** 2
gen = squares()
gen
for x in gen:
print(x, end=" ")
生成器表达式
gen = (x ** 2 for x in range(100))
gen
itertools模块
groupby可以根据任意的序列和一个函数,通过函数的返回值对序列中连续的元素进行分组:
import itertools
def first_letter(x):
return x[0]
names = ["Alan", "Adam", "Wes", "Will", "Albert", "Steven"]
for letter, names in itertools.groupby(names, first_letter):
print(letter, list(names)) # names is a generator
错误和异常处理
# 可以通过将多个异常类型写成元组的方式同时捕获多个异常(小括号是必不可少的):
def attempt_float(x):
try:
return float(x)
except (TypeError, ValueError):
return x
文件与操作系统
Python文件模式:
重要的Python文件方法或属性:
Python官方文档