Python基础知识总结

1. 列表(Lists)

定义与特性

  • 有序集合:列表中的元素按照插入顺序排列,每个元素都有一个对应的索引。
  • 可变性:列表是可变的,可以在创建后修改其内容,包括添加、删除或更改元素。
  • 多样性:列表可以包含不同类型的元素,如整数、字符串、甚至其他列表(嵌套列表)。

创建列表

1
2
3
4
5
# 创建一个空列表
empty_list = []

# 创建一个包含多个元素的列表
fruits = ['苹果', '香蕉', '橙子']

基本操作

索引和切片

  • 索引:通过索引访问特定位置的元素,索引从0开始。支持负索引,从末尾开始计数。
  • 切片:通过指定开始和结束索引来获取子列表。
1
2
3
4
fruits = ['苹果', '香蕉', '橙子', '葡萄', '桃子']
print(fruits[0]) # 输出: 苹果
print(fruits[-1]) # 输出: 桃子
print(fruits[1:3]) # 输出: ['香蕉', '橙子']

添加和删除元素

  • 添加元素

    • append(x):在列表末尾添加一个元素。
    • insert(i, x):在指定位置插入一个元素。
    • extend(iterable):将一个可迭代对象的所有元素添加到列表末尾。
  • 删除元素

    • remove(x):删除列表中第一个匹配的元素。
    • pop([i]):删除并返回指定位置的元素,默认删除最后一个元素。
    • clear():删除列表中的所有元素。
1
2
3
4
5
6
fruits = ['苹果', '香蕉', '橙子']
fruits.append('葡萄') # ['苹果', '香蕉', '橙子', '葡萄']
fruits.insert(1, '桃子') # ['苹果', '桃子', '香蕉', '橙子', '葡萄']
fruits.remove('香蕉') # ['苹果', '桃子', '橙子', '葡萄']
last_fruit = fruits.pop() # 删除并返回 '葡萄'
fruits.clear() # []

列表推导式

列表推导式是一种简洁的生成列表的方式,通常包含一个表达式和一个或多个 for 循环以及可选的 if 条件。

1
2
3
4
5
# 生成0到9的平方列表
squares = [x**2 for x in range(10)] # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 生成偶数的平方列表
even_squares = [x**2 for x in range(10) if x % 2 == 0] # [0, 4, 16, 36, 64]

其他常用方法

  • index(x):返回元素x在列表中第一次出现的索引。
  • count(x):返回元素x在列表中出现的次数。
  • sort():对列表进行排序。
  • reverse():反转列表中的元素。
1
2
3
4
5
numbers = [3, 1, 4, 1, 5, 9, 2]
print(numbers.index(5)) # 输出: 4
print(numbers.count(1)) # 输出: 2
numbers.sort() # [1, 1, 2, 3, 4, 5, 9]
numbers.reverse() # [9, 5, 4, 3, 2, 1, 1]

2. 元组(Tuples)

定义与特性

  • 有序集合:元组中的元素按照插入顺序排列,每个元素都有一个对应的索引。
  • 不可变性:元组一旦创建,其内容不能修改,包括添加、删除或更改元素。
  • 多样性:元组可以包含不同类型的元素,支持嵌套。

创建元组

1
2
3
4
5
# 创建一个空元组
empty_tuple = ()

# 创建一个包含多个元素的元组
coordinates = (10, 20)

基本操作

访问元素

  • 索引:通过索引访问特定位置的元素,支持负索引。
  • 切片:获取子元组。
1
2
3
4
coordinates = (10, 20, 30, 40)
print(coordinates[0]) # 输出: 10
print(coordinates[-1]) # 输出: 40
print(coordinates[1:3]) # 输出: (20, 30)

不可变性

  • 特性:元组一旦创建,不能修改其内容。这使得元组在需要确保数据不被意外修改的场景下非常有用,例如作为字典的键。
  • 优点:由于不可变性,元组在某些情况下比列表更高效,尤其是在作为哈希键使用时。

元组的使用场景

  • 多重赋值

    1
    2
    point = (10, 20)
    x, y = point
  • 函数返回多个值

    1
    2
    3
    4
    def get_min_max(values):
    return (min(values), max(values))

    min_val, max_val = get_min_max([3, 1, 4, 1, 5, 9, 2])

其他常用方法

  • count(x):返回元素x在元组中出现的次数。
  • index(x):返回元素x在元组中第一次出现的索引。
1
2
3
numbers = (1, 2, 3, 2, 4)
print(numbers.count(2)) # 输出: 2
print(numbers.index(4)) # 输出: 4

3. 集合(Sets)

定义与特性

  • 无序集合:集合中的元素没有特定的顺序。
  • 唯一性:集合中的元素是唯一的,重复的元素会被自动去除。
  • 可变性:集合是可变的,可以添加或删除元素。
  • 类型限制:集合中的元素必须是可哈希的(即不可变的)。

创建集合

1
2
3
4
5
# 创建一个空集合(注意:{} 创建的是空字典)
empty_set = set()

# 创建一个包含多个元素的集合
fruits = {'苹果', '香蕉', '橙子'}

基本操作

添加和删除元素

  • 添加元素

    • add(x):添加一个元素到集合中。
    • update(iterable):添加多个元素到集合中。
  • 删除元素

    • remove(x):删除集合中的元素x,如果x不存在会引发 KeyError
    • discard(x):删除集合中的元素x,如果x不存在不会有任何操作。
    • pop():随机删除并返回一个元素。
1
2
3
4
5
6
fruits = {'苹果', '香蕉'}
fruits.add('橙子') # {'苹果', '香蕉', '橙子'}
fruits.update(['葡萄', '桃子']) # {'苹果', '香蕉', '橙子', '葡萄', '桃子'}
fruits.remove('香蕉') # {'苹果', '橙子', '葡萄', '桃子'}
fruits.discard('草莓') # 不存在,不报错
removed_fruit = fruits.pop() # 随机删除一个元素

集合运算

  • 并集(Union):两个集合的所有元素。

    • 运算符:|
    • 方法:union()
  • 交集(Intersection):两个集合的共同元素。

    • 运算符:&
    • 方法:intersection()
  • 差集(Difference):存在于第一个集合但不存在于第二个集合的元素。

    • 运算符:-
    • 方法:difference()
  • 对称差集(Symmetric Difference):存在于其中一个集合但不同时存在于两个集合中的元素。

    • 运算符:^
    • 方法:symmetric_difference()
1
2
3
4
5
6
7
a = {'苹果', '香蕉', '橙子'}
b = {'香蕉', '葡萄', '桃子'}

print(a | b) # 并集: {'苹果', '香蕉', '橙子', '葡萄', '桃子'}
print(a & b) # 交集: {'香蕉'}
print(a - b) # 差集: {'苹果', '橙子'}
print(a ^ b) # 对称差集: {'苹果', '橙子', '葡萄', '桃子'}

其他常用方法

  • issubset(other):判断当前集合是否是另一个集合的子集。
  • issuperset(other):判断当前集合是否是另一个集合的超集。
  • isdisjoint(other):判断两个集合是否没有共同元素。
1
2
3
4
5
6
7
a = {'苹果', '香蕉'}
b = {'苹果', '香蕉', '橙子'}

print(a.issubset(b)) # 输出: True
print(b.issuperset(a)) # 输出: True
print(a.isdisjoint({'葡萄'})) # 输出: True
print(a.isdisjoint({'香蕉'})) # 输出: False

4. 字典(Dictionaries)

定义与特性

  • 键值对集合:字典由键(key)和值(value)组成的对组成,每个键与其对应的值相关联。
  • 无序集合:字典中的键值对没有特定的顺序(Python 3.7+ 中,字典保持插入顺序)。
  • 键的唯一性:每个键在字典中必须是唯一的,值则可以重复。
  • 键的不可变性:键必须是不可变的数据类型,如字符串、数字或元组。

创建字典

1
2
3
4
5
6
7
8
9
# 创建一个空字典
empty_dict = {}

# 创建一个包含多个键值对的字典
person = {
'姓名': '张三',
'年龄': 30,
'城市': '北京'
}

基本操作

访问与修改

  • 访问值

    • 通过键访问对应的值,使用 dict[key]dict.get(key) 方法。
    • get(key, default) 方法可以在键不存在时返回默认值,而不会引发错误。
  • 修改值

    • 通过键直接赋值,添加或更新键值对。
  • 删除键值对

    • 使用 del dict[key] 删除特定键。
    • 使用 pop(key) 删除特定键并返回其值。
    • 使用 clear() 删除所有键值对。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
person = {'姓名': '张三', '年龄': 30}

# 访问
print(person['姓名']) # 输出: 张三
print(person.get('城市', '未知')) # 输出: 未知

# 修改
person['年龄'] = 31 # 更新年龄
person['城市'] = '上海' # 添加城市

# 删除
del person['城市'] # 删除城市
age = person.pop('年龄') # 删除年龄并返回
person.clear() # 清空字典

字典方法

  • keys():返回字典中所有键的视图。
  • values():返回字典中所有值的视图。
  • items():返回字典中所有键值对的视图。
  • update(other):将另一个字典或键值对序列合并到当前字典中。
  • copy():返回字典的浅拷贝。
1
2
3
4
5
6
7
8
person = {'姓名': '张三', '年龄': 30, '城市': '北京'}

print(person.keys()) # 输出: dict_keys(['姓名', '年龄', '城市'])
print(person.values()) # 输出: dict_values(['张三', 30, '北京'])
print(person.items()) # 输出: dict_items([('姓名', '张三'), ('年龄', 30), ('城市', '北京')])

# 更新字典
person.update({'年龄': 31, '职业': '工程师'})

字典推导式

类似于列表推导式,字典推导式用于通过某种逻辑生成字典。

1
2
3
4
5
# 生成一个数字及其平方的字典
squares = {x: x**2 for x in range(5)} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# 过滤字典中的偶数键
filtered = {k: v for k, v in squares.items() if k % 2 == 0} # {0: 0, 2: 4, 4: 16}

5. 序列操作(Sequence Operations)

通用操作

Python 提供了一组通用函数,可以应用于所有序列类型(如列表、元组、字符串等)。

  • len(s):返回序列的长度。
  • max(s):返回序列中的最大值。
  • min(s):返回序列中的最小值。
  • sorted(s):返回一个排序后的新列表。
  • reversed(s):返回一个反向迭代器。
1
2
3
4
5
6
7
fruits = ['苹果', '香蕉', '橙子']
print(len(fruits)) # 输出: 3
print(max(fruits)) # 输出: 橙子(根据字母顺序)
print(min(fruits)) # 输出: 苹果

sorted_fruits = sorted(fruits) # ['苹果', '橙子', '香蕉']
reversed_fruits = list(reversed(fruits)) # ['橙子', '香蕉', '苹果']

成员测试

使用 innot in 运算符检查元素是否存在于序列中。

1
2
3
fruits = ['苹果', '香蕉', '橙子']
print('香蕉' in fruits) # 输出: True
print('葡萄' not in fruits) # 输出: True

连接与重复

  • 连接:使用 + 运算符连接两个序列。
  • 重复:使用 * 运算符重复序列。
1
2
3
4
5
6
7
8
9
a = [1, 2, 3]
b = [4, 5]
c = a + b # [1, 2, 3, 4, 5]
d = a * 2 # [1, 2, 3, 1, 2, 3]

s1 = 'Hello'
s2 = 'World'
s3 = s1 + ' ' + s2 # 'Hello World'
s4 = s1 * 3 # 'HelloHelloHello'

6. 列表推导式(List Comprehensions)

定义与用法

列表推导式是一种简洁且高效的方法,用于根据已有的可迭代对象生成新列表。它由一个表达式和一个或多个 for 循环组成,并可包含条件筛选。

1
2
3
4
5
# 基本语法
[表达式 for 变量 in 可迭代对象]

# 带条件的列表推导式
[表达式 for 变量 in 可迭代对象 if 条件]

示例

  • 生成平方数列表

    1
    squares = [x**2 for x in range(10)]  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
  • 筛选偶数

    1
    even_squares = [x**2 for x in range(10) if x % 2 == 0]  # [0, 4, 16, 36, 64]
  • 嵌套循环

    1
    2
    pairs = [(x, y) for x in range(3) for y in range(3)]  
    # [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

嵌套推导式

列表推导式可以嵌套使用,用于生成多维列表或更复杂的数据结构。

1
2
3
# 生成一个3x3的矩阵
matrix = [[x for x in range(3)] for y in range(3)]
# [[0, 1, 2], [0, 1, 2], [0, 1, 2]]

优势

  • 简洁:相比传统的 for 循环,列表推导式代码更简洁。
  • 高效:列表推导式在执行速度上通常比等效的循环更快。

注意事项

  • 可读性:过于复杂的列表推导式可能影响代码的可读性,建议在逻辑简单时使用,复杂时采用传统循环。
  • 嵌套深度:避免过多嵌套,以免代码难以理解。

7. 嵌套数据结构(Nested Data Structures)

多层嵌套

Python 的数据结构(如列表、元组、字典、集合)可以相互嵌套,形成复杂的数据层次,适用于存储和处理复杂的数据关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 列表嵌套
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]

# 字典嵌套
students = {
'张三': {'年龄': 20, '成绩': {'数学': 90, '英语': 85}},
'李四': {'年龄': 22, '成绩': {'数学': 95, '英语': 80}}
}

# 混合嵌套
data = [
{'姓名': '张三', '兴趣': ['足球', '音乐']},
{'姓名': '李四', '兴趣': ['篮球', '绘画']}
]

访问嵌套元素

通过多级索引或键访问内部元素。

1
2
3
4
5
6
7
8
# 访问嵌套列表
print(matrix[1][2]) # 输出: 6

# 访问嵌套字典
print(students['张三']['成绩']['数学']) # 输出: 90

# 访问混合嵌套
print(data[0]['兴趣'][1]) # 输出: 音乐

应用场景

  • 数据库记录:模拟数据库中的表和记录。
  • 配置文件:存储多层次的配置选项。
  • 数据分析:处理和组织复杂的数据集。

8. 其他数据结构

集合推导式(Set Comprehensions)

类似于列表推导式,集合推导式用于生成集合,保证元素的唯一性。

1
2
3
4
5
# 生成一个包含0到9平方的集合
squares_set = {x**2 for x in range(10)} # {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}

# 过滤偶数
even_squares_set = {x**2 for x in range(10) if x % 2 == 0} # {0, 4, 16, 36, 64}

字典推导式(Dictionary Comprehensions)

用于通过某种逻辑生成字典,包含键和值的表达式。

1
2
3
4
5
# 生成一个数字及其平方的字典
squares_dict = {x: x**2 for x in range(5)} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# 反转键值对
inverse_dict = {v: k for k, v in squares_dict.items()} # {0: 0, 1: 1, 4: 2, 9: 3, 16: 4}

9. 数据结构的选择

适用场景

选择合适的数据结构对编写高效、可维护的代码至关重要。以下是常见数据结构的适用场景:

  • 列表(Lists)

    • 特点:有序、可变、允许重复元素。
    • 适用场景:需要按顺序存储元素并频繁进行添加、删除操作的场景,如任务列表、动态数据集合。
  • 元组(Tuples)

    • 特点:有序、不可变、允许重复元素。
    • 适用场景:需要固定数据集合,确保数据不被修改的场景,如函数返回多个值、字典的键。
  • 集合(Sets)

    • 特点:无序、唯一、不重复。
    • 适用场景:需要去重、进行集合运算(如并集、交集)的场景,如用户标签、权限管理。
  • 字典(Dictionaries)

    • 特点:键值对存储、无序(保持插入顺序)、键唯一。
    • 适用场景:需要快速查找、关联数据的场景,如配置选项、数据库记录。

性能考虑

  • 访问速度

    • 列表:按索引访问速度快,但查找元素需要线性时间。
    • 字典和集合:基于哈希表,查找、插入和删除操作平均时间复杂度为O(1)。
  • 内存使用

    • 列表和元组:内存占用较低,尤其是元组因不可变性更为紧凑。
    • 字典和集合:由于需要存储哈希值,内存占用相对较高。

可变性与不可变性

  • 可变数据结构(如列表、字典、集合)适用于需要频繁修改数据的场景。
  • 不可变数据结构(如元组)适用于需要确保数据不被修改或作为哈希键的场景。

模块(Modules)

1. 模块的定义与导入

定义

  • 模块是一个包含Python定义和语句的文件。模块可以定义函数、类和变量,还可以包含可执行的代码。
  • 通过模块,可以组织和重用代码,促进代码的模块化和维护性。

导入模块

  • 使用 import 语句导入模块。
  • 语法:
    1
    import module_name
  • 例子:
    1
    2
    import math
    print(math.sqrt(16)) # 输出: 4.0

从模块中导入特定部分

  • 使用 from ... import ... 语句。
  • 语法:
    1
    from module_name import function_name
  • 例子:
    1
    2
    from math import sqrt
    print(sqrt(25)) # 输出: 5.0

导入所有内容

  • 使用 from module_name import * 导入模块的所有公共属性和方法。
  • 例子:
    1
    2
    3
    from math import *
    print(pi) # 输出: 3.141592653589793
    print(sin(0)) # 输出: 0.0
  • 注意:不推荐使用 import *,因为它可能导致命名冲突,降低代码可读性。

使用别名

  • 使用 as 关键字为模块或导入的部分指定别名。
  • 例子:
    1
    2
    3
    4
    5
    import math as m
    print(m.sqrt(9)) # 输出: 3.0

    from math import sqrt as square_root
    print(square_root(16)) # 输出: 4.0

2. 模块搜索路径

搜索顺序

当导入模块时,Python解释器会按照以下顺序搜索模块:

  1. 当前目录:首先在当前脚本所在的目录查找模块。
  2. **环境变量 PYTHONPATH**:如果设置了 PYTHONPATH,则在该路径下搜索。
  3. 标准库目录:搜索Python安装目录下的标准库模块。
  4. 第三方库目录:搜索安装的第三方库模块。

查看搜索路径

  • 使用 sys 模块的 sys.path 属性查看当前的模块搜索路径。
  • 例子:
    1
    2
    import sys
    print(sys.path)

修改搜索路径

  • 可以在运行时通过修改 sys.path 来添加新的搜索路径。
  • 例子:
    1
    2
    import sys
    sys.path.append('/path/to/your/module')

3. 标准模块

标准库概述

  • Python提供了丰富的标准库模块,涵盖了文件操作、系统调用、网络编程、数据处理等多个领域。
  • 常用标准模块包括 sys, os, math, datetime, json, re 等。

例子:使用 datetime 模块

1
2
3
4
import datetime

now = datetime.datetime.now()
print(now) # 输出当前日期和时间

4. 创建模块

创建一个简单模块

  • 创建一个Python文件,例如 mymodule.py,并定义一些函数和变量。
    1
    2
    3
    4
    5
    6
    # mymodule.py

    def greet(name):
    return f"Hello, {name}!"

    pi = 3.141592653589793
  • 在另一个脚本中导入并使用该模块。
    1
    2
    3
    4
    import mymodule

    print(mymodule.greet("Alice")) # 输出: Hello, Alice!
    print(mymodule.pi) # 输出: 3.141592653589793

模块的命名空间

  • 每个模块都有自己的命名空间,模块内部的变量不会影响其他模块,反之亦然。
  • 可以使用 dir() 函数查看模块的属性。
    1
    2
    import mymodule
    print(dir(mymodule))

5. 包(Packages)

定义

  • 是一种组织模块的方式,使用文件夹和 __init__.py 文件实现。
  • 包可以包含子包和模块,形成层级结构。

创建包

  • 创建一个文件夹,例如 mypackage,并在其中添加一个 __init__.py 文件(可以为空)。

  • mypackage 文件夹中添加模块文件,例如 module1.pymodule2.py

    1
    2
    3
    4
    mypackage/
    __init__.py
    module1.py
    module2.py
  • 导入包中的模块:

    1
    2
    from mypackage import module1
    from mypackage.module2 import some_function

相对导入

  • 在包内部,可以使用相对导入来引用同一包中的其他模块。
  • 使用 . 表示当前包,.. 表示上级包。
    1
    2
    3
    # 在 mypackage/module1.py 中
    from . import module2
    from .module2 import some_function

6. 模块的命名空间

全局变量与局部变量

  • 模块的全局变量在整个模块中可见。
  • 在函数或类内部定义的变量是局部变量,仅在其作用范围内可见。

__name__ 变量

  • 每个模块都有一个特殊变量 __name__,表示模块的名称。

  • 当模块被直接运行时,__name__ 的值为 '__main__'

  • 当模块被导入时,__name__ 的值为模块的实际名称。

    1
    2
    3
    4
    5
    # mymodule.py
    if __name__ == "__main__":
    print("模块被直接运行")
    else:
    print("模块被导入")

7. from-import 语句

基本用法

  • 从模块中导入特定的属性或方法,避免使用模块前缀。
    1
    2
    3
    from math import sqrt, pi
    print(sqrt(16)) # 输出: 4.0
    print(pi) # 输出: 3.141592653589793

导入所有内容

  • 使用 from module import * 导入模块的所有公共属性和方法。
    1
    2
    from math import *
    print(sin(0)) # 输出: 0.0
  • 注意:这种方式可能导致命名冲突,需谨慎使用。

为导入的属性指定别名

  • 使用 as 关键字为导入的属性指定别名。
    1
    2
    from math import sqrt as square_root
    print(square_root(25)) # 输出: 5.0

8. 动态导入模块

使用 __import__ 函数

  • 可以在运行时动态导入模块,适用于需要根据变量导入不同模块的情况。
    1
    2
    3
    module_name = "math"
    math_module = __import__(module_name)
    print(math_module.sqrt(9)) # 输出: 3.0

使用 importlib 模块

  • 更现代和灵活的动态导入方式。
    1
    2
    3
    4
    5
    import importlib

    module_name = "math"
    math_module = importlib.import_module(module_name)
    print(math_module.pi) # 输出: 3.141592653589793

9. 模块的生命周期

导入模块时的执行过程

  • 当模块被首次导入时,Python会执行模块中的所有顶层代码,并将模块对象存储在 sys.modules 中。
  • 后续导入相同模块时,不会重新执行模块代码,而是直接从 sys.modules 中获取模块对象。

模块的缓存

  • sys.modules 是一个字典,保存了所有已导入的模块。它可以用于检查模块是否已被导入,或动态修改模块。

    1
    2
    3
    4
    import sys
    import math

    print('math' in sys.modules) # 输出: True

10. 编写可重用的代码

模块化编程的优势

  • 代码重用:将常用功能封装在模块中,便于多处使用。
  • 命名空间隔离:避免不同模块之间的命名冲突。
  • 可维护性:模块化代码更易于理解和维护。

示例:创建一个工具模块

1
2
3
4
5
6
7
# utils.py

def add(a, b):
return a + b

def subtract(a, b):
return a - b
1
2
3
4
5
6
# main.py

import utils

print(utils.add(5, 3)) # 输出: 8
print(utils.subtract(10, 4)) # 输出: 6

11. 内建模块(Built-in Modules)

概述

  • Python提供了许多内建模块,随解释器一起分发,无需额外安装即可使用。
  • 常用内建模块包括 sys, os, math, datetime, json, re, random 等。

示例:使用 random 模块

1
2
3
4
import random

print(random.randint(1, 10)) # 输出: 1到10之间的随机整数
print(random.choice(['apple', 'banana', 'cherry'])) # 随机选择一个水果

12. 模块的高级特性

包的相对导入

  • 在包内部,可以使用相对导入来引用同一包中的其他模块。
  • 示例结构:
    1
    2
    3
    4
    5
    6
    mypackage/
    __init__.py
    module1.py
    subpackage/
    __init__.py
    module2.py
  • module2.py 中导入 module1
    1
    from .. import module1

动态修改模块

  • 可以在运行时添加、删除或修改模块的属性。
    1
    2
    3
    import math
    math.new_attribute = "新的属性"
    print(math.new_attribute) # 输出: 新的属性

模块的生命周期管理

  • 使用 importlib.reload() 可以重新加载模块,适用于开发过程中需要更新模块内容的场景。
    1
    2
    3
    4
    import importlib
    import mymodule

    importlib.reload(mymodule)

13. __main__ 模块

定义

  • 当模块被直接运行时,__name__ 变量被设置为 '__main__'
  • 这使得模块可以区分是被导入还是被直接运行,从而执行不同的代码逻辑。

示例

1
2
3
4
5
6
7
# mymodule.py

def main():
print("模块被直接运行")

if __name__ == "__main__":
main()
1
2
3
4
5
6
7
# 直接运行模块
$ python mymodule.py
输出: 模块被直接运行

# 导入模块
>>> import mymodule
# 不会输出任何内容

14. 小结

模块是Python中组织和重用代码的基本单元。通过模块化编程,可以提高代码的可维护性、可读性和复用性。理解模块的导入机制、搜索路径、命名空间以及包的结构,是高效编写Python程序的关键。

15. 实践建议

  • 合理组织代码:将相关功能封装在模块和包中,避免模块过于庞大。
  • 使用有意义的命名:为模块和包选择描述性强的名称,便于理解和使用。
  • 遵循PEP 8:遵循Python的编码规范(PEP 8),保持代码一致性和可读性。
  • 避免循环依赖:模块之间尽量减少相互依赖,避免复杂的循环导入关系。
  • 利用标准库:在编写新模块前,先查找是否已有标准库模块或第三方库能够满足需求。

输入与输出(Input and Output)

更复杂的输出格式

在 Python 中,除了基本的 print() 函数外,还有多种方法可以实现更复杂和灵活的输出格式,包括格式化字符串字面值(f-strings)、str.format() 方法、手动格式化字符串以及旧式的字符串格式化方法。

格式化字符串字面值(f-strings)

定义与特性

  • 从 Python 3.6 开始引入的字符串格式化方法,使用前缀 fF
  • 允许在字符串中嵌入表达式,表达式放在花括号 {} 内。

示例

1
2
3
4
5
6
7
8
9
name = "Alice"
age = 30
print(f"姓名:{name},年龄:{age}")
# 输出: 姓名:Alice,年龄:30

# 带表达式
pi = 3.14159
print(f"圆周率约为 {pi:.2f}")
# 输出: 圆周率约为 3.14

优势

  • 语法简洁,易读性强。
  • 支持表达式计算,可以直接在字符串中进行计算和调用函数。

字符串 format() 方法

定义与用法

  • 使用花括号 {} 作为占位符,通过 str.format() 方法插入变量。
  • 支持位置参数和关键字参数。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
name = "Bob"
age = 25
print("姓名:{},年龄:{}".format(name, age))
# 输出: 姓名:Bob,年龄:25

# 使用关键字参数
print("姓名:{name},年龄:{age}".format(name="Charlie", age=28))
# 输出: 姓名:Charlie,年龄:28

# 嵌套字段
data = {'name': 'Diana', 'age': 22}
print("姓名:{name},年龄:{age}".format(**data))
# 输出: 姓名:Diana,年龄:22

格式说明符

  • 可以在花括号内添加格式说明符,如指定宽度、对齐方式、小数位数等。
    1
    2
    3
    pi = 3.14159
    print("圆周率约为 {:.2f}".format(pi))
    # 输出: 圆周率约为 3.14

手动格式化字符串

定义与用法

  • 通过字符串的拼接或转换,将变量手动插入到字符串中。
  • 使用 + 运算符或 , 分隔符。

示例

1
2
3
4
5
6
7
name = "Eve"
age = 35
print("姓名:" + name + ",年龄:" + str(age))
# 输出: 姓名:Eve,年龄:35

print("姓名:", name, ",年龄:", age, sep="")
# 输出: 姓名:Eve,年龄:35

注意事项

  • 需要手动转换非字符串类型为字符串。
  • 代码可能较为冗长,可读性较低。

旧式字符串格式化方法

定义与用法

  • 使用 % 运算符进行字符串格式化。
  • 类似于 C 语言中的 printf 风格。

示例

1
2
3
4
5
6
7
8
name = "Frank"
age = 40
print("姓名:%s,年龄:%d" % (name, age))
# 输出: 姓名:Frank,年龄:40

pi = 3.14159
print("圆周率约为 %.2f" % pi)
# 输出: 圆周率约为 3.14

注意事项

  • 虽然仍然被支持,但不推荐在新代码中使用,因为 str.format() 和 f-strings 提供了更强大的功能和更好的可读性。

读写文件

Python 提供了多种方法来读取和写入文件,包括文本文件和二进制文件的操作,以及使用 JSON 保存结构化数据。

文件对象的方法

打开文件

  • 使用 open() 函数打开文件,返回文件对象。
  • 常用模式包括读取 'r'、写入 'w'、追加 'a',以及二进制模式 'rb', 'wb' 等。

示例

1
2
3
4
5
# 以读取模式打开文件
f = open('example.txt', 'r', encoding='utf-8')

# 以写入模式打开文件
f = open('example.txt', 'w', encoding='utf-8')

常用方法

  • read(size=-1):读取指定大小的内容,默认读取全部内容。

    1
    2
    3
    with open('example.txt', 'r', encoding='utf-8') as f:
    content = f.read()
    print(content)
  • readline():读取文件的一行内容。

    1
    2
    3
    4
    5
    with open('example.txt', 'r', encoding='utf-8') as f:
    line = f.readline()
    while line:
    print(line, end='')
    line = f.readline()
  • readlines():读取文件的所有行,返回一个列表。

    1
    2
    3
    4
    with open('example.txt', 'r', encoding='utf-8') as f:
    lines = f.readlines()
    for line in lines:
    print(line, end='')
  • write(string):将字符串写入文件。

    1
    2
    3
    with open('example.txt', 'w', encoding='utf-8') as f:
    f.write('Hello, World!\n')
    f.write('这是一个示例文件。\n')
  • writelines(lines):将一个字符串列表写入文件,不自动添加换行符。

    1
    2
    3
    lines = ['第一行\n', '第二行\n', '第三行\n']
    with open('example.txt', 'w', encoding='utf-8') as f:
    f.writelines(lines)

使用 with 语句管理文件

  • 使用 with 语句可以自动管理文件资源,确保文件在使用完毕后正确关闭,即使发生异常也能保证关闭文件。
  • 示例
    1
    2
    3
    with open('example.txt', 'r', encoding='utf-8') as f:
    content = f.read()
    print(content)

文件模式

  • 'r':只读模式,文件指针放在文件开头。
  • 'rb':二进制读取模式。
  • 'w':写入模式,会覆盖已有文件。
  • 'wb':二进制写入模式。
  • 'a':追加模式,文件指针放在文件末尾。
  • 'ab':二进制追加模式。
  • 'r+':读写模式,文件指针放在开头。
  • 'w+':写读模式,会覆盖文件。
  • 'a+':追加读写模式。

文件指针操作

  • tell():返回文件指针的当前位置(以字节为单位)。
    1
    2
    3
    4
    with open('example.txt', 'r', encoding='utf-8') as f:
    f.read(10)
    position = f.tell()
    print(f"当前文件指针位置:{position}")
  • seek(offset, whence):移动文件指针到指定位置。
    • offset:偏移量。
    • whence:参考点,0 表示文件开头,1 表示当前位置,2 表示文件末尾。
      1
      2
      3
      4
      with open('example.txt', 'r', encoding='utf-8') as f:
      f.seek(5) # 移动到第6个字节
      data = f.read(10)
      print(data)

使用 JSON 保存结构化数据

定义与用途

  • JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。
  • 适用于保存和传输结构化数据,如配置文件、数据存储等。

Python 中的 JSON 模块

  • Python 提供了内置的 json 模块,用于处理 JSON 数据的编码和解码。

常用函数

  • json.dump(obj, fp, ...):将 Python 对象编码为 JSON 格式,并写入文件对象 fp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import json

    data = {
    'name': 'Alice',
    'age': 30,
    'city': 'Beijing',
    'interests': ['reading', 'traveling']
    }

    with open('data.json', 'w', encoding='utf-8') as f:
    json.dump(data, f, ensure_ascii=False, indent=4)
  • json.load(fp, ...):从文件对象 fp 中读取 JSON 数据,并解码为 Python 对象。

    1
    2
    3
    4
    5
    6
    import json

    with open('data.json', 'r', encoding='utf-8') as f:
    data = json.load(f)
    print(data)
    # 输出: {'name': 'Alice', 'age': 30, 'city': 'Beijing', 'interests': ['reading', 'traveling']}
  • json.dumps(obj, ...):将 Python 对象编码为 JSON 字符串。

    1
    2
    3
    4
    5
    6
    import json

    data = {'name': 'Bob', 'age': 25}
    json_str = json.dumps(data)
    print(json_str)
    # 输出: {"name": "Bob", "age": 25}
  • json.loads(s, ...):将 JSON 字符串解码为 Python 对象。

    1
    2
    3
    4
    5
    6
    import json

    json_str = '{"name": "Charlie", "age": 28}'
    data = json.loads(json_str)
    print(data)
    # 输出: {'name': 'Charlie', 'age': 28}

参数说明

  • ensure_ascii:默认为 True,确保输出的所有字符都是 ASCII 字符。设为 False 可输出非 ASCII 字符。
  • indent:设置缩进级别,使输出的 JSON 更加美观和易读。

示例:保存和读取复杂数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import json

# 复杂数据结构
data = {
'employees': [
{'name': 'Alice', 'age': 30, 'department': 'HR'},
{'name': 'Bob', 'age': 25, 'department': 'IT'},
{'name': 'Charlie', 'age': 28, 'department': 'Finance'}
],
'company': 'TechCorp',
'location': 'New York'
}

# 写入 JSON 文件
with open('company.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=4)

# 读取 JSON 文件
with open('company.json', 'r', encoding='utf-8') as f:
loaded_data = json.load(f)
print(loaded_data)

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"employees": [
{
"name": "Alice",
"age": 30,
"department": "HR"
},
{
"name": "Bob",
"age": 25,
"department": "IT"
},
{
"name": "Charlie",
"age": 28,
"department": "Finance"
}
],
"company": "TechCorp",
"location": "New York"
}

注意事项

  • JSON 支持的数据类型有限,主要包括对象(字典)、数组(列表)、字符串、数字、布尔值和 null。不支持自定义对象或复杂数据类型,需先转换为可序列化的格式。
  • 使用 json 模块时,确保数据结构中所有键和值都是 JSON 支持的类型。

读写二进制文件

除了文本文件,Python 还支持二进制文件的读写操作,适用于处理图片、音频、视频等非文本数据。

读取二进制文件

1
2
3
with open('image.png', 'rb') as f:
data = f.read()
# 处理二进制数据

写入二进制文件

1
2
with open('copy_image.png', 'wb') as f:
f.write(data)

注意事项

  • 在二进制模式下,读写的是字节对象(bytes),而不是字符串。
  • 处理二进制数据时,需要注意编码和解码问题,确保数据的正确性。

文件对象的方法

常用方法概览

  • read(size=-1):读取指定大小的内容,默认读取全部内容。
  • readline(size=-1):读取一行内容。
  • readlines(hint=-1):读取所有行,返回列表。
  • write(string):写入字符串。
  • writelines(lines):写入字符串列表。
  • seek(offset, whence=0):移动文件指针。
  • tell():返回文件指针当前位置。
  • flush():刷新缓冲区,将数据写入文件。
  • close():关闭文件。

示例

1
2
3
4
5
6
7
8
9
# 读取文件内容
with open('example.txt', 'r', encoding='utf-8') as f:
content = f.read()
print(content)

# 写入文件内容
with open('example.txt', 'w', encoding='utf-8') as f:
f.write('Hello, Python!\n')
f.write('这是第二行内容。\n')

使用 with 语句

  • 自动管理文件资源,确保文件在使用完毕后关闭。
    1
    2
    3
    with open('example.txt', 'r', encoding='utf-8') as f:
    for line in f:
    print(line, end='')

总结

Python 的输入与输出操作功能强大且灵活,涵盖了从简单的打印信息到复杂的文件读写操作、数据格式化以及结构化数据处理。通过掌握以下关键点,可以有效地处理各种 I/O 需求:

  • 文件操作

    • 使用 open() 函数以不同模式打开文件。
    • 通过 read(), readline(), readlines(), write(), writelines() 等方法进行文件内容的读取和写入。
    • 使用 with 语句自动管理文件资源,确保文件正确关闭。
    • 处理文件指针的移动和当前位置的获取。
  • 输出格式化

    • 使用 print() 函数进行基本输出。
    • 利用 f-strings、str.format() 方法实现复杂和灵活的字符串格式化。
    • 了解手动格式化和旧式格式化方法,但推荐使用 f-strings 或 str.format()
  • 处理结构化数据

    • 使用 json 模块读取和写入 JSON 数据,适用于配置文件和数据交换。
    • 理解 JSON 的数据类型限制,确保数据可序列化。
  • 读写二进制文件

    • 了解如何以二进制模式打开和处理文件,适用于非文本数据。

理解并灵活运用这些输入与输出的方法和技巧,能够显著提升 Python 程序的功能性和用户体验。

Python 支持面向对象编程(OOP),允许开发者使用类和对象来组织和管理代码。类是创建对象的蓝图,通过类可以封装数据和功能,促进代码的复用性和可维护性。


名称和对象

在 Python 中,一切皆对象。类本身也是对象,具有自己的属性和方法。每个对象都有一个唯一的身份(ID)、类型(type)和一个值(value)。

  • 名称:名称是指向对象的引用,类似于变量名。名称绑定到对象,而不是对象绑定到名称。
  • 对象:对象是类的实例,拥有类定义的属性和方法。

示例

1
2
3
4
5
class Dog:
pass

my_dog = Dog()
print(type(my_dog)) # 输出: <class '__main__.Dog'>

Python 作用域和命名空间

作用域(Scope)和命名空间(Namespace)是理解变量解析和访问权限的关键概念。

作用域和命名空间示例

  • 局部作用域:函数或方法内部定义的命名空间。
  • 全局作用域:模块级别的命名空间。
  • 内建作用域:Python 解释器启动时创建的命名空间,包含内置函数和异常。

示例

1
2
3
4
5
6
7
8
x = "全局变量"

def foo():
x = "局部变量"
print(x)

foo() # 输出: 局部变量
print(x) # 输出: 全局变量

初探类

类是创建对象的蓝图,定义了对象的属性和行为。通过类,可以创建多个实例,每个实例都有自己的属性值。

类定义语法

使用 class 关键字定义类,类名通常采用大驼峰命名法。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Dog:
"""一个简单的狗类"""

def __init__(self, name, age):
"""初始化属性"""
self.name = name
self.age = age

def sit(self):
"""模拟小狗被命令时蹲下"""
print(f"{self.name} is now sitting.")

def roll_over(self):
"""模拟小狗被命令时打滚"""
print(f"{self.name} rolled over!")

Class 对象

类本身也是对象,属于 type 类型。可以动态地为类添加属性和方法。

示例

1
2
3
4
5
6
7
8
class MyClass:
pass

print(type(MyClass)) # 输出: <class 'type'>

# 动态添加属性
MyClass.attr = "类属性"
print(MyClass.attr) # 输出: 类属性

实例对象

实例对象是根据类创建的具体对象,拥有类定义的属性和方法。

示例

1
2
3
4
my_dog = Dog('Willie', 6)
print(f"My dog's name is {my_dog.name}.") # 输出: My dog's name is Willie.
print(f"My dog is {my_dog.age} years old.") # 输出: My dog is 6 years old.
my_dog.sit() # 输出: Willie is now sitting.

方法对象

方法是类中的函数,定义了对象的行为。方法的第一个参数通常为 self,代表实例本身。

示例

1
2
3
4
5
6
7
8
9
class Cat:
def __init__(self, name):
self.name = name

def meow(self):
print(f"{self.name} says Meow!")

my_cat = Cat('Whiskers')
my_cat.meow() # 输出: Whiskers says Meow!

类和实例变量

  • 类变量:属于类本身,所有实例共享。定义在类体中,但不在任何方法内。
  • 实例变量:属于每个实例,互不影响。通常在 __init__ 方法中通过 self 定义。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Bird:
species = '鸟类' # 类变量

def __init__(self, name):
self.name = name # 实例变量

bird1 = Bird('Sparrow')
bird2 = Bird('Eagle')

print(bird1.species) # 输出: 鸟类
print(bird2.species) # 输出: 鸟类

bird1.species = '麻雀' # 修改实例变量,不影响类变量
print(bird1.species) # 输出: 麻雀
print(bird2.species) # 输出: 鸟类
print(Bird.species) # 输出: 鸟类

补充说明

特殊方法

类中可以定义特殊方法(如 __init__, __str__, __repr__ 等)来定制对象的行为。这些方法以双下划线开头和结尾,具有特定的用途。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __str__(self):
return f"Point({self.x}, {self.y})"

def __repr__(self):
return f"Point(x={self.x}, y={self.y})"

p = Point(1, 2)
print(p) # 输出: Point(1, 2)
print(repr(p)) # 输出: Point(x=1, y=2)

描述符

描述符是实现了 __get__, __set__, __delete__ 方法的类,用于管理属性访问。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Descriptor:
def __init__(self):
self.value = None

def __get__(self, obj, objtype):
return self.value

def __set__(self, obj, value):
if not isinstance(value, int):
raise ValueError("必须是整数")
self.value = value

def __delete__(self, obj):
del self.value

class MyClass:
attr = Descriptor()

instance = MyClass()
instance.attr = 10
print(instance.attr) # 输出: 10
# instance.attr = "Hello" # 将引发 ValueError: 必须是整数

属性(Properties)

使用 @property 装饰器将方法转换为属性,实现属性的获取、设置和删除操作,增强封装性。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Temperature:
def __init__(self, kelvin):
self._kelvin = kelvin

@property
def kelvin(self):
return self._kelvin

@kelvin.setter
def kelvin(self, value):
if value < 0:
raise ValueError("温度不能低于绝对零度")
self._kelvin = value

@kelvin.deleter
def kelvin(self):
del self._kelvin

temp = Temperature(300)
print(temp.kelvin) # 输出: 300

temp.kelvin = 310
print(temp.kelvin) # 输出: 310

# temp.kelvin = -5 # 将引发 ValueError: 温度不能低于绝对零度

继承

继承是面向对象编程的核心概念之一,允许子类继承父类的属性和方法,实现代码复用和扩展。

多重继承

一个子类可以继承多个父类,形成多重继承关系。Python 使用方法解析顺序(MRO)来确定方法和属性的搜索顺序。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Mother:
def skills(self):
print("Cooking")

class Father:
def skills(self):
print("Driving")

class Child(Mother, Father):
def skills(self):
Mother.skills(self)
Father.skills(self)
print("Programming")

child = Child()
child.skills()
# 输出:
# Cooking
# Driving
# Programming

私有变量

通过命名约定控制属性和方法的访问权限,增强封装性和数据保护。

命名约定

  • **单前导下划线 _**:表示受保护的属性或方法,建议内部使用,外部不直接访问。
  • **双前导下划线 __**:触发名称重整(Name Mangling),使属性在类外部难以访问。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class MyClass:
def __init__(self):
self.public = "公开属性"
self._protected = "受保护属性"
self.__private = "私有属性"

def public_method(self):
print("公开方法")

def _protected_method(self):
print("受保护方法")

def __private_method(self):
print("私有方法")

obj = MyClass()
print(obj.public) # 输出: 公开属性
print(obj._protected) # 输出: 受保护属性
# print(obj.__private) # 将引发 AttributeError

obj.public_method() # 输出: 公开方法
obj._protected_method() # 输出: 受保护方法
# obj.__private_method() # 将引发 AttributeError

# 通过名称重整访问私有属性(不推荐)
print(obj._MyClass__private) # 输出: 私有属性
obj._MyClass__private_method() # 输出: 私有方法

使用 property 控制访问

通过 @property 装饰器和相应的装饰器控制属性的访问和修改,提供更好的封装。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class BankAccount:
def __init__(self, balance=0):
self._balance = balance

@property
def balance(self):
return self._balance

@balance.setter
def balance(self, amount):
if amount < 0:
raise ValueError("余额不能为负数")
self._balance = amount

@balance.deleter
def balance(self):
del self._balance

account = BankAccount(100)
print(account.balance) # 输出: 100

account.balance = 150
print(account.balance) # 输出: 150

# account.balance = -50 # 将引发 ValueError: 余额不能为负数

杂项说明

方法装饰器

除了 @property,还有其他方法装饰器如 @classmethod@staticmethod,用于定义类方法和静态方法。

示例

1
2
3
4
5
6
7
8
9
10
11
class MyClass:
@classmethod
def class_method(cls):
print("这是一个类方法")

@staticmethod
def static_method():
print("这是一个静态方法")

MyClass.class_method() # 输出: 这是一个类方法
MyClass.static_method() # 输出: 这是一个静态方法

动态添加属性和方法

类和实例在运行时可以动态添加属性和方法,增强灵活性。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
class Dynamic:
pass

obj = Dynamic()
obj.new_attr = "新属性"
print(obj.new_attr) # 输出: 新属性

def new_method(self):
print("这是动态添加的方法")

import types
obj.new_method = types.MethodType(new_method, obj)
obj.new_method() # 输出: 这是动态添加的方法

迭代器

类可以实现迭代器协议,使其实例能够在 for 循环中使用。迭代器需要实现 __iter____next__ 方法。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyIterator:
def __init__(self, limit):
self.limit = limit
self.current = 0

def __iter__(self):
return self

def __next__(self):
if self.current < self.limit:
num = self.current
self.current += 1
return num
else:
raise StopIteration

iterator = MyIterator(3)
for num in iterator:
print(num)
# 输出:
# 0
# 1
# 2

生成器

生成器是一种简化迭代器创建的方法,通过 yield 关键字生成值。生成器使代码更简洁,节省内存。

示例

1
2
3
4
5
6
7
8
9
10
11
12
def my_generator(limit):
current = 0
while current < limit:
yield current
current += 1

for num in my_generator(3):
print(num)
# 输出:
# 0
# 1
# 2

生成器表达式

生成器表达式是一种简洁的生成器的语法,类似于列表推导式,但使用圆括号 ()

示例

1
2
3
4
5
6
7
8
9
gen = (x**2 for x in range(5))
for num in gen:
print(num)
# 输出:
# 0
# 1
# 4
# 9
# 16

总结

Python 的类机制提供了强大的面向对象编程支持,使代码组织、复用和扩展变得更加容易。通过理解和掌握以下关键点,可以高效地使用类来构建复杂的应用程序:

  • 名称和对象:理解名称与对象的关系,掌握作用域和命名空间的概念。
  • 类的定义与实例化:使用 class 关键字定义类,通过构造方法 __init__ 初始化实例属性。
  • 类和实例变量:区分类变量与实例变量,理解它们的共享和独立性。
  • 方法对象:掌握实例方法、类方法和静态方法的定义与使用。
  • 继承与多重继承:通过继承实现代码复用,理解方法解析顺序(MRO)。
  • 私有变量:通过命名约定和 property 控制属性的访问权限,增强封装性。
  • 迭代器与生成器:实现迭代器协议,使用生成器简化迭代器创建。
  • 高级特性:如描述符、属性、动态添加属性和方法等,提升类的灵活性和功能性。

理解并灵活运用这些类的特性和方法,能够显著提升 Python 程序的组织性、可维护性和功能性。

2019-2025 Sean Yam