学习日志20 python
1 序列(Sequence)
在 Python 中,序列(Sequence)是一种基本的数据结构类别,指的是有序的元素集合。序列中的每个元素都可以通过索引(位置编号)来访问。Python 提供了多种内置的序列类型,包括最常用的 列表(List)、元组(Tuple)、字符串(String),以及不太常见的 范围(range)、字节序列(bytes、bytearray) 等。
序列的共同特点
- 有序性:元素按照特定顺序排列,可以通过索引访问。索引从
0
开始,也支持负数索引(如-1
表示最后一个元素)。 - 可迭代:可以使用
for
循环遍历元素。 - 支持切片:可以通过切片操作(如
seq[start:stop:step]
)获取子序列。 - 通用操作:支持
len()
获取长度、in
判断元素是否存在、+
拼接序列等。
常见序列类型
1. 列表(List)
- 特点:可变(可修改)、用方括号
[]
定义。
python
运行
fruits = ["apple", "banana", "cherry"]
fruits[0] = "grape" # 修改元素
print(fruits[1]) # 输出: banana
2. 元组(Tuple)
- 特点:不可变(创建后不能修改)、用圆括号
()
定义。
python
运行
point = (3, 4)
print(point[0]) # 输出: 3
# point[0] = 5 # 错误:元组不可变
3. 字符串(String)
- 特点:不可变的字符序列,用引号定义。
python
运行
s = "hello"
print(s[1]) # 输出: e
# s[1] = 'a' # 错误:字符串不可变
序列的常用操作
以下是所有序列类型都支持的操作:
操作 | 描述 |
---|---|
seq[i] | 获取索引 i 处的元素。 |
seq[start:stop] | 获取从 start 到 stop-1 的切片。 |
len(seq) | 获取序列的长度。 |
x in seq | 判断元素 x 是否存在于序列中。 |
seq1 + seq2 | 拼接两个序列(生成新序列,元组和字符串需类型相同)。 |
seq * n | 重复序列 n 次(生成新序列)。 |
min(seq) / max(seq) | 返回序列中的最小 / 最大值(元素需可比较)。 |
sorted(seq) | 返回排序后的新序列(不修改原序列)。 |
序列的优势
- 统一的接口:多种类型共享相同的操作方法,降低学习成本。
- 高效处理:底层优化使得序列操作非常快速。
- 灵活应用:适合各种场景,如数据存储、循环遍历、字符串处理等。
总结
序列是 Python 中最基础且重要的数据结构之一,掌握列表、元组和字符串等序列类型的使用,是编写高效 Python 代码的关键。如果需要存储可变数据且经常修改,推荐使用列表;如果数据不可变或需要作为字典键,使用元组;处理文本时则使用字符串。
2 在 Python 中,不是所有特殊方法都会自动生成
题目
根据以下程序,下列选项中,说法正确的是()class Foo():def __init__(self):passdef __getitem__(self,pos):return range(0,30,10)[pos]foo = Foo()A foo对象表现得像个序列B 可以使用len(foo)来查看对象foo的元素个数C 可以使用for i in foo:print(i)来遍历foo的元素D 不能使用foo[0]来访问对象foo的第一个元素正确答案:C
在 Python 中,不是所有特殊方法都会自动生成,这取决于类的定义方式。让我通过例子详细解释:
1. __len__
必须显式定义
Python 不会为类自动生成 __len__
方法。如果类需要支持 len(obj)
,必须自己实现:
python
运行
class MyList:def __init__(self, data):self.data = data# 必须定义 __len__ 才能使用 len()def __len__(self):return len(self.data)obj = MyList([1, 2, 3])
print(len(obj)) # 输出: 3
如果没有实现 __len__
,调用 len(obj)
会报错:
python
运行
class BadList:def __init__(self, data):self.data = dataobj = BadList([1, 2, 3])
print(len(obj)) # 报错: TypeError: object of type 'BadList' has no len()
2. __getitem__
的作用
实现 __getitem__
后,类实例可以像序列一样使用下标访问(如 obj[0]
),但仍不是完整的序列:
python
运行
class Foo:def __getitem__(self, pos):return [0, 10, 20][pos] # 返回固定序列foo = Foo()
print(foo[0]) # 输出: 0
print(foo[1]) # 输出: 10
虽然能下标访问,但 Foo
不是序列,因为缺少 __len__
:
python
运行
print(len(foo)) # 报错: TypeError
3. for
循环的隐式调用
Python 的 for
循环更灵活:
- 如果对象有
__iter__
,优先调用它(如列表、字典)。 - 如果没有
__iter__
,尝试用__getitem__
从 0 开始依次读取元素,直到出现IndexError
。
例子:
python
运行
class Foo:def __getitem__(self, pos):if pos < 3: # 限制范围return pos * 10raise IndexError # 超出范围时触发结束循环foo = Foo()
for i in foo:print(i) # 输出: 0, 10, 20
4. 哪些方法会自动生成?
Python 不会自动生成任何特殊方法,但:
- 所有类默认继承
object
,它有一些基础实现(如__str__
返回类名和内存地址)。 - 容器类(如
list
、dict
)已经实现了全套特殊方法。
回到原题:
选项解析
A. foo 对象表现得像个序列
要成为序列,对象需要同时支持__getitem__
和__len__
方法。但在本题中,类Foo
并没有定义__len__
方法,所以它不符合序列的完整定义。因此,该选项错误。
B. 可以使用 len (foo) 来查看对象 foo 的元素个数
由于类Foo
没有实现__len__
方法,一旦调用len(foo)
,程序就会抛出TypeError
异常。所以,该选项错误。
C. 可以使用 for i in foo:print (i) 来遍历 foo 的元素
在 Python 中,当对象没有定义__iter__
方法时,for
循环会借助__getitem__
方法,从索引 0 开始依次尝试获取元素,直至出现IndexError
异常。对于Foo
类的实例,__getitem__
方法能够正常返回range(0, 30, 10)
中的元素,所以可以通过for
循环进行遍历。因此,该选项正确。
D. 不能使用 foo [0] 来访问对象 foo 的第一个元素
Foo
类已经实现了__getitem__
方法,所以完全可以使用foo[0]
来访问元素,并且会返回 0。所以,该选项错误。
总结
正确答案是 C。本题的关键在于理解__getitem__
方法赋予对象的能力,以及for
循环在对象没有实现__iter__
方法时的 fallback 机制。在解答这类题目时,要仔细查看类中实现了哪些特殊方法,以及这些方法对应的功能。
建议
遇到这类题目时:
- 检查类定义:看实现了哪些特殊方法(如
__getitem__
、__len__
)。 - 理解方法作用:
__getitem__
→ 支持下标访问和for
循环。__len__
→ 支持len()
。
- 注意隐含规则:
for
循环的 fallback 机制是关键。
通过多分析例子,你会逐渐熟悉这些规则!
3 在 Python 中,所有标准对象都可以用于布尔测试
在 Python 中,所有标准对象确实都可以用于布尔测试,字典也不例外。布尔测试的核心是判断对象为 “真”(True
)或 “假”(False
),具体规则如下:
哪些情况对象会被视为 “假”?
对于标准对象,以下情况在布尔测试中返回 False
:
- 常量:
None
、False
- 数值:
0
(整数)、0.0
(浮点数)、0j
(复数) - 空序列 / 集合:空字符串(
""
)、空列表([]
)、空元组(()
)、空字典({}
)、空集合(set()
)、空范围(range(0)
) - 其他:实现了
__bool__()
方法且返回False
,或实现了__len__()
方法且返回0
的对象
字典的布尔测试规则
字典的布尔测试遵循 “空则为假,非空则为真”:
- 空字典(
{}
)→ 布尔值为False
- 非空字典(如
{"a": 1}
)→ 布尔值为True
python
运行
empty_dict = {}
non_empty_dict = {"name": "Python"}print(bool(empty_dict)) # 输出: False
print(bool(non_empty_dict)) # 输出: True
总结
Python 中所有标准对象(包括字典)都能参与布尔测试,其结果由对象的 “空值” 或 “非空值” 特性决定。字典作为常用的标准对象,空字典被视为 False
,非空字典被视为 True
,这一规则在条件判断(如 if
语句)中经常用到。
4 在list中添加新元素的方法
在 Python 中,向列表(list
)添加新元素有多种方法,每种方法适用于不同的场景。以下是最常用的几种方式及其区别:
1. append()
:在末尾添加单个元素
- 语法:
list.append(element)
- 特点:将元素直接添加到列表末尾,原地修改列表,返回值为
None
。
python
运行
fruits = ["apple", "banana"]
fruits.append("cherry")
print(fruits) # 输出: ['apple', 'banana', 'cherry']
2. extend()
:在末尾添加多个元素(可迭代对象)
- 语法:
list.extend(iterable)
- 特点:将可迭代对象(如列表、元组、字符串)的元素逐个展开并添加到列表末尾,原地修改列表。
python
运行
fruits = ["apple", "banana"]
fruits.extend(["cherry", "date"]) # 添加列表
print(fruits) # 输出: ['apple', 'banana', 'cherry', 'date']fruits.extend(("elderberry", "fig")) # 添加元组
print(fruits) # 输出: ['apple', 'banana', 'cherry', 'date', 'elderberry', 'fig']fruits.extend("grape") # 添加字符串(会拆分为字符)
print(fruits) # 输出: ['apple', 'banana', 'cherry', 'date', 'elderberry', 'fig', 'g', 'r', 'a', 'p', 'e']
3. insert()
:在指定位置插入元素
- 语法:
list.insert(index, element)
- 特点:将元素插入到指定索引位置,原位置及后续元素后移,原地修改列表。
python
运行
fruits = ["apple", "banana"]
fruits.insert(1, "cherry") # 在索引1处插入
print(fruits) # 输出: ['apple', 'cherry', 'banana']fruits.insert(0, "date") # 在开头插入
print(fruits) # 输出: ['date', 'apple', 'cherry', 'banana']
4. +
或 +=
:拼接列表
+
:创建新列表,不修改原列表。+=
:等价于extend()
,原地修改列表。
python
运行
# 使用 + 创建新列表
fruits1 = ["apple", "banana"]
fruits2 = ["cherry", "date"]
new_fruits = fruits1 + fruits2
print(new_fruits) # 输出: ['apple', 'banana', 'cherry', 'date']# 使用 += 原地扩展
fruits1 += fruits2
print(fruits1) # 输出: ['apple', 'banana', 'cherry', 'date']
5. 切片赋值:替换或插入元素
- 语法:
list[start:end] = iterable
- 特点:可在指定位置替换或插入多个元素,原地修改列表。
python
运行
fruits = ["apple", "banana", "cherry"]
fruits[1:2] = ["date", "elderberry"] # 替换索引1的元素
print(fruits) # 输出: ['apple', 'date', 'elderberry', 'cherry']fruits[1:1] = ["fig"] # 在索引1处插入(不替换)
print(fruits) # 输出: ['apple', 'fig', 'date', 'elderberry', 'cherry']
方法对比表
方法 | 作用 | 是否原地修改 | 接受多个元素? | 适用场景 |
---|---|---|---|---|
append() | 末尾添加单个元素 | 是 | 否 | 简单添加单个元素 |
extend() | 末尾展开添加可迭代对象 | 是 | 是 | 添加多个元素(如列表合并) |
insert() | 指定位置插入单个元素 | 是 | 否 | 在特定位置插入元素 |
+ | 拼接两个列表 | 否 | 是 | 创建新列表(不修改原列表) |
+= | 原地扩展列表 | 是 | 是 | 高效扩展现有列表 |
切片赋值 | 替换 / 插入元素 | 是 | 是 | 复杂的批量替换或插入操作 |
常见错误
- 误用
append()
添加列表:若直接append()
一个列表,会将整个列表作为单个元素添加。
python
运行
fruits = ["apple", "banana"]
fruits.append(["cherry", "date"])
print(fruits) # 输出: ['apple', 'banana', ['cherry', 'date']] (嵌套列表)
正确做法是使用 extend()
或 +=
:
python
运行
fruits = ["apple", "banana"]
fruits.extend(["cherry", "date"])
print(fruits) # 输出: ['apple', 'banana', 'cherry', 'date']
总结
根据需求选择合适的方法:
- 若只需添加单个元素,用
append()
。 - 若要添加多个元素(如合并列表),用
extend()
或+=
。 - 若需在特定位置插入,用
insert()
或切片赋值。 - 若不想修改原列表,用
+
创建新列表。