(4, 5, 6)
本章深入探讨 Python 中对数据分析至关重要的基本构建块。我们将探索 Python 的内置数据结构,如何创建可重用的函数,以及如何与文件交互。
Note
虽然像 pandas 和 NumPy 这样的库为更大的数据集提供了高级功能,但它们的设计目的是与 Python 的核心数据操作工具协同工作。掌握这些基础知识至关重要!🛠️
Python 提供了几种通用的数据结构。我们将从以下几个开始:
理解这些是掌握 Python 的关键一步。🐍
元组是固定长度、不可变的 Python 对象序列。一旦创建,就不能更改其元素或大小。
不可变性
不可变性意味着内容在创建后不能更改。这确保了数据的完整性。可以把它想象成一个密封的容器📦——你可以看到里面的东西,但你不能交换东西。
使用逗号分隔的值创建元组,通常在括号中:
括号通常是可选的:
使用 tuple()
将序列/迭代器转换为元组:
my_list = [4, 0, 2] # 创建一个列表
my_tuple = tuple(my_list) # 将列表转换为元组
print(my_tuple) # 输出: (4, 0, 2)
string_tuple = tuple('string') # 将字符串转换为元组
print(string_tuple) # 输出: ('s', 't', 'r', 'i', 'n', 'g')
(4, 0, 2)
('s', 't', 'r', 'i', 'n', 'g')
使用 []
访问元素(从 0 开始索引):
元组可以包含其他元组:
虽然元组内的对象可能是可变的,但元组本身是不可变的:
tup = tuple(['foo', [1, 2], True]) # 创建一个包含列表的元组
# tup[2] = False # 这行代码会引发 TypeError,因为元组不可变!
# 但是,可以*在原处*修改可变元素:
tup[1].append(3) # 向列表 [1, 2] 中追加元素 3
print(tup) # 输出: ('foo', [1, 2, 3], True)
('foo', [1, 2, 3], True)
Caution
你不能将新对象分配给元组中的位置,但是你可以修改元组中可变对象的内容。
使用 +
连接:
tuple1 = (4, None, 'foo') # 创建元组 tuple1
tuple2 = (6, 0) # 创建元组 tuple2
tuple3 = ('bar',) # 创建单元素元组 tuple3(注意逗号)
combined_tuple = tuple1 + tuple2 + tuple3 # 连接三个元组
print(combined_tuple) # 输出: (4, None, 'foo', 6, 0, 'bar')
(4, None, 'foo', 6, 0, 'bar')
使用 *
重复:
repeated_tuple = ('foo', 'bar') * 4 # 将元组重复 4 次
print(repeated_tuple) # 输出: ('foo', 'bar', 'foo', 'bar', 'foo', 'bar', 'foo', 'bar')
('foo', 'bar', 'foo', 'bar', 'foo', 'bar', 'foo', 'bar')
Note
只会复制对象的引用,而不是对象本身。
将元组解包到变量中:
嵌套元组也可以:
优雅的变量交换:
*rest
捕获剩余的元素:
values = 1, 2, 3, 4, 5 # 创建一个元组
a, b, *rest = values # 将前两个元素赋值给 a 和 b,其余赋值给 rest
print(a) # 输出: 1
print(b) # 输出: 2
print(rest) # 输出: [3, 4, 5]
1
2
[3, 4, 5]
_
用于不需要的变量:
由于不可变性,元组的方法很少。count()
很有用:
列表是可变长度且可变的。你可以在创建后更改其内容和大小。
可变性
可变性意味着你可以在创建后更改元素、添加新元素或删除现有元素。列表非常灵活!🤸♀️
使用 []
或 list()
创建列表:
a_list = [2, 3, 7, None] # 使用方括号创建列表
tup = ('foo', 'bar', 'baz') # 创建一个元组
b_list = list(tup) # 将元组转换为列表
print(b_list) # 输出: ['foo', 'bar', 'baz']
['foo', 'bar', 'baz']
修改元素:
list()
实体化迭代器/生成器:
append()
: 添加到末尾。['foo', 'peekaboo', 'baz', 'dwarf']
insert()
: 在特定位置插入。b_list.insert(1, 'red') # 在索引 1 处插入 'red'
print(b_list) # 输出: ['foo', 'red', 'peekaboo', 'baz', 'dwarf']
['foo', 'red', 'peekaboo', 'baz', 'dwarf']
Caution
insert
比 append
开销更大(它会移动元素)。
pop()
: 删除并返回指定索引处的元素。removed_element = b_list.pop(2) # 删除索引 2 处的元素,并将其赋值给 removed_element
print(removed_element) # 输出: peekaboo
print(b_list) # 输出: ['foo', 'red', 'baz', 'dwarf']
peekaboo
['foo', 'red', 'baz', 'dwarf']
remove()
: 删除第一个出现的指定值。print('dwarf' in b_list) # 检查 'dwarf' 是否在列表中,输出: True
print('dwarf' not in b_list) # 检查 'dwarf' 是否不在列表中,输出: False
True
False
Note
对于列表,in
/ not in
速度很慢(线性扫描)。字典和集合快得多(哈希表,常数时间)。
使用 +
连接:
list1 = [4, None, 'foo'] # 创建列表 list1
list2 = [7, 8, (2, 3)] # 创建列表 list2
combined_list = list1 + list2 # 连接两个列表
print(combined_list) # 输出: [4, None, 'foo', 7, 8, (2, 3)]
[4, None, 'foo', 7, 8, (2, 3)]
extend()
追加多个元素:
x = [4, None, 'foo'] # 创建列表 x
x.extend([7, 8, (2, 3)]) # 使用 extend 方法追加多个元素
print(x) # 输出: [4, None, 'foo', 7, 8, (2, 3)]
[4, None, 'foo', 7, 8, (2, 3)]
Tip
extend()
通常比 +
快(不创建新列表)。
sort()
: 就地排序。key
参数: 提供自定义排序方法。使用 start:stop
选择部分:
start
: 默认为开头。stop
: 默认为结尾。使用步长选择每 n 个元素:
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
H | E | L | L | O | ! |
0 | 1 | 2 | 3 | 4 | 5 |
-6 | -5 | -4 | -3 | -2 | -1 |
LL
ELL
此图说明了在字符串“HELLO!”上的切片。索引显示在“格子边缘”,以帮助显示使用正索引或负索引时切片选择的开始和停止位置。
字典(或 dict
)至关重要。它们存储键值对(类似于哈希映射)。
键值对
每个键都与一个值相关联。键是唯一且不可变的(字符串、数字、元组)。值可以是任何东西。
使用 {}
和 :
:
del
:d1[5] = 'some value' # 添加键值对 5: 'some value'
d1['dummy'] = 'another value' # 添加键值对 'dummy': 'another value'
print(d1) # 输出: {'a': 'some value', 'b': [1, 2, 3, 4], 7: 'an integer', 5: 'some value', 'dummy': 'another value'}
del d1[5] # 删除键 5 及其对应的值
print(d1) # 输出: {'a': 'some value', 'b': [1, 2, 3, 4], 7: 'an integer', 'dummy': 'another value'}
{'a': 'some value', 'b': [1, 2, 3, 4], 7: 'an integer', 5: 'some value', 'dummy': 'another value'}
{'a': 'some value', 'b': [1, 2, 3, 4], 7: 'an integer', 'dummy': 'another value'}
pop()
: 删除并返回。keys()
: 键的迭代器。values()
: 值的迭代器。items()
: 键值对的迭代器。print(list(d1.keys())) # 获取字典中所有键的列表,输出: ['a', 'b', 7]
print(list(d1.values())) # 获取字典中所有值的列表,输出: ['some value', [1, 2, 3, 4], 'an integer']
print(list(d1.items())) # 获取字典中所有键值对的列表,输出: [('a', 'some value'), ('b', [1, 2, 3, 4]), (7, 'an integer')]
['a', 'b', 7]
['some value', [1, 2, 3, 4], 'an integer']
[('a', 'some value'), ('b', [1, 2, 3, 4]), (7, 'an integer')]
Note
键的顺序取决于插入顺序。keys
和 values
以相同的顺序返回迭代器。
update()
合并字典:
d1.update({'b': 'foo', 'c': 12}) # 更新键 'b' 的值,并添加键值对 'c': 12
print(d1) # 输出: {'a': 'some value', 'b': 'foo', 7: 'an integer', 'c': 12}
{'a': 'some value', 'b': 'foo', 7: 'an integer', 'c': 12}
Caution
update()
是就地操作。现有键的值会被丢弃。
key_list = ['a', 'b', 'c'] # 创建一个键列表
value_list = [1, 2, 3] # 创建一个值列表
mapping = {} # 创建一个空字典
for key, value in zip(key_list, value_list): # 使用 zip 将键列表和值列表配对
mapping[key] = value # 将键值对添加到字典中
print(mapping) # 输出: {'a': 1, 'b': 2, 'c': 3}
# 简洁写法:dict() 和 zip()
mapping = dict(zip(range(5), reversed(range(5)))) # 使用 zip 和 reversed 创建字典
print(mapping) # 输出: {0: 4, 1: 3, 2: 2, 3: 1, 4: 0}
{'a': 1, 'b': 2, 'c': 3}
{0: 4, 1: 3, 2: 2, 3: 1, 4: 0}
zip
函数可以将多个序列的元素配对。dict
接受一个由 2 元组组成的列表。
如果键不存在,get
返回 None
(或指定的默认值),而 pop
会引发异常。
words = ['apple', 'bat', 'bar', 'atom', 'book'] # 创建一个字符串列表
by_letter = {} # 创建一个空字典
for word in words: # 遍历列表中的每个单词
letter = word[0] # 获取单词的首字母
by_letter.setdefault(letter, []).append(word) # 使用 setdefault 设置默认值,并追加单词
print(by_letter) # 输出: {'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book']}
{'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book']}
setdefault(key, default)
: 如果 key
存在,则返回其值。如果不存在,则插入 key
并设置值为 default
,然后返回 default
。
collections.defaultdict
简化了初始化:
from collections import defaultdict # 导入 defaultdict
by_letter = defaultdict(list) # 创建一个 defaultdict,默认值为列表
for word in words: # 遍历列表中的每个单词
by_letter[word[0]].append(word) # 自动创建列表并追加单词
print(by_letter) # 输出: defaultdict(<class 'list'>, {'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book']})
defaultdict(<class 'list'>, {'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book']})
hash()
检查:print(hash('string')) # 字符串可以哈希,输出一个哈希值
print(hash((1, 2, (2, 3)))) # 元组可以哈希,输出一个哈希值
# print(hash((1, 2, [2, 3]))) # 这行代码会引发 TypeError: unhashable type: 'list',因为列表不可哈希
# 列表作为键?转换为元组:
d = {} # 创建一个空字典
d[tuple([1, 2, 3])] = 5 # 将列表转换为元组作为键
print(d) # 输出: {(1, 2, 3): 5}
822069171903840025
-9209053662355515447
{(1, 2, 3): 5}
集合是无序的唯一元素集合(类似于只有键的字典)。
a = {1, 2, 3, 4, 5} # 创建集合 a
b = {3, 4, 5, 6, 7, 8} # 创建集合 b
# 并集 (| 或 union())
print(a.union(b)) # 输出: {1, 2, 3, 4, 5, 6, 7, 8}
print(a | b) # 输出: {1, 2, 3, 4, 5, 6, 7, 8}
# 交集 (& 或 intersection())
print(a.intersection(b)) # 输出: {3, 4, 5}
print(a & b) # 输出: {3, 4, 5}
# 差集 (- 或 difference())
print(a.difference(b)) # 输出: {1, 2}
print(a - b) #输出: {1, 2}
{1, 2, 3, 4, 5, 6, 7, 8}
{1, 2, 3, 4, 5, 6, 7, 8}
{3, 4, 5}
{3, 4, 5}
{1, 2}
{1, 2}
函数 | 替代语法 | 描述 |
---|---|---|
a.add(x) |
N/A | 将元素 x 添加到集合 a |
a.clear() |
N/A | 将集合 a 重置为空集,丢弃所有元素 |
a.remove(x) |
N/A | 从集合 a 中移除元素 x |
a.pop() |
N/A | 从集合 a 中移除并返回一个任意元素,如果集合为空则引发 KeyError |
a.union(b) |
a | b |
a 和 b 中的所有唯一元素 |
函数 | 替代语法 | 描述 |
---|---|---|
a.update(b) |
a |= b |
将 a 的内容设置为 a 和 b 中元素的并集 |
a.intersection(b) |
a & b |
a 和 b 中的所有元素 |
a.intersection_update(b) |
a &= b |
将 a 的内容设置为 a 和 b 中元素的交集 |
a.difference(b) |
a - b |
a 中不在 b 中的元素 |
a.difference_update(b) |
a -= b |
将 a 设置为 a 中不在 b 中的元素 |
函数 | 替代语法 | 描述 |
---|---|---|
a.symmetric_difference(b) |
a ^ b |
a 或 b 中的所有元素,但不同时在两者中 |
a.symmetric_difference_update(b) |
a ^= b |
将 a 设置为包含 a 或 b 中的元素,但不同时在两者中 |
a.issubset(b) |
<= |
如果 a 的所有元素都包含在 b 中,则为 True |
a.issuperset(b) |
>= |
如果 b 的所有元素都包含在 a 中,则为 True |
a.isdisjoint(b) |
N/A | 如果 a 和 b 没有共同元素,则为True |
存在就地版本(例如,a |= b
):
c = a.copy() # 创建 a 的副本
c |= b # 就地并集
print(c) # 输出: {1, 2, 3, 4, 5, 6, 7, 8}
d = a.copy() # 创建 a 的副本
d &= b # 就地交集
print(d) # 输出: {3, 4, 5}
{1, 2, 3, 4, 5, 6, 7, 8}
{3, 4, 5}
Tip
对于大型集合,就地运算更有效。
与字典键一样,集合元素必须是不可变且可哈希的。
如果内容相等,则集合相等:
enumerate
在迭代期间跟踪索引:
collection = ['foo', 'bar', 'baz'] # 创建一个列表
# 不使用 enumerate 的写法:
i = 0 # 初始化索引
for value in collection: # 遍历列表
print(f"Index: {i}, Value: {value}") # 打印索引和值
i += 1 # 索引递增
# 使用 enumerate:
for i, value in enumerate(collection): # 同时获取索引和值
print(f"Index: {i}, Value: {value}") # 打印索引和值
Index: 0, Value: foo
Index: 1, Value: bar
Index: 2, Value: baz
Index: 0, Value: foo
Index: 1, Value: bar
Index: 2, Value: baz
enumerate
返回 (index, value)
元组。
sorted
返回一个新的排序列表:
print(sorted([7, 1, 2, 6, 0, 3, 2])) # 对列表进行排序,输出: [0, 1, 2, 2, 3, 6, 7]
print(sorted('horse race')) # 对字符串进行排序,输出: [' ', 'a', 'c', 'e', 'e', 'h', 'o', 'r', 'r', 's']
[0, 1, 2, 2, 3, 6, 7]
[' ', 'a', 'c', 'e', 'e', 'h', 'o', 'r', 'r', 's']
Note
sorted()
返回一个新列表。list.sort()
就地排序。
zip
将元素“配对”:
seq1 = ['foo', 'bar', 'baz'] # 创建列表 seq1
seq2 = ['one', 'two', 'three'] # 创建列表 seq2
zipped = zip(seq1, seq2) # 将 seq1 和 seq2 配对
print(list(zipped)) # 输出: [('foo', 'one'), ('bar', 'two'), ('baz', 'three')]
[('foo', 'one'), ('bar', 'two'), ('baz', 'three')]
zip
可以接受任意数量的序列。输出长度由最短的序列决定:
使用 enumerate
迭代多个序列:
reversed
以相反的顺序迭代:
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Note
reversed
是一个生成器(在实体化之前不会创建反向序列)。
推导式可以简洁地创建新集合。
示例:
# {expr for val in collection if condition}
strings = ['a', 'as', 'bat', 'car', 'dove', 'python'] # 创建一个字符串列表
unique_lengths = {len(x) for x in strings} # 使用集合推导式获取列表中字符串的唯一长度
print(unique_lengths) # 输出: {1, 2, 3, 4, 6}
# 使用 map 函数
print(set(map(len, strings))) # 使用map函数和set达到同样效果
{1, 2, 3, 4, 6}
{1, 2, 3, 4, 6}
类似于列表推导式,但使用 {}
(创建一个集合)。
# {key_expr: value_expr for val in collection if condition}
strings = ['a', 'as', 'bat', 'car', 'dove', 'python'] # 创建一个字符串列表
# 使用字典推导式创建一个字典,其中键是字符串,值是字符串在列表中的索引
loc_mapping = {val: index for index, val in enumerate(strings)}
print(loc_mapping) # 输出: {'a': 0, 'as': 1, 'bat': 2, 'car': 3, 'dove': 4, 'python': 5}
{'a': 0, 'as': 1, 'bat': 2, 'car': 3, 'dove': 4, 'python': 5}
all_data = [['John', 'Emily', 'Michael', 'Mary', 'Steven'],
['Maria', 'Juan', 'Javier', 'Natalia', 'Pilar']]
# 使用嵌套列表推导式找出名字中包含2个以上a的名字。
names_of_interest = [name for names in all_data for name in names
if name.count('a') >= 2]
print(names_of_interest) # 输出: ['Maria', 'Natalia']
some_tuples = [(1, 2, 3), (4, 5, 6), (7, 8, 9)] # 创建一个元组列表
flattened = [x for tup in some_tuples for x in tup] # 使用嵌套列表推导式展开元组
print(flattened) # 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 列表推导式中的列表推导式
flattened = [[x for x in tup] for tup in some_tuples] # 两层列表推导
print(flattened) # 输出: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
['Maria', 'Natalia']
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Note
for
的顺序与嵌套 for
循环中的顺序相同。
函数组织和重用代码。
def my_function(x, y): # 定义一个函数,名为 my_function,接受两个参数 x 和 y
"""Docstring: 解释函数的功能。""" # 函数的文档字符串
return x + y # 返回 x 和 y 的和
result = my_function(1, 2) # 调用函数,并将返回值赋给 result
print(result) # 输出: 3
print(my_function.__doc__) # 访问文档字符串, 输出: Docstring: 解释函数的功能。
3
Docstring: 解释函数的功能。
return
返回一个值。return
语句,则隐式返回 None
。def my_function2(x, y, z=1.5): # 定义函数,z 是关键字参数,默认值为 1.5
if z > 1: # 如果 z 大于 1
return z * (x + y) # 返回 z 乘以 x 和 y 的和
else: # 否则
return z / (x + y) # 返回 z 除以 x 和 y 的和
print(my_function2(5, 6, z=0.7)) # 使用关键字参数调用函数,输出: 0.06363636363636363
print(my_function2(3.14, 7, 3.5)) # 使用位置参数调用函数,输出: 35.49
print(my_function2(10, 20)) # 使用默认参数调用函数,输出: 45.0
0.06363636363636363
35.49
45.0
global
与 nonlocal
。可以访问封闭作用域,但要修改它们,请使用 global
或 nonlocal
。
a = [] # 全局变量
def func2(): # 定义一个函数
for i in range(5): # 循环 5 次
a.append(i) # 修改全局变量 'a'
func2() # 调用函数
print(a) # 输出: [0, 1, 2, 3, 4]
b = None # 全局变量
def bind_b_variable(): # 定义一个函数
global b # 声明 'b' 为全局变量
b = [] # 将空列表赋值给全局变量 'b'
bind_b_variable() # 调用函数
print(b) # 输出: []
[0, 1, 2, 3, 4]
[]
Caution
尽量减少 global
的使用。更好的设计可以减少对全局状态的依赖。
def f(): # 定义一个函数
a = 5 # 局部变量
b = 6 # 局部变量
c = 7 # 局部变量
return a, b, c # 返回一个元组
x, y, z = f() # 调用函数,并将返回的元组解包到变量 x, y, z 中
print(x, y, z) # 输出: 5 6 7
# 或者返回一个字典
def f2():
a = 5
b = 6
c = 7
return {'a' : a, 'b' : b, 'c' : c} # 返回值改为字典
result = f2()
print(result) # 输出: {'a': 5, 'b': 6, 'c': 7}
5 6 7
{'a': 5, 'b': 6, 'c': 7}
函数是一等公民:
import re # 导入正则表达式模块
states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda',
'south carolina##', 'West virginia?'] # 列表
def clean_strings(strings): # 定义一个函数,用于清理字符串列表
result = [] # 创建一个空列表
for value in strings: # 遍历列表中的每个字符串
value = value.strip() # 去除字符串两端的空格
value = re.sub('[!#?]', '', value) # 使用正则表达式去除标点符号
value = value.title() # 将字符串转换为标题格式
result.append(value) # 将处理后的字符串添加到列表中
return result # 返回处理后的列表
print(clean_strings(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_functional(strings, ops): # 定义一个函数,接受字符串列表和操作列表
result = [] # 创建一个空列表
for value in strings: # 遍历字符串列表中的每个字符串
for function in ops: # 遍历操作列表中的每个函数
value = function(value) # 对字符串应用函数
result.append(value) # 将处理后的字符串添加到列表中
return result # 返回处理后的列表
print(clean_strings_functional(states, clean_ops)) #输出: ['Alabama', 'Georgia', 'Georgia', 'Georgia', 'Florida', 'South Carolina', 'West Virginia']
# 将函数与 map 结合使用:
for x in map(remove_punctuation, states): # 使用 map 函数对列表中的每个字符串应用 remove_punctuation 函数
print(x)
['Alabama', 'Georgia', 'Georgia', 'Georgia', 'Florida', 'South Carolina', 'West Virginia']
['Alabama', 'Georgia', 'Georgia', 'Georgia', 'Florida', 'South Carolina', 'West Virginia']
Alabama
Georgia
Georgia
georgia
FlOrIda
south carolina
West virginia
这种方式很灵活,而且可以重用。
# 使用 `lambda` 定义小型匿名函数:
# 等效于:
# def short_function(x):
# return x * 2
equiv_anon = lambda x: x * 2 # 定义一个 lambda 函数,接受一个参数 x,返回 x * 2
print(equiv_anon(4)) # 输出: 8
8
用于传递短函数很方便:
def apply_to_list(some_list, f): # 定义一个函数,接受一个列表和一个函数作为参数
return [f(x) for x in some_list] # 对列表中的每个元素应用函数 f,并返回结果列表
ints = [4, 0, 1, 5, 6] # 创建一个整数列表
result = apply_to_list(ints, lambda x: x * 2) # 将 lambda 函数作为参数传递
print(result) # 输出: [8, 0, 2, 10, 12]
strings = ['foo', 'card', 'bar', 'aaaa', 'abab'] # 创建一个字符串列表
strings.sort(key=lambda x: len(set(x))) # 使用 lambda 函数作为 key,按照字符串中不同字符的数量排序
print(strings) # 输出: ['aaaa', 'foo', 'abab', 'bar', 'card']
[8, 0, 2, 10, 12]
['aaaa', 'foo', 'abab', 'bar', 'card']
生成器按需生成值(节省内存)。
def squares(n=10): # 定义一个生成器函数,生成 1 到 n^2 的平方数
print('Generating squares from 1 to %d' % n ** 2) # 打印一条消息
for i in range(1, n + 1): # 循环 1 到 n
yield i ** 2 # 使用 yield 产生 i 的平方
gen = squares() # 创建一个生成器对象
print(gen) # 输出: <generator object squares at 0x...> (生成器对象)
for x in gen: # 请求值
print(x, end=' ') # 输出: 1 4 9 16 25 36 49 64 81 100
<generator object squares at 0x7f187ff5f300>
Generating squares from 1 to 100
1 4 9 16 25 36 49 64 81 100
yield
而不是 return
。简洁的生成器(类似于推导式):
# 列表推导式:[x ** 2 for x in range(100)]
gen = (x ** 2 for x in range(100)) # 使用圆括号创建生成器表达式
print(gen) # 输出: <generator object <genexpr> at 0x...> (生成器对象)
print(sum(gen)) # 输出: 328350
# 作为函数参数:
print(sum(x ** 2 for x in range(100))) # 将生成器表达式作为参数传递给 sum 函数,输出: 328350
print(dict((i, i **2) for i in range(5))) # 将生成器表达式作为参数传递给 dict 函数,输出: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
<generator object <genexpr> at 0x7f18a4c57510>
328350
328350
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
itertools
提供了有用的生成器:
import itertools # 导入 itertools 模块
def first_letter(x): # 定义一个函数,返回字符串的第一个字母
return x[0]
names = ['Alan', 'Adam', 'Wes', 'Will', 'Albert', 'Steven'] # 创建一个名字列表
for letter, names_iter in itertools.groupby(names, first_letter): # 使用 itertools.groupby 按首字母分组
print(letter, list(names_iter)) # 输出每组的首字母和名字列表
A ['Alan', 'Adam']
W ['Wes', 'Will']
A ['Albert']
S ['Steven']
itertools.groupby
对连续的元素进行分组。
函数 | 描述 |
---|---|
chain(*iterables) |
链接迭代器。 |
combinations(iterable, k) |
生成长度为 k 的组合,忽略顺序,不放回。 |
permutations(iterable, k) |
生成长度为 k 的排列,考虑顺序。 |
groupby(iterable[, keyfunc]) |
为每个唯一键生成 (key, sub-iterator)。 |
product(*iterables, repeat=1) |
生成笛卡尔积(类似于嵌套 for 循环)。 |
处理错误对于健壮的代码至关重要。
def attempt_float(x): # 定义一个函数,尝试将输入转换为浮点数
try: # 尝试执行以下代码块
return float(x) # 将 x 转换为浮点数并返回
except: # 如果发生任何异常,则执行以下代码块
return x # 返回原始输入
print(attempt_float('1.2345')) # 输出: 1.2345
print(attempt_float('something')) # 输出: something
1.2345
something
try
: 可能引发异常的代码。except
: 如果发生异常,则执行。指定异常类型(首选)。
使用元组捕获多种类型的异常。
finally
: 始终执行(清理)。else
: 如果 try
块没有引发异常,则执行。IPython 提供了有用的回溯信息。使用 %xmode
控制详细程度(Plain、Context、Verbose)。
path = 'examples/segismundo.txt' # 设置文件路径(请确保此文件存在)
# 默认编码因平台而异,最好明确指定。
f = open(path, encoding='utf-8') # 以读取模式打开文件('r' 是默认模式),指定编码为 utf-8
for line in f: # 逐行读取文件
print(line.rstrip()) # 移除每行末尾的空白字符并打印
f.close() # 关闭文件
Sueña el rico en su riqueza,
que más cuidados le ofrece;
sueña el pobre que padece
su miseria y su pobreza;
sueña el que a medrar empieza,
sueña el que afana y pretende,
sueña el que agravia y ofende,
y en el mundo, en conclusión,
todos sueñan lo que son,
aunque ninguno lo entiende.
open(path, mode='r', encoding=None)
path
: 文件路径。mode
: ‘r’ (读取), ‘w’ (写入), ‘a’ (追加), ‘x’ (创建), ‘rb’, ‘wb’ 等。encoding
: 文件编码 (例如, ‘utf-8’)。f.close()
。# 读取所有行
f = open(path, encoding="utf-8") # 以读取模式打开文件,指定编码为 utf-8
lines = [x.rstrip() for x in f] # 读取所有行,移除每行末尾的空白字符,并存储在列表中
print(lines) # 打印所有行
f.close() # 关闭文件
['Sueña el rico en su riqueza,', 'que más cuidados le ofrece;', '', 'sueña el pobre que padece', 'su miseria y su pobreza;', '', 'sueña el que a medrar empieza,', 'sueña el que afana y pretende,', 'sueña el que agravia y ofende,', '', 'y en el mundo, en conclusión,', 'todos sueñan lo que son,', 'aunque ninguno lo entiende.', '']
with
语句会自动关闭文件:
with open(path, encoding="utf-8") as f: # 使用 with 语句打开文件,指定编码为 utf-8
lines = [x.rstrip() for x in f] # 读取所有行,移除每行末尾的空白字符,并存储在列表中
print(lines) # 打印所有行
# 'f' 在这里自动关闭
['Sueña el rico en su riqueza,', 'que más cuidados le ofrece;', '', 'sueña el pobre que padece', 'su miseria y su pobreza;', '', 'sueña el que a medrar empieza,', 'sueña el que afana y pretende,', 'sueña el que agravia y ofende,', '', 'y en el mundo, en conclusión,', 'todos sueñan lo que son,', 'aunque ninguno lo entiende.', '']
Tip
使用 with
!即使出现错误,它也能确保清理。
模式 | 描述 |
---|---|
r | 只读 |
w | 只写;创建新文件(擦除现有文件) |
x | 只写;创建新文件 |
a | 追加到现有文件(如果需要,创建新文件) |
r+ | 读写 |
b | 二进制模式(添加到模式:‘rb’, ‘wb’) |
t | 文本模式(解码字节);默认 |
f1 = open(path, encoding = "utf-8") # 以文本模式打开文件,指定编码为 utf-8
print(f1.read(10)) # 读取 10 个*字符*
f2 = open(path, mode='rb') # 以二进制模式打开文件
print(f2.read(10)) # 读取 10 个*字节*
print(f1.tell()) # 获取当前位置(字符)
print(f2.tell()) # 获取当前位置(字节)
import sys
print(sys.getdefaultencoding()) # 获取默认编码
f1.seek(3) # 移动到第 3 个字节/字符
print(f1.read(1)) # 读取 1 个字符
f1.close() # 关闭文件
f2.close() # 关闭文件
Sueña el r
b'Sue\xc3\xb1a el '
11
10
utf-8
ñ
read(n)
: 读取 n
个字符/字节。tell()
: 当前位置。seek(position)
: 移动指针。seek
和 UTF-8(多字节字符)。with open('tmp.txt', 'w', encoding = "utf-8") as handle: # 以写入模式打开一个临时文件,指定编码为 utf-8
# 将 segismundo.txt 文件中长度大于 1 的行写入 tmp.txt
handle.writelines(x for x in open(path, encoding = "utf-8") if len(x) > 1)
with open('tmp.txt', encoding = "utf-8") as f: # 以读取模式打开临时文件,指定编码为 utf-8
lines = f.readlines() # 读取所有行
print(lines) # 打印所有行
import os
os.remove("tmp.txt") # 删除临时文件
['Sueña el rico en su riqueza,\n', 'que más cuidados le ofrece;\n', 'sueña el pobre que padece\n', 'su miseria y su pobreza;\n', 'sueña el que a medrar empieza,\n', 'sueña el que afana y pretende,\n', 'sueña el que agravia y ofende,\n', 'y en el mundo, en conclusión,\n', 'todos sueñan lo que son,\n', 'aunque ninguno lo entiende.\n']
write(string)
: 写入字符串。writelines(list_of_strings)
: 写入字符串列表。方法/属性 | 描述 |
---|---|
read([size]) |
返回数据(字节/字符串)。 |
readable() |
如果可读,则返回 True 。 |
readlines([size]) |
返回行的列表。 |
write(string) |
写入字符串。 |
writable() |
如果可写,则返回 True 。 |
writelines(strings) |
写入字符串序列。 |
close() |
关闭文件。 |
flush() |
将缓冲区刷新到磁盘。 |
seek(pos) |
移动到指定位置。 |
seekable() |
如果可寻址,则返回 True 。 |
tell() |
返回当前位置。 |
closed |
如果已关闭,则返回 True 。 |
encoding |
返回文件编码 (例如, UTF-8)。 |
mode='rb'
/'wb'
: 二进制模式(字节)。with open(path, encoding = "utf-8") as f: # 以文本模式打开文件
chars = f.read(10) # 读取前10个字符
print(len(chars)) #输出字符数
with open(path, 'rb') as f: # 以二进制模式打开文件
data = f.read(10) # 读取前10个字节
print(data)
print(data.decode('utf-8')) # 将字节解码为字符串
# print(data[:4].decode('utf-8')) # 这行代码可能会引发 UnicodeDecodeError(不完整的多字节字符)
10
b'Sue\xc3\xb1a el '
Sueña el
read(n)
个字符 ≠ read(n)
个字节。read
返回确切的字节。sink_path = 'sink.txt' # 设置一个用于演示的文件名
with open(path, encoding = "utf-8") as source: # 以utf-8编码读取
with open(sink_path, 'x', encoding='iso-8859-1') as sink: # 以iso-8859-1编码写入
sink.write(source.read())
with open(sink_path, encoding='iso-8859-1') as f: # 以iso-8859-1编码读取
print(f.read(10))
import os
os.remove(sink_path) # 删除临时文件
# 注意:
f = open(path, encoding = "utf-8") # 以utf-8编码读取
print(f.read(5))
f.seek(4) # 移动到第4个字节位置
# print(f.read(1)) # 这行代码可能会引发 UnicodeDecodeError,因为可能在多字节字符的中间
f.close()
Sueña el r
Sueña
Caution
在非二进制模式下使用 seek
时要非常小心。
enumerate
、sorted
、zip
、reversed
。itertools
:有用的迭代器工具。try
、except
、finally
、else
。这些是 Python 数据分析的基础!🧱
with
语句: 文件处理的优势?