Python 基础
Dec 26, 2024
Python 是一种功能强大且易于学习的编程语言,它在人工智能、Web 开发、数据分析等领域都有着广泛的应用。本教程旨在帮助零基础的读者快速入门 Python 编程。
为什么选择 Python?
- 简洁易读: Python 语法清晰简洁,接近自然语言,易于理解和学习。
- 应用广泛: 从人工智能、机器学习到 Web 开发、数据科学,Python 无处不在。
- 强大的库支持: Python 拥有海量的标准库和第三方库,可以轻松完成各种任务。
- 社区活跃: 庞大的 Python 社区提供了丰富的学习资源和技术支持。
Python 在人工智能领域的应用 (包括但不限于):
- 文本到图像生成 (Text to Images)
- 大型语言模型 (LLMs)
- 深度学习 (Deep Learning)
知名公司都在使用 Python:
- Tesla (特斯拉): 使用 OpenCV 进行计算机视觉应用开发。
- Instagram (照片墙): 采用 Django 框架构建 Web 应用。
Python 的应用场景
- 人工智能 (AI) & 机器学习 (ML)
- 计算机视觉 (Computer Vision)
- Web 应用程序开发 (Web Applications): 例如 Flask, Django 等框架。
- 数据分析与可视化 (Data Analytics and Visualization)
- 自动化脚本 (Automation Scripts)
- 科学计算 (Scientific Computing)
基础语法
变量 (Variables)
变量是用于存储数据值的容器。在 Python 中,变量无需声明类型,直接赋值即可。
a = 1 # 整型变量
b = False # 布尔型变量
c = "你好" # 字符串变量 (中文 Unicode 字符)
d = None # NoneType 空值
数据类型 (Data Types)
Python 提供了多种内置数据类型:
-
数字 (Number): 用于表示数值。
- 整型 (int): 整数,例如
3
,-8
,0
- 浮点型 (float): 小数,例如
3.8
,0.001
,-9.0
- 复数 (complex): 复数,例如
2 + 3j
,可以使用complex(实部, 虚部)
创建,例如a = complex(123, 2)
#123 + 2j
- 整型 (int): 整数,例如
-
文本 (Text): 用于表示文本信息。
- 字符串 (str): 文本序列,使用单引号
'
或双引号"
括起来,例如"Hello World"
,'Python 教程'
- 字符串 (str): 文本序列,使用单引号
-
布尔值 (Boolean): 用于表示真假。
True
(真) 或False
(假)
-
序列 (Sequence): 有序的数据集合。
-
列表 (list): 可变的有序集合,元素用逗号分隔,方括号
[]
括起来。list1 = [1, 2.3, [-4, 5], ['apple']] # 列表可以包含不同类型的元素
-
元组 (tuple): 不可变的有序集合,元素用逗号分隔,圆括号
()
括起来。tuple1 = (("张三", "李四"), ("王五", "赵六")) # 元组一旦创建就不能修改
-
可变 (Mutable) vs 不可变 (Immutable):
- 可变数据类型 (如列表) 创建后可以修改其内容。
- 不可变数据类型 (如元组、字符串、数字) 创建后内容不能直接修改,任何修改操作都会创建新的对象。
-
-
映射 (Mapping): 键值对集合。
-
字典 (dictionary 或 dict): 无序的键值对集合,键值对之间用逗号分隔,花括号
{}
括起来。dict1 = { "姓名": "张三", "年龄": 20, "性别": "男", "会编程": True } # 字典使用键值对存储数据
注意: 在 Python 中,一切皆对象 (OBJECT)! 包括字典、数字、布尔值等。
-
运算符 (Operators)
运算符 (Operator) 运算符名称 (Operator Name) 示例 (Example)
+ 加法 (Addition) 5 + 7
- 减法 (Subtraction) 5 - 7
* 乘法 (Multiplication) 5 * 7
/ 除法 (Division) 5 / 7
% 取模 (Modulus) 5 % 7 (求余数)
// 地板除法 (Floor Division) 5 // 7 (向下取整)
** 幂运算 (Exponentiation) 5 ** 2 (5 的 2 次方)
类型转换 (Type Casting)
类型转换是将一个数据类型的值转换为另一个数据类型的过程。Python 中称为类型转换 (Type Conversion)。
常用类型转换函数:
int()
: 转换为整型float()
: 转换为浮点型str()
: 转换为字符串ord()
: 返回字符的 Unicode 代码点hex()
: 转换为十六进制字符串oct()
: 转换为八进制字符串tuple()
: 转换为元组list()
: 转换为列表set()
: 转换为集合dict()
: 转换为字典
类型转换方式:
- 显式转换 (Explicit Conversion): 开发者手动进行类型转换。
- 隐式转换 (Implicit Conversion): Python 自动进行的类型转换。
显式转换示例:
a = "1" # 字符串类型
b = "2"
print(int(a) + int(b)) # 将字符串转换为整型后再相加,输出 3
隐式转换示例:
c = 1.9 # 浮点型
d = 8 # 整型
print(c + d) # d 会被隐式转换为浮点型,输出 9.9
用户输入 (Taking User Input)
使用 input()
函数获取用户输入,该函数返回的始终是 字符串 类型。
x = input("请输入第一个数字: ") # 提示用户输入
y = input("请输入第二个数字: ")
print(x + y) # 字符串拼接,例如输入 12 和 100,输出 12100
要进行数值运算,需要将输入的字符串转换为数值类型:
print(int(x) + int(y)) # 类型转换后相加,例如输入 12 和 100,输出 112
字符串操作 (String Handling)
字符串 (Strings) 是文本数据的序列,使用单引号或双引号括起来。
访问字符 (Accessing Characters)
字符串可以像数组一样通过索引访问字符,索引从 0 开始。
name = "张三"
print(name[0]) # 输出: '张'
print(name[1]) # 输出: '三'
字符串切片 (String Slicing)
切片用于提取字符串的一部分。
fruit = "苹果"
length = len(fruit) # len() 函数获取字符串长度
print("“", fruit, "” 是一个", length, "个字的词") # 输出: “ 苹果 ” 是一个 2 个字的词
pie = "苹果派"
print(pie[:2]) # 输出: '苹果' (从开始到索引 2 之前,不包含索引 2)
print(pie[2]) # 输出: '派' (索引为 2 的字符)
字符串方法 (String Methods)
Python 字符串对象提供了丰富的方法,用于字符串操作。
-
upper()
: 转换为大写。str_example = "HelloWorld" print(str_example.upper()) # 输出: 'HELLOWORLD'
-
lower()
: 转换为小写。 -
strip()
: 去除字符串首尾的空白字符 (空格、制表符、换行符等)。str_example = " 空格很多 " print(str_example.strip()) # 输出: '空格很多'
-
rstrip()
: 去除字符串尾部的指定字符。str_example = "hello!!!" print(str_example.rstrip("!")) # 输出: 'hello'
-
replace(old, new)
: 将字符串中所有出现的old
子串替换为new
子串。str_example = "香蕉苹果" print(str_example.replace("苹果", "梨子")) # 输出: '香蕉梨子'
-
split(sep)
: 根据分隔符sep
将字符串分割成列表。str_example = "苹果 香蕉 橘子" print(str_example.split(" ")) # 输出: ['苹果', '香蕉', '橘子']
-
capitalize()
: 将字符串的第一个字符转换为大写。 -
center(width, fillchar)
: 将字符串居中,并使用fillchar
填充两侧至指定宽度width
。 -
count(sub)
: 统计子串sub
在字符串中出现的次数。str_example = "abracadabra" count_a = str_example.count("a") print(count_a) # 输出: 5
-
endswith(suffix)
: 检查字符串是否以suffix
结尾,返回True
或False
。 -
find(sub)
: 查找子串sub
在字符串中首次出现的索引,如果未找到则返回-1
。str_example = "我的名字是张三,他也是。" print(str_example.find("是")) # 输出: 4 print(str_example.find("李四")) # 输出: -1
注意:
index()
方法与find()
类似,但如果子串不存在,index()
会抛出异常 (ValueError),而find()
返回-1
。 -
isalnum()
: 检查字符串是否只包含字母和数字字符,返回True
或False
。 -
islower()
: 检查字符串是否所有字符都是小写,返回True
或False
。 -
isprintable()
: 检查字符串是否所有字符都是可打印字符,返回True
或False
。 -
isspace()
: 检查字符串是否只包含空白字符,返回True
或False
。 -
istitle()
: 检查字符串是否是标题格式 (每个单词首字母大写),返回True
或False
。 -
isupper()
: 检查字符串是否所有字符都是大写,返回True
或False
。
条件语句 (Conditional Statements)
if
: 基本条件判断。if-else
: 条件成立和不成立时分别执行不同的代码块。if-elif-else
: 多条件判断。- 嵌套
if-elif-else
: 条件语句内部再嵌套条件语句。
if
语句示例
iphone_price = 60000
budget = 20000
if budget < iphone_price:
print("预算不足,再攒攒钱吧!") # 如果预算小于 iPhone 价格,则输出
else:
print("可以买个 iPhone 啦!") # 否则输出
elif
语句示例
number = 0
if number < 0:
print("数字是负数")
elif number == 0:
print("数字是零")
else:
print("数字是正数")
嵌套 if
语句示例
number = 18
if number < 0:
print("数字是负数")
elif number > 0:
if number <= 10:
print("数字在 1 到 10 之间")
elif number > 10 and number <= 20:
print("数字在 11 到 20 之间")
else:
print("数字大于 20")
else:
print("数字是零")
# 输出: 数字在 11 到 20 之间
条件运算符 (Conditional Operators)
条件运算符用于比较值。
- 运算符:
>
(大于),<
(小于),>=
(大于等于),<=
(小于等于),==
(等于),!=
(不等于)
a = 18
print(a > 18) # 输出: False
print(a <= 18) # 输出: True
print(a == 18) # 输出: True
print(a != 18) # 输出: False
循环语句 (Loops)
for
循环 (For Loops)
for
循环用于遍历可迭代对象 (Iterable objects),例如字符串、列表、元组、集合、字典等。
-
遍历字符串示例:
name = "Martin" for char in name: print(char, end=", ") # end=", " 表示每次打印后以逗号和空格结尾,而不是默认的换行符 # 输出: M, a, r, t, i, n,
-
遍历列表示例:
colors = ["红色", "绿色", "蓝色"] for color in colors: print(color) for letter in color: print(letter)
-
使用
range()
函数生成数字序列:for k in range(1, 200): # range(start, stop) 生成从 start 到 stop-1 的整数序列 print(k) # 输出: 从 1 到 199 的数字
for k in range(2, 12, 2): # range(start, stop, step) step 为步长 print(k) # 输出: 2, 4, 6, 8, 10
while
循环 (While Loop)
while
循环在条件为 True
时重复执行代码块。
count = 5
while count > 0:
print(count)
count -= 1 # 每次循环 count 减 1
else:
print("循环结束,进入 else 块") # 当 while 循环条件变为 False 时,会执行 else 块 (可选)
break
和 continue
语句 (Break & Continue)
-
break
: 立即跳出循环,终止循环的执行。 -
continue
: 跳过当前循环迭代的剩余代码,直接进入下一次迭代。 -
break
语句示例:for i in range(1, 101): print(i, end=" ") if i == 50: break # 当 i 等于 50 时,跳出循环 else: print("继续循环") # 输出: 1 继续循环 2 继续循环 ... 49 继续循环 50
-
continue
语句示例:for i in range(12): if i == 10: print("跳过本次迭代") continue # 当 i 等于 10 时,跳过本次循环的剩余代码 print("5 x", i, "=", 5 * i) # 输出: 5 x 0 = 0 ... 5 x 9 = 45 跳过本次迭代 5 x 11 = 55
函数 (Python Functions)
函数是一段组织好的、可重复使用的代码块,用于执行特定任务。函数可以提高代码的模块化和可重用性。
-
内置函数 (Built-in Functions): Python 提供的预定义函数,例如
min()
,max()
,len()
,sum()
,type()
,tuple()
,list()
,set()
,print()
,range()
,dict()
等。 -
用户自定义函数 (User-Defined Functions): 开发者根据需求定义的函数。
用户自定义函数示例:
def greet(first_name, last_name): # 定义函数 greet,接收两个参数 print("你好,", first_name, last_name) greet("三", "张") # 调用函数,传入参数
函数参数 (Function Arguments)
-
默认参数 (Default Arguments): 在定义函数时为参数设置默认值。
def greet(first_name, middle_name="某某", last_name="先生"): # middle_name 和 last_name 设置了默认值 print("你好,", first_name, middle_name, last_name) greet("李") # 输出: 你好, 李 某某 先生 (middle_name 和 last_name 使用默认值)
-
关键字参数 (Keyword Arguments): 在调用函数时使用
参数名=值
的形式传递参数,可以不按参数顺序传递。def greet(first_name, middle_name, last_name): print("你好,", first_name, middle_name, last_name) greet(middle_name="彼得", last_name="韦斯克", first_name="杰登") # 使用关键字参数,顺序可以改变 # 输出: 你好, 杰登 彼得 韦斯克
-
必需参数 (Required Arguments): 在函数定义中没有默认值的参数,调用时必须提供值。
# 调用 greet("张三", "先生") 会报错,因为缺少 last_name 参数 # greet("张三", "先生") # TypeError: greet() missing 1 required positional argument: 'last_name'
-
可变长度参数 (Variable-Length Arguments): 允许函数接收不定数量的参数。
-
*任意位置参数 (Arbitrary Arguments - args): 使用
*
前缀,将传入的位置参数打包成元组。def greet(*names): # *names 将接收到的所有位置参数打包成元组 names print("你好,", names[0], names[1], names[2]) greet("张三", "李四", "王五")
-
**任意关键字参数 (Arbitrary Keyword Arguments - kwargs): 使用
**
前缀,将传入的关键字参数打包成字典。def greet(**names): # **names 将接收到的所有关键字参数打包成字典 names print(names["first_name"], names["middle_name"], names["last_name"]) greet(first_name="张", middle_name="小", last_name="三")
-
-
return
语句 (Return Statement): 函数可以使用return
语句返回值给调用者。def add(a, b): return a + b # 返回 a + b 的结果 result = add(5, 3) print(result) # 输出: 8
列表 (List)
列表 (List) 是 Python 中常用的数据结构,它是一个 有序 且 可变 的元素集合。列表中的元素可以是不同的数据类型。
-
列表示例:
list1 = [1, 2, 3, 4, 5, 6] list2 = ["红色", "绿色", "蓝色"] details = ["张三", 20, "计算机科学"]
检查元素是否在列表中:使用
in
关键字。
colors = ["红色", "绿色", "蓝色", "黄色"]
if "黄色" in colors:
print("黄色在列表中")
列表索引范围 (Range of Index)
可以使用索引访问列表的不同部分:
-
打印从指定索引到末尾的所有元素:
animals = ["猫", "狗", "蝙蝠", "狮子", "老虎", "山羊", "牛"] print(animals[4:]) # 从索引 4 开始到末尾
-
打印从开始到指定索引之前的所有元素:
print(animals[:6]) # 从开始到索引 6 之前
-
打印间隔取值的元素 (步长为 2):
print(animals[::2]) # 从开始到末尾,步长为 2
-
在指定范围内,每隔 3 个元素取一个:
print(animals[1:8:3]) # 从索引 1 到 8 之前,步长为 3
列表推导式 (List Comprehension)
列表推导式提供了一种简洁的方式来创建列表,可以从其他可迭代对象 (如列表、元组、字典、集合,甚至字符串) 创建新列表。
-
示例 1: 筛选包含字母 "o" 的元素:
names = ["Milo", "Sarah", "Bruno", "Anastasia", "Rose"] names_with_o = [item for item in names if "o" in item] # 列表推导式,筛选包含 "o" 的名字 print(names_with_o) # 输出: ['Milo', 'Bruno', 'Rose']
-
示例 2: 筛选长度大于 4 的元素:
names_with_4_letters = [item for item in names if len(item) > 4] # 列表推导式,筛选长度大于 4 的名字 print(names_with_4_letters) # 输出: ['Sarah', 'Bruno', 'Anastasia']
列表方法 (List Methods)
以下是一些常用的列表方法:
-
list.sort()
: 对列表进行升序排序 (默认)。colors = ["紫色", "靛蓝", "蓝色", "绿色"] colors.sort() # 升序排序 print(colors) # 输出: ['蓝色', '绿色', '靛蓝', '紫色']
降序排序:
numbers = [4, 5, 1, 2, 3, 6, 7, 9, 8] numbers.sort(reverse=True) # 降序排序 print(numbers) # 输出: [9, 8, 7, 6, 5, 4, 3, 2, 1]
-
list.reverse()
: 反转列表中的元素顺序。colors.reverse() # 反转列表 print(colors) # 输出 (基于之前的排序结果): ['紫色', '靛蓝', '绿色', '蓝色']
-
list.index(item)
: 返回列表中第一个匹配项item
的索引。index_green = colors.index("绿色") print(index_green) # 输出: 2 (假设列表为 ['紫色', '靛蓝', '绿色', '蓝色'])
-
list.count(item)
: 返回列表中item
出现的次数。count_green = colors.count("绿色") print(count_green) # 输出: 1 (假设列表为 ['紫色', '靛蓝', '绿色', '蓝色'])
-
list.copy()
: 返回列表的浅拷贝。- 用于在不修改原始列表的情况下,对列表副本进行操作。
copy_colors = colors.copy() # 创建 colors 列表的副本
-
list.append(item)
: 将item
添加到列表末尾。colors = ["紫色", "靛蓝"] colors.append("蓝色") # 添加 "蓝色" 到列表末尾 print(colors) # 输出: ['紫色', '靛蓝', '蓝色']
-
list.insert(index, item)
: 在指定索引index
处插入item
。colors = ["紫色", "靛蓝"] colors.insert(1, "绿色") # 在索引 1 处插入 "绿色" print(colors) # 输出: ['紫色', '绿色', '靛蓝']
-
list.extend(iterable)
: 将另一个可迭代对象iterable
(如列表、元组) 中的元素添加到列表末尾。colors = ["紫", "靛", "蓝"] rainbow = ["绿", "黄", "橙", "红"] colors.extend(rainbow) # 将 rainbow 列表中的元素添加到 colors 列表末尾 print(colors) # 输出: ['紫', '靛', '蓝', '绿', '黄', '橙', '红']
-
列表连接 (Concatenating Two Lists):
color1 = ["红", "橙"] color2 = ["黄", "绿"] combined_list = color1 + color2 # 使用 + 运算符连接两个列表 print(combined_list) # 输出: ['红', '橙', '黄', '绿']
元组 (Tuples)
-
元组 (Tuple) 也是 有序 的数据集合,类似于列表,但元组是 不可变 的 (immutable),即创建后不能修改。
tuple1 = (1, 2, 2, 3, 4, 5, 6) tuple2 = ("红色", "绿色", "蓝色") details = ("张三", 19, "计算机科学", 9.7)
-
元组索引 (Tuple Indexes):
- 元组中的每个元素都有自己的索引,索引从 0 开始。
country = ("中国", "意大利", "西班牙")
[0] [1] [2]
country[0]
# -> "中国"
-
检查元素是否在元组中使用
in
关键字:
-
country = ("中国", "意大利", "西班牙")
if "中国" in country:
print("中国在元组中")
操作元组 (Manipulating Tuples)
-
由于元组是不可变的,如果需要添加、删除或修改元组中的元素,需要先将元组转换为 列表 (list)。
-
元组转换为列表 (Tuple to List Conversion):
countries = ("意大利", "中国", "英国") temp_list = list(countries) # 将元组转换为列表 temp_list.append("德国") # 列表可以添加元素 temp_list.pop(1) # 列表可以删除元素 (删除索引 1 的元素) temp_list[2] = "芬兰" # 列表可以修改元素 countries = tuple(temp_list) # 将列表转换回元组 print(countries)
注意: 可以直接连接两个元组,无需转换为列表。
countries1 = ("中国",) # 注意,只有一个元素的元组,元素后要加逗号
countries2 = ("英国",)
print(countries1 + countries2) # 元组连接
# 输出: ('中国', '英国')
元组方法 (Tuple Methods)
-
count(item)
: 返回元组中item
出现的次数。tup1 = (1, 2, 3, 3, 3) print(tup1.count(3)) # 输出: 3
-
index(item)
: 返回元组中第一个匹配项item
的索引。tup1 = (1, 2, 3, 4) print(tup1.index(3)) # 输出: 2
注意:
-
tup1 = (1)
创建的是整型变量,而不是元组。要创建只有一个元素的元组,需要使用tup2 = (1,)
。tup1 = (1) print(type(tup1)) # 输出: <class 'int'> tup2 = (1,) print(type(tup2)) # 输出: <class 'tuple'>
-
注意: 元组通常用于存储不应被修改的常量数据。
F-字符串 (F-Strings)
F-字符串 (Formatted String Literals) 提供了一种简洁的方式,在字符串字面值中嵌入表达式,使用花括号 {}
括起来。
name = "张三"
print(f"你好, {name}!") # 使用 f-字符串,直接在字符串中嵌入变量 name
Python 文档字符串 (Docstrings)
- 文档字符串 (Docstrings) 是字符串字面值,出现在函数、方法、类或模块定义的正文之后。
- 它们用于为代码提供文档说明。
def square(n):
'''计算数字 n 的平方并返回。''' # 文档字符串,描述函数功能
return n**2
print(square(5)) # 输出:25
print(square.__doc__) # 访问函数的文档字符串,输出:计算数字 n 的平方并返回。
Python 注释 (Comments) vs. 文档字符串 (Docstrings)
-
注释 (Comments): 用于解释代码,给人类阅读。Python 解释器会忽略注释。类似于你暗恋的人忽略你!
-
文档字符串 (Docstrings): 用于为函数、方法、类等编写文档。可以使用
__doc__
属性访问文档字符串。print(square.__doc__) # 访问 square 函数的文档字符串
PEP 8 代码规范 (PEP 8)
- PEP 8 是一份 Python 代码风格指南,提供了编写 Python 代码的最佳实践建议。
- 旨在提高 Python 代码的可读性和一致性。
- PEP: Python 增强提案 (Python Enhancement Proposal)
递归 (Recursion)
- 递归 (Recursion) 是一种定义事物的方式,其中该事物在其自身定义中被引用。
- 函数可以调用其他函数,也可以调用自身。
def factorial(num):
if (num == 1 or num == 0):
return 1 # 递归基例:当 num 为 1 或 0 时,阶乘为 1
else:
return (num * factorial(num-1)) # 递归调用:factorial(num) 调用 factorial(num-1)
阶乘计算过程示例 (以 5! 为例):
factorial(5)
= 5 * factorial(4)
= 5 * 4 * factorial(3)
= 5 * 4 * 3 * factorial(2)
= 5 * 4 * 3 * 2 * factorial(1)
= 5 * 4 * 3 * 2 * 1 # factorial(1) 返回 1,递归结束
= 120
集合 (Sets)
- 集合 (Set) 是 无序 且 唯一 的元素集合。
- 集合中的元素用逗号分隔,花括号
{}
括起来。 - 集合是 可变 的,但集合中的元素必须是 不可变 的数据类型 (例如,数字、字符串、元组)。
- 集合 不包含重复元素。
info = {"张三", 20, False, 5.9} # 集合示例
print(info) # 输出的顺序可能与定义的顺序不同,因为集合是无序的
- 集合是无序的,因此不能使用索引访问元素。
- 集合的应用场景:例如,在办公室检查哪些员工没有收到礼物,需要收集员工姓名,并去重。
集合操作: add
, union
, difference
, intersection
, update
, intersection_update
, symmetric_difference
, discard
(不报错), remove
(报错)
集合的连接操作 (Joining operations in sets):
-
union()
和update()
: 并集- 返回两个集合中所有元素的并集。
update()
方法将另一个集合的元素添加到现有集合中。
cities1 = {"东京", "马德里", "柏林", "德里"} cities2 = {"东京", "首尔", "喀布尔", "马德里"} cities3 = cities1.union(cities2) # 返回 cities1 和 cities2 的并集,创建新集合 cities3 print(cities3) # 输出: {'东京', '马德里', '柏林', '德里', '首尔', '喀布尔'} cities1.update(cities2) # 将 cities2 的元素添加到 cities1 中,修改 cities1 本身 print(cities1) # 输出: {'东京', '马德里', '柏林', '德里', '首尔', '喀布尔'}
-
intersection()
和intersection_update()
: 交集- 返回两个集合中共同的元素 (交集)。
cities3 = cities1.intersection(cities2) # 返回 cities1 和 cities2 的交集,创建新集合 cities3 print(cities3) # 输出: {"马德里", "东京"} cities1.intersection_update(cities2) # 将 cities1 更新为 cities1 和 cities2 的交集,修改 cities1 本身 print(cities1) # 输出: {"马德里", "东京"}
-
symmetric_difference()
和symmetric_difference_update()
: 对称差集- 返回两个集合中不相同的元素 (对称差集)。
cities1 = {"东京", "马德里", "柏林", "德里"} cities2 = {"东京", "首尔", "喀布尔", "马德里"} print(cities1.symmetric_difference(cities2)) # 返回 cities1 和 cities2 的对称差集,创建新集合 # 输出: {"首尔", "喀布尔", "柏林", "德里"}
-
difference()
和difference_update()
: 差集- 返回集合间的差集。例如
cities1.difference(cities2)
返回cities1
中存在,但cities2
中不存在的元素。
- 返回集合间的差集。例如
集合方法 (Set methods):
-
isdisjoint(other_set)
:- 检查当前集合与
other_set
是否不相交 (没有共同元素)。 - 如果不相交,返回
True
,否则返回False
。
cities1 = {"东京", "马德里"} cities2 = {"首尔", "喀布尔"} is_disjoint = cities1.isdisjoint(cities2) print(is_disjoint) # 输出: True (因为 cities1 和 cities2 没有共同元素)
- 检查当前集合与
-
issuperset(other_set)
:- 检查当前集合是否是
other_set
的超集 (包含other_set
的所有元素)。 - 如果是超集,返回
True
,否则返回False
。
cities1 = {"东京", "马德里", "柏林"} cities2 = {"东京", "马德里"} is_superset = cities1.issuperset(cities2) print(is_superset) # 输出: True (因为 cities1 包含 cities2 的所有元素)
- 检查当前集合是否是
-
issubset(other_set)
: 检查当前集合是否是other_set
的子集。 -
add(item)
: 向集合中添加元素item
。cities1 = {"东京", "马德里"} cities1.add("赫尔辛基") # 向 cities1 集合添加 "赫尔辛基" 元素 print(cities1) # 输出 (顺序可能不同): {'东京', '马德里', '赫尔辛基'}
-
update(other_set)
: 将另一个集合other_set
中的元素添加到当前集合。cities1 = {"东京", "马德里"} cities2 = {"柏林", "首尔"} cities1.update(cities2) # 将 cities2 的元素添加到 cities1 print(cities1) # 输出 (顺序可能不同): {'东京', '马德里', '柏林', '首尔'}
-
remove(item)
/discard(item)
: 从集合中删除元素item
。-
remove(item)
: 如果元素item
不存在,会抛出 KeyError 错误。cities1 = {"东京", "马德里"} cities1.remove("东京") # 删除 "东京" 元素 print(cities1) # 输出: {'马德里'} # cities1.remove("北京") # 如果 "北京" 不在 cities1 中,会抛出 KeyError 错误
-
discard(item)
: 如果元素item
不存在,不会报错,安全删除。cities2 = {"东京", "首尔"} cities2.discard("东京") # 删除 "东京" 元素 print(cities2) # 输出: {'首尔'} cities2.discard("北京") # 如果 "北京" 不在 cities2 中,不会报错
-
-
pop()
: 随机删除并返回集合中的一个元素。由于集合是无序的,所以删除的是“随机”的元素。 -
del set_name
: 删除整个集合。 -
clear()
: 清空集合中的所有元素,集合变为空集。
字典 (Python Dictionaries)
- 字典 (Dictionary) 是 Python 中的 有序 (Python 3.7+ 版本开始有序) 键值对集合。
- 字典用于存储 键 (Key) - 值 (Value) 对,键必须是 唯一 且 不可变 的数据类型 (例如,字符串、数字、元组),值可以是任意数据类型。
- 字典中的键值对用逗号分隔,花括号
{}
括起来。
info = {"姓名": "张三", "年龄": 19, "语言": "Python"} # 字典示例
-
访问单个值 (Accessing single values):
name = info['姓名'] # 使用键 '姓名' 访问对应的值 print(name) # 输出: '张三' language = info.get('语言') # 使用 get() 方法,更安全,如果键不存在返回 None 或默认值 print(language) # 输出: 'Python'
-
访问多个值和键 (Access multiple values and keys):
values = info.values() # 获取字典中所有值的视图对象 print(values) # 输出: dict_values(['张三', 19, 'Python']) keys = info.keys() # 获取字典中所有键的视图对象 print(keys) # 输出: dict_keys(['姓名', '年龄', '语言'])
-
访问键值对 (Access key-value pairs): 使用
items()
方法遍历键值对。for key, value in info.items(): # items() 方法返回键值对的视图对象 print(f"{key}: {value}") # 使用 f-字符串格式化输出 # 输出: # 姓名: 张三 # 年龄: 19 # 语言: Python
字典方法 (Dictionary methods):
ep1 = {122: 45, 123: 89, 567: 69, 670: 69}
ep2 = {222: 67, 566: 90}
ep1.update(ep2) # 将 ep2 中的键值对添加到 ep1 中,如果键已存在则更新值
print(ep1) # 输出: {122: 45, 123: 89, 567: 69, 670: 69, 222: 67, 566: 90}
ep1.clear() # 清空字典 ep1
print(ep1) # 输出: {}
ep1 = {122: 45, 123: 89}
removed_value = ep1.pop(122) # 删除键为 122 的键值对,并返回对应的值 45
print(ep1) # 输出: {123: 89}
print(removed_value) # 输出: 45
ep1 = {122: 45, 123: 89}
removed_item = ep1.popitem() # 删除并返回字典的最后一个键值对 (Python 3.7+ 版本中字典是有序的,删除的是最后添加的)
print(ep1) # 输出: {122: 45} (假设 123: 89 是最后添加的)
print(removed_item) # 输出: (123, 89)
ep1 = {122: 45, 123: 89}
del ep1[122] # 删除键为 122 的键值对
print(ep1) # 输出: {123: 89}
-
update(other_dict)
: 更新字典,将other_dict
中的键值对添加到当前字典。如果键已存在,则更新值;如果键不存在,则添加新的键值对。info = {'年龄': 19} info.update({'年龄': 20}) # 更新键 '年龄' 的值,19 -> 20 print(info) # 输出: {'年龄': 20} info.update({'出生年份': 2003}) # 添加新的键值对 '出生年份': 2003 print(info) # 输出: {'年龄': 20, '出生年份': 2003}
-
clear()
: 清空字典中的所有键值对,使字典变为空字典。 -
pop(key)
: 删除指定键key
的键值对,并返回对应的值。如果键不存在,会抛出 KeyError 错误。 -
popitem()
: 删除并返回字典的最后一个键值对 (Python 3.7+ 版本中字典是有序的,删除的是最后添加的)。 -
del dict[key]
: 删除指定键key
的键值对。也可以使用del dict_name
删除整个字典。注意:
del dict[key]
和pop(key)
的区别:pop(key)
会返回被删除的值!
for...else
循环 (For loop with else)
for x in range(5):
print(f"{x} 数字")
else:
print("for 循环结束,进入 else 块") # for 循环正常结束后,执行 else 块
x = 0
while x < 7:
print(x)
x += 1
else:
print("while 循环结束,进入 else 块") # while 循环条件变为 False 后,执行 else 块
else
块出现在循环体之后。else
块在循环正常结束 (即,不是通过break
语句跳出循环) 后执行。- 程序只有在循环的所有迭代都完成后才会退出循环并执行
else
块。
异常处理 (Exception Handling)
try:
num = int(input("请输入一个整数: "))
a = [6, 3]
print(a[num]) # 尝试访问列表 a 的索引 num 的元素
except IndexError:
print("索引错误: 列表索引超出范围") # 捕获 IndexError 异常
except ValueError:
print("值错误:输入的不是整数") # 捕获 ValueError 异常
a_str = input("请输入一个数字: ")
print(f"{a_str} 的乘法表是:")
try:
a = int(a_str)
for i in range(1, 11):
print(f"{a} x {i} = {a * i}")
except ValueError:
print("无效输入:请输入有效的数字") # 捕获 ValueError 异常,处理非数字输入的情况
- 异常处理是一种处理程序运行时错误 (异常) 的机制。
- 避免程序因错误而崩溃。
finally
子句 (Finally clause)
finally
子句用于定义无论是否发生异常都 始终 执行的代码块 (例如,关闭文件、关闭数据库连接、程序结束时输出提示信息)。
使用 finally
示例:
try:
result = 10 / 0 # 可能会抛出 ZeroDivisionError 异常
except ZeroDivisionError:
print("不能除以零。") # 捕获 ZeroDivisionError 异常并处理
finally:
print("finally 块总是会被执行。") # finally 块中的代码始终会被执行
在这个例子中:
- 如果发生异常 (例如,除以零),
except
块会处理异常。 - 无论是否发生异常,
finally
块都会被执行。这确保了清理代码或重要的最后步骤始终能够执行。
不使用 finally
的情况:
try:
result = 10 / 0
except ZeroDivisionError:
print("不能除以零。")
# 没有 `finally` 块,如果发生异常,这行代码可能不会执行
print("如果发生异常,这行代码可能不会执行。")
在这种情况下,try-except
块之后的代码 (print("如果发生异常,这行代码可能不会执行。")
) 只有在 没有 异常抛出时才会执行。如果捕获到异常,except
块之后的代码不会被执行。
自定义异常 (Custom Errors)
a = int(input("请输入 5 到 9 之间的值: "))
if a < 5 or a > 9:
raise ValueError("值应该在 5 到 9 之间 :)") # 使用 raise 关键字抛出自定义 ValueError 异常
- 使用
raise
关键字创建和抛出自定义异常。 - 当需要在特定异常发生时执行特定操作时很有用 (例如,发送错误报告给管理员、调用 API 等)。
简写 if-else
语句 (Short-hand if-else):
a = 330000
b = 3303
print("A") if a > b else print("=") if a == b else print("B") # 简写 if-elif-else 结构
c = 9 if a > b else 0 # 基于条件赋值
result = value_if_true if condition else value_if_false # 通用简写 if-else 结构
- 用于简单的
if-else
语句,特别是在需要根据条件为变量赋值时。
enumerate()
函数 (Enumerate)
marks = [12, 56, 32, 98, 12, 45, 1, 4]
index = 0
for mark in marks:
print(mark)
if index == 3:
print("真棒!")
index += 1
for index, mark in enumerate(marks, start=1): # enumerate() 函数同时返回索引和值,start=1 指定索引起始值
print(index, mark)
if index == 3:
print("真棒!")
enumerate()
函数允许在遍历序列 (列表、元组、字符串) 时,同时获取元素的索引和值。- 当需要同时操作索引和值时非常有用。
fruits = ['苹果', '香蕉', '芒果']
for index, fruit in enumerate(fruits, start=1): # 索引从 1 开始
print(index, fruit)
# 输出:
1 苹果
2 香蕉
3 芒果
虚拟环境 (Virtual Environment)
-
虚拟环境 (Virtual Environment) 用于隔离 Python 项目的依赖环境,以便在同一台机器上开发多个项目,避免不同项目之间包版本冲突。
-
例如:项目 1 使用 OpenCV 版本 4.9.0,而另一个项目使用 OpenCV 版本 4.5。
-
常用命令:
python -m venv env # 创建名为 env 的虚拟环境 env/scripts/activate # (Windows) 激活虚拟环境 source env/bin/activate # (macOS/Linux) 激活虚拟环境 pip freeze > requirements.txt # 将当前环境的包列表导出到 requirements.txt 文件 pip install -r requirements.txt # 从 requirements.txt 文件安装包 deactivate # 退出虚拟环境
-
方便在云端或新机器上快速部署项目环境。
-
检查包版本示例:
import pandas as pd print(pd.__version__)
import
语句的工作原理 (How Import Works)
-
从
math
模块导入sqrt
函数并重命名为s
:from math import sqrt as s # 从 math 模块导入 sqrt 函数并重命名为 s import math as math_builtin # 导入 math 模块并重命名为 math_builtin result = math_builtin.pi * s(9) # 使用导入的模块和函数 print(result)
-
my_module.py
(自定义模块文件):def welcome(): print("欢迎!") module_variable = "模块变量"
-
main.py
(主程序文件):from my_module import welcome # 从 my_module 模块导入 welcome 函数 welcome() # 调用 my_module 模块中的 welcome 函数
-
import
语句用于将模块 (module) 中的代码加载到当前脚本中,允许使用模块中定义的函数、类和变量。 -
导入
math
模块的所有内容:from math import * # 导入 math 模块的所有内容 (不推荐,可能导致命名空间污染)
-
重命名导入的模块:
import math as m # 将 math 模块重命名为 m
-
列出模块的方法和属性:
import math print(dir(math)) # 使用 dir() 函数查看模块的属性和方法列表
if __name__ == "__main__":
语句 (if name == "main")
if __name__ == "__main__":
是 Python 脚本中常见的模式,用于判断脚本是被直接运行还是作为模块被导入到其他脚本中。
def main():
print("脚本正在直接运行")
if __name__ == "__main__": # 当脚本被直接运行时,__name__ 的值为 "__main__"
main() # 直接运行时执行 main() 函数
- 这种模式允许代码既可以作为脚本直接运行,也可以作为模块被导入到其他脚本中使用,而不会在导入时执行脚本中的代码。
- 有助于组织代码,区分可以直接运行的代码和作为模块导入的代码。
好的,我们继续深入 Python 编程的学习之旅。
OS 模块 (OS Module)
import os
if not os.path.exists("data"): # 检查 "data" 目录是否存在
os.mkdir("data") # 如果不存在,则创建 "data" 目录
for i in range(0, 100):
os.makedirs(f"data/Day{i+1}", exist_ok=True) # 创建 "data/Day1", "data/Day2", ... "data/Day100" 等目录,exist_ok=True 表示目录已存在时不会报错
os
模块是 Python 内置的操作系统接口模块,提供了许多与操作系统交互的函数。- 可以进行文件和目录操作,执行系统命令等。
try:
f = os.open("file.txt", os.O_RDONLY) # 以只读模式打开文件 "file.txt"
g = os.open("gfile.txt", os.O_WRONLY | os.O_CREAT) # 以只写模式打开文件 "gfile.txt",如果文件不存在则创建
contents = os.read(f, 1024) # 从文件描述符 f 读取最多 1024 字节的内容
os.write(g, b"你好,世界!") # 向文件描述符 g 写入字节字符串
os.close(f) # 关闭文件描述符 f
os.close(g) # 关闭文件描述符 g
except FileNotFoundError:
print("文件未找到")
except Exception as e:
print(f"发生错误: {e}")
-
os.listdir(path)
-> 获取指定目录path
下的文件和目录列表。files = os.listdir(".") # 获取当前目录下的文件和目录列表 print(files)
-
os.mkdir(path)
-> 创建一个目录path
。 -
os.makedirs(path, exist_ok=False)
-> 递归创建目录path
,可以创建多级目录。exist_ok=True
表示如果目录已存在,不会抛出异常。 -
运行系统命令:
os.system("date") # 在终端中执行 "date" 命令 (显示当前日期和时间)
局部变量与全局变量 (Local & Global Variable)
x = 10 # 全局变量 x
def my_function():
global x # 声明在函数内部使用的是全局变量 x
x = 5 # 修改全局变量 x 的值
y = 5 # 局部变量 y,只在函数内部有效
my_function()
print(x) # 输出: 5 (全局变量 x 的值被函数修改)
# print(y) # 报错: NameError: name 'y' is not defined (局部变量 y 在函数外部不可访问)
注意: 尽量避免在函数内部直接修改全局变量,容易导致代码维护性降低。
文件输入/输出 (File I/O)
try:
f = open('myfile.txt', 'r', encoding='utf-8') # 'r' -> 读取模式,encoding='utf-8' 指定编码
print(f) # 打印文件对象信息
text = f.read() # 读取文件所有内容
print(text) # 打印文件内容
except FileNotFoundError:
print("文件未找到")
finally:
if 'f' in locals() and f and not f.closed: # 检查文件对象 f 是否已成功打开并存在
f.close() # 确保文件被关闭
文件写入 (Writing to a File)
try:
f = open('myfile.txt', 'a', encoding='utf-8') # 'a' -> 追加模式,encoding='utf-8' 指定编码
f.write('你好,世界!\n') # 写入字符串到文件末尾,\n 表示换行
except Exception as e:
print(f"写入文件时发生错误: {e}")
finally:
if 'f' in locals() and f and not f.closed:
f.close()
# 使用 with 语句,自动管理文件关闭
with open('myfile.txt', 'a', encoding='utf-8') as f:
f.write("这是使用 with 语句写入的内容。\n") # 文件操作结束后,文件会自动关闭
文件模式 (Modes):
- 读取 (r): 打开文件用于读取,如果文件不存在则抛出
FileNotFoundError
异常。 - 写入 (w): 打开文件用于写入,如果文件不存在则创建新文件;如果文件已存在,则清空文件内容。
- 追加 (a): 打开文件用于追加写入,如果文件不存在则创建新文件;如果文件已存在,则在文件末尾追加内容。
- 创建 (x): 创建新文件,如果文件已存在则抛出
FileExistsError
异常。 - 文本模式 (t): 默认模式,处理文本文件,例如读取和写入字符串。
- 二进制模式 (b): 用于处理二进制文件,例如图片、PDF 等。
注意: 使用
with open(...) as f:
语句可以确保文件在使用完毕后自动关闭,即使发生异常也能保证文件资源被释放。
Lambda 函数 (Lambda Functions)
def apply_func(func, value): # func 参数接收一个函数
return 6 + func(value)
double = lambda x: x * 2 # 定义 lambda 函数 double,返回输入 x 的两倍
cube = lambda x: x ** 3 # 定义 lambda 函数 cube,返回输入 x 的立方
average = lambda x, y, z: (x + y + z) / 3 # 定义 lambda 函数 average,计算三个数的平均值
print(double(5)) # 输出: 10
print(apply_func(lambda x: x ** 2, 2)) # 输出: 10 (将 lambda 函数 x**2 作为参数传递给 apply_func)
- Lambda 函数 (匿名函数) 是一种简洁的函数定义方式,适用于简单的、功能单一的函数。
- 语法:
lambda arguments: expression
(lambda 关键字,参数列表,冒号,返回值表达式)
示例:
def multiply(x, y):
return x * y
# Lambda 函数等价形式
lambda x, y: x * y
lambda x, y: print(f"{x} * {y} = {x * y}") # Lambda 函数也可以包含 print 等语句
Map, Filter, Reduce
- Map, Filter, Reduce 是 Python 中常用的高阶函数 (Higher-order functions),它们接收函数作为参数,并对序列 (或其他可迭代对象) 进行操作。
Map
map(function, iterable)
: 将函数function
应用于可迭代对象iterable
的每个元素,返回一个新的迭代器,包含每次函数调用的结果。
numbers = [1, 2, 3, 4, 5]
doubled_numbers = map(lambda x: x * 2, numbers) # 使用 map 函数和 lambda 函数将 numbers 列表中的每个元素乘以 2
print(list(doubled_numbers)) # 输出: [2, 4, 6, 8, 10] (将迭代器转换为列表输出)
Filter
filter(function, iterable)
: 使用函数function
(返回布尔值) 过滤可迭代对象iterable
的元素,返回一个新的迭代器,包含function
返回True
的元素。
numbers = [1, 2, 3, 4, 5]
even_numbers = filter(lambda x: x % 2 == 0, numbers) # 使用 filter 函数和 lambda 函数筛选 numbers 列表中的偶数
print(list(even_numbers)) # 输出: [2, 4] (将迭代器转换为列表输出)
Reduce
reduce(function, iterable[, initializer])
: 将函数function
(接收两个参数) 累积地应用于可迭代对象iterable
的元素,将序列缩减为单个返回值。需要从functools
模块导入。
from functools import reduce
numbers = [1, 2, 3, 4, 5]
sum_numbers = reduce(lambda x, y: x + y, numbers) # 使用 reduce 函数和 lambda 函数计算 numbers 列表中所有元素的和
print(sum_numbers) # 输出: 15
is
vs ==
a = None
b = None
print(a is b) # True: a 和 b 都指向 None 对象,是同一个对象 (身份相同)
print(a is None) # True: a 是否是 None 对象 (身份比较)
print(a == b) # True: a 和 b 的值相等 (值比较)
is
和==
都是 Python 中的比较运算符,但它们比较的内容不同。is
: 比较的是 身份 (identity),即判断两个变量是否引用同一个对象 (内存地址是否相同)。==
: 比较的是 值 (value),即判断两个变量的值是否相等。
示例:
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True: a 和 b 的值相等 (元素相同,顺序相同)
print(a is b) # False: a 和 b 是不同的列表对象,它们在内存中位于不同的位置 (身份不同)
注意: 对于不可变对象 (如字符串、小整数),Python 可能会进行对象缓存或 intern 机制,导致值相同的不同变量可能指向同一个对象,此时
is
和==
的结果可能相同,但一般情况下,应该使用==
进行值比较,使用is
比较对象身份,例如判断变量是否为None
。
面向对象编程 (OOPS)
OOP 的四大支柱 (4 Pillars of OOP)
- 继承 (Inheritance): 属性共享,子类继承父类的属性和方法。
- 封装 (Encapsulation): 数据隐藏,将数据和操作数据的方法封装在对象内部,对外隐藏实现细节。
- 抽象 (Abstraction): 简化接口,只对外暴露必要的接口,隐藏复杂的内部实现。
- 多态 (Polymorphism): 多种形态,允许不同类的对象对同一消息做出不同的响应。
### 面向对象编程 (Object-Oriented Programming)
- 类 (Class): 定义对象的蓝图或模板,描述对象的属性和行为。
- 属性 (Properties): 对象的数据或状态 (成员变量)。
- 方法 (Methods): 对象可以执行的操作 (成员函数)。
- 对象 (Object): 类的实例,包含类定义的属性和方法。每个对象都有自己的数据和方法。
## 封装 (Encapsulation)
- 封装 (Encapsulation) 的目的是隐藏对象的内部状态,并通过对象的方法来访问和修改状态。
- 保护对象的数据,防止外部代码意外修改对象的状态。
访问修饰符 (Access Specifiers)
Python 中通过命名约定来实现访问控制,没有像 Java 或 C++ 那样的强制访问修饰符。
- 公共成员 (Public Members): 默认情况下,类的所有成员都是公共的,可以在类外部访问。
- 受保护成员 (Protected Members): 以下划线
_
开头的成员,表示受保护的,建议在类内部和子类中使用,但不强制限制外部访问。 - 私有成员 (Private Members): 以双下划线
__
开头的成员,Python 会进行名称修饰 (name mangling),使其更难从外部直接访问,但并非完全无法访问。
抽象 (Abstraction)
抽象 (Abstraction) 的目的是隐藏功能的内部实现细节,只对外暴露必要的接口。用户只需要知道如何使用接口,而不需要了解接口内部的复杂实现。
多态 (Polymorphism)
- 多态 (Polymorphism) 允许不同类的对象对同一消息 (方法调用) 做出不同的响应。
- 提高代码的灵活性和可扩展性。
class Animal:
def speak(self):
pass # 父类 Animal 的 speak 方法默认不做任何事情
class Dog(Animal):
def speak(self):
return "汪汪!" # 子类 Dog 重写 speak 方法,返回 "汪汪!"
class Cat(Animal):
def speak(self):
return "喵喵!" # 子类 Cat 重写 speak 方法,返回 "喵喵!"
class Bird(Animal):
def speak(self):
return "啾啾!" # 子类 Bird 重写 speak 方法,返回 "啾啾!"
# 演示多态性的函数
def make_animal_speak(animal):
print(animal.speak()) # 调用传入对象的 speak 方法,根据对象类型的不同,执行不同的 speak 方法
# 创建不同动物类的实例
dog = Dog()
cat = Cat()
bird = Bird()
# 演示多态性
make_animal_speak(dog) # 输出: 汪汪! (调用 Dog 类的 speak 方法)
make_animal_speak(cat) # 输出: 喵喵! (调用 Cat 类的 speak 方法)
make_animal_speak(bird) # 输出: 啾啾! (调用 Bird 类的 speak 方法)
类 (Classes)
类 (Class) 是创建对象的蓝图或模板,定义了对象的属性 (数据) 和方法 (行为)。
class Details:
name = "张三" # 类属性 name
age = 20 # 类属性 age
对象:类的实例 (Object: Instance of Class)
obj1 = Details() # 创建 Details 类的对象 obj1
print(obj1.name) # 输出: 张三 (访问对象 obj1 的 name 属性)
print(obj1.age) # 输出: 20 (访问对象 obj1 的 age 属性)
self
参数 (Self Parameter)
self
参数在类的方法定义中是必需的,它指向类的当前实例 (对象) 本身。- 通过
self
可以访问对象的属性和调用对象的方法。
class Details:
name = "张三"
age = 20
def describe_person(self): # 类方法 describe_person,self 参数指向类的实例
print("我的名字是", self.name, ",今年", self.age, "岁。") # 通过 self 访问对象的 name 和 age 属性
obj1 = Details() # 创建 Details 类的对象 obj1
obj1.describe_person() # 调用对象 obj1 的 describe_person 方法
示例 (Example)
class Person:
name = "默认姓名" # 类属性 name
occupation = "程序员" # 类属性 occupation
net_worth = 10 # 类属性 net_worth
def info(self): # 类方法 info
print(f"{self.name} 是一名 {self.occupation}") # 使用 f-字符串格式化输出
a = Person() # 创建 Person 类的对象 a
b = Person() # 创建 Person 类的对象 b
c = Person() # 创建 Person 类的对象 c
a.name = "李四" # 修改对象 a 的 name 属性
a.occupation = "会计" # 修改对象 a 的 occupation 属性
b.name = "王五" # 修改对象 b 的 name 属性
b.occupation = "人力资源" # 修改对象 b 的 occupation 属性
a.info() # 输出: 李四 是一名 会计 (调用对象 a 的 info 方法)
b.info() # 输出: 王五 是一名 人力资源 (调用对象 b 的 info 方法)
c.info() # 输出: 默认姓名 是一名 程序员 (调用对象 c 的 info 方法,使用默认属性值)
构造函数 (__init__
)
- 构造函数 (
__init__
方法) 是类中的特殊方法,用于创建和初始化类的对象。 - 当创建类的对象时,构造函数会自动被调用。
- 主要作用是为对象的属性赋初始值。
def __init__(self):
# __init__ -> Python 中的保留函数名,用于定义构造函数
pass # pass 语句表示空代码块,什么也不做
-
参数化构造函数 (Parameterized Constructors):
- 构造函数接收除了
self
之外的其他参数。 - 这些参数用于在创建对象时为对象的属性赋值。
class Details: def __init__(self, animal_name, animal_group): # 构造函数接收 animal_name 和 animal_group 参数 self.animal = animal_name # 将 animal_name 赋值给对象的 animal 属性 self.group = animal_group # 将 animal_group 赋值给对象的 group 属性 obj1 = Details("狮子", "食肉动物") # 创建 Details 类的对象 obj1,并传入参数 "狮子" 和 "食肉动物" print(obj1.animal, "属于", obj1.group) # 输出: 狮子 属于 食肉动物 (访问对象 obj1 的 animal 和 group 属性)
- 构造函数接收除了
-
默认构造函数 (Default Constructors):
- 构造函数只接收
self
参数,不接收其他参数。 - 如果类中没有显式定义构造函数,Python 会自动提供一个默认的构造函数 (不执行任何操作)。
class Details: def __init__(self): # 默认构造函数,只接收 self 参数 print("动物:狮子,食肉动物") obj1 = Details() # 创建 Details 类的对象 obj1,调用默认构造函数 # 输出: 动物:狮子,食肉动物 (构造函数中的 print 语句被执行)
- 构造函数只接收
装饰器 (Decorators)
-
Python 装饰器 (Decorators) 是一种强大的工具,用于修改函数或方法的行为,而无需修改其源代码。
-
装饰器本质上是一个函数,它接收一个函数作为参数,并返回一个新的函数,新函数通常会在原函数的基础上增加一些功能。
-
新返回的函数被称为“被装饰”的函数。
@decorator_function # 使用 @ 符号应用装饰器 decorator_function 到 my_function 函数 def my_function(): pass
def smart_divide(func): # 装饰器函数 smart_divide,接收一个函数 func 作为参数 def inner(a, b): # 内部函数 inner,接收参数 a 和 b print("我要计算", a, "除以", b) # 打印提示信息 if b == 0: print("不能除以零") # 如果除数为 0,打印错误信息 return # 提前返回,不执行原函数 return func(a, b) # 调用原函数 func 并返回结果 return inner # 返回内部函数 inner @smart_divide # 使用 @smart_divide 装饰 divide 函数 def divide(a, b): # 被装饰的函数 divide print(a / b) # 执行除法运算并打印结果 divide(2, 5) # 调用被装饰的 divide 函数 # 输出: # 我要计算 2 除以 5 # 0.4 divide(2, 0) # 调用被装饰的 divide 函数,除数为 0 # 输出: # 我要计算 2 除以 0 # 不能除以零
Getter 方法 (Getters)
- Getter 方法 (取值器) 用于访问对象属性的值。
- 通常使用
@property
装饰器定义,用于返回特定属性的值。
class MyClass:
def __init__(self, value):
self._value = value # 使用 _value 存储实际的值,以区分 getter 方法名
@property # 使用 @property 装饰器将 value 方法定义为属性 getter
def value(self): # 定义 getter 方法 value,方法名即属性名
return self._value # 返回 _value 的值
obj = MyClass(10) # 创建 MyClass 类的对象 obj,初始化 _value 为 10
print(obj.value) # 输出: 10 (访问 value 属性,实际调用 getter 方法)
注意: Getter 方法不接收参数,并且不能通过 getter 方法直接设置属性的值。
Setter 方法 (Setters)
class MyClass:
def __init__(self, value):
self._value = value
@property
def value(self): # getter 方法
return self._value
@value.setter # 使用 @value.setter 装饰器定义 value 属性的 setter 方法
def value(self, new_val): # setter 方法名必须与 getter 方法名相同
if new_val < 0:
raise ValueError("值不能为负数") # 添加数据验证逻辑
self._value = new_val # 设置 _value 的新值
obj = MyClass(10) # 创建 MyClass 类的对象 obj,初始化 _value 为 10
obj.value = 20 # 设置 value 属性的值,实际调用 setter 方法
print(obj.value) # 输出: 20 (访问 value 属性,实际调用 getter 方法)
# obj.value = -5 # 会抛出 ValueError 异常,因为 setter 方法中添加了数据验证
注意: Setter 方法与 getter 方法配合使用,可以实现对属性的受控访问,用于数据封装和数据验证。Setter 方法通常用于在设置属性值之前进行一些验证或处理。
继承 (Inheritance)
- 继承 (Inheritance) 是面向对象编程的重要特性,允许一个类 (子类或派生类) 继承另一个类 (父类或基类) 的属性和方法。
- 子类可以复用父类的代码,并在此基础上进行扩展或修改。
- 提高了代码的重用性和可维护性。
继承的类型 (Types of Inheritance):
-
单继承 (Single Inheritance)
-
多继承 (Multiple Inheritance)
-
多层继承 (Multilevel Inheritance)
-
层次继承 (Hierarchical Inheritance)
-
混合继承 (Hybrid Inheritance)
单继承 (Single Inheritance)
- 子类只继承一个父类的属性和方法。
class Parent: # 父类 Parent
def func1(self):
print("父类方法")
class Child(Parent): # 子类 Child 继承自 Parent 类
def func2(self):
print("子类方法")
obj = Child() # 创建 Child 类的对象 obj
obj.func1() # 输出: 父类方法 (子类对象可以调用父类的方法)
obj.func2() # 输出: 子类方法 (子类对象可以调用子类自身的方法)
多继承 (Multiple Inheritance)
- 子类继承多个父类的属性和方法。
class Mother: # 父类 Mother
mother_name = ""
def mother(self):
print("母亲姓名:", self.mother_name)
class Father: # 父类 Father
father_name = ""
def father(self):
print("父亲姓名:", self.father_name)
class Son(Mother, Father): # 子类 Son 多继承自 Mother 和 Father 类
def parents(self):
print("父亲姓名:", self.father_name)
print("母亲姓名:", self.mother_name)
s1 = Son() # 创建 Son 类的对象 s1
s1.father_name = "老爸" # 设置父类 Father 的属性
s1.mother_name = "老妈" # 设置父类 Mother 的属性
s1.parents() # 调用子类 Son 的 parents 方法
多层继承 (Multilevel Inheritance)
- 形成继承链,子类继承父类,父类又继承自更高级别的父类。
class Grandfather: # 祖父类
def __init__(self, grandfather_name):
self.grandfather_name = grandfather_name
class Father(Grandfather): # 父类 Father 继承自 Grandfather
def __init__(self, father_name, grandfather_name):
Grandfather.__init__(self, grandfather_name) # 调用父类 Grandfather 的构造函数
self.father_name = father_name
class Son(Father): # 子类 Son 继承自 Father
def __init__(self, father_name, grandfather_name, son_name):
Father.__init__(self, father_name, grandfather_name) # 调用父类 Father 的构造函数
self.son_name = son_name
def print_name(self):
print("祖父姓名:", self.grandfather_name)
print("父亲姓名:", self.father_name)
print("儿子姓名:", self.son_name)
s1 = Son("王子", "老王", "小王") # 创建 Son 类的对象 s1,并初始化各个父类的属性
s1.print_name() # 调用子类 Son 的 print_name 方法
层次继承 (Hierarchical Inheritance)
- 多个子类继承自同一个父类。
class Parent: # 父类 Parent
def func1(self):
print("父类方法")
class Child1(Parent): # 子类 Child1 继承自 Parent
def func2(self):
print("子类 Child1 方法")
class Child2(Parent): # 子类 Child2 继承自 Parent
def func3(self):
print("子类 Child2 方法")
obj1 = Child1() # 创建 Child1 类的对象 obj1
obj2 = Child2() # 创建 Child2 类的对象 obj2
obj1.func1() # 输出: 父类方法 (Child1 对象可以调用父类方法)
obj2.func1() # 输出: 父类方法 (Child2 对象可以调用父类方法)
obj1.func2() # 输出: 子类 Child1 方法 (Child1 对象可以调用自身方法)
obj2.func3() # 输出: 子类 Child2 方法 (Child2 对象可以调用自身方法)
混合继承 (Hybrid Inheritance)
- 混合使用多种继承类型,例如多层继承和多继承的组合。
class School: # 基类 School
def func1(self):
print("学校")
class Student1(School): # 子类 Student1 继承自 School
def func2(self):
print("学生 1")
class Student2(School): # 子类 Student2 继承自 School
def func3(self):
print("学生 2")
class Student3(Student1, School): # 子类 Student3 多继承自 Student1 和 School (注意继承顺序)
def func4(self):
print("学生 3 的方法")
obj1 = Student3() # 创建 Student3 类的对象 obj1
obj1.func1() # 输出: 学校 (Student3 对象可以调用 School 类的方法)
obj1.func2() # 输出: 学生 1 (Student3 对象可以调用 Student1 类的方法)
# obj1.func3() # Student3 类没有直接继承 Student2,无法调用 func3
obj1.func4() # 输出: 学生 3 的方法 (Student3 对象可以调用自身方法)
访问修饰符或访问控制 (Access Modifiers or Specifiers)
- 访问修饰符 (Access Modifiers) 用于控制类成员 (属性和方法) 的访问权限,限制从类外部访问类成员。
- Python 中主要通过命名约定实现访问控制。
类型 (Types):
- 公共访问修饰符 (Public Access Modifiers)
- 私有访问修饰符 (Private Access Modifiers)
- 受保护访问修饰符 (Protected Access Modifiers)
class Student:
def __init__(self):
self._name = "小明" # 受保护访问修饰符,以下划线开头
def _fun_name(self): # 受保护方法,以下划线开头
return "张老师"
class Subject(Student): # 子类 Subject 继承自 Student
pass
obj = Student() # 创建 Student 类的对象 obj
obj1 = Subject() # 创建 Subject 类的对象 obj1
print(dir(obj)) # 使用 dir() 函数查看对象 obj 的属性和方法列表,可以看到 _funName, __init__, _name 等
# 通过 Student 类的对象访问受保护成员
print(obj._name) # 访问受保护属性 _name
print(obj._fun_name()) # 调用受保护方法 _fun_name()
# 通过 Subject 类的对象访问受保护成员 (子类可以访问父类的受保护成员)
print(obj1._name) # 访问继承自父类的受保护属性 _name
print(obj1._fun_name()) # 调用继承自父类的受保护方法 _fun_name()
-
公共访问修饰符 (Public Access Modifier): 默认情况下,类的所有成员都是公共的,可以在类外部自由访问。
self.age = age
->age
属性是公共的。
-
私有访问修饰符 (Private Access Modifier): 以双下划线
__
开头的成员被视为私有成员,只能在类内部访问。- Python 中并没有严格的私有访问控制,以
__
开头的成员会被名称修饰 (name mangling),使其更难从外部直接访问,但仍然可以通过特殊方式访问。
- Python 中并没有严格的私有访问控制,以
class Student:
def __init__(self, age, name):
self.__age = age # 私有变量,以双下划线开头
def __fun_name(self): # 私有方法,以双下划线开头
self.y = 34
print(self.y)
class Subject(Student): # 子类 Subject 继承自 Student
pass
obj = Student(21, "小明") # 创建 Student 类的对象 obj
obj1 = Subject() # 创建 Subject 类的对象 obj1
# 以下代码会抛出 AttributeError 错误,因为无法直接从外部访问私有成员
# print(obj.__age) # AttributeError: 'Student' object has no attribute '__age'
# obj.__fun_name() # AttributeError: 'Student' object has no attribute '__fun_name'
# print(obj1.__age) # AttributeError: 'Subject' object has no attribute '__age'
# obj1.__fun_name() # AttributeError: 'Subject' object has no attribute '__fun_name'
# 通过名称修饰后的名称访问私有变量 (不推荐,破坏了封装性)
print(obj._Student__age) # 可以通过 _ClassName__member_name 的方式访问私有成员
如何访问私有变量?:
*obj._Student__name*
(例如,obj._Student__age
),但不推荐这样做,应该尽量避免直接访问私有成员,而是通过公共方法 (如 getter 和 setter) 来间接访问。
方法重写 (Method Overriding)
class Shape: # 父类 Shape
def __init__(self, x, y):
self.x = x
self.y = y
def area(self): # 父类方法 area,计算矩形面积
return self.x * self.y
class Circle(Shape): # 子类 Circle 继承自 Shape
def __init__(self, r):
self.r = r
super().__init__(r, r) # 调用父类 Shape 的构造函数,初始化 x 和 y 为 r
def area(self): # 子类 Circle 重写父类 area 方法,计算圆形面积
return 3.14 * super().area() # 调用父类 Shape 的 area 方法 (计算 r*r),再乘以 3.14
rect = Shape(3, 5) # 创建 Shape 类的对象 rect
print(rect.area()) # 输出矩形面积: 15 (调用父类 Shape 的 area 方法)
# 输出: 15
c = Circle(5) # 创建 Circle 类的对象 c
print(c.area()) # 输出圆形面积: 78.5 (调用子类 Circle 重写的 area 方法)
# 输出: 78.5
- 方法重写 (Method Overriding) 发生在继承关系中,子类可以重写 (override) 父类中已有的方法,提供自己的实现。
- 当创建子类的对象并调用被重写的方法时,实际执行的是子类中重写后的版本,而不是父类中的版本。
静态方法 (Static Methods)
静态方法 (Static Methods) 是属于类本身的方法,而不是类的实例 (对象)。
- 使用
@staticmethod
装饰器定义。 - 静态方法 不能访问 类的实例 (对象) 属性 (即不能使用
self
参数)。 - 静态方法通常用于创建与类相关,但不依赖于特定实例的实用工具函数。
class Math:
@staticmethod # 使用 @staticmethod 装饰器定义静态方法
def add(a, b): # 静态方法 add,不接收 self 参数
return a + b
result = Math.add(1, 2) # 直接通过类名 Math 调用静态方法 add
print(result) # 输出: 3
类变量 vs 实例变量 (Class vs Instance Variables)
-
类变量 (Class variables): 属于类本身的变量,被类的所有实例 (对象) 共享。
- 示例: 用于存储所有实例共享的通用信息。
-
实例变量 (Instance variables): 属于类的每个实例 (对象) 的变量,每个实例拥有独立的实例变量副本。
- 示例: 用于存储每个实例的特定信息。
访问变量 (Accessing Variables):
- 类变量:
ClassName.variable
或self.__class__.variable
(在实例方法中) - 实例变量:
self.variable_name
(在实例方法中)
class Animal:
species = "哺乳动物" # 类变量 species,所有 Animal 类的实例共享
def __init__(self, name):
self.name = name # 实例变量 name,每个 Animal 类的实例拥有独立的 name 属性
a1 = Animal("狗") # 创建 Animal 类的对象 a1
a2 = Animal("猫") # 创建 Animal 类的对象 a2
print(a1.species) # 输出: 哺乳动物 (访问对象 a1 的类变量 species)
print(a2.name) # 输出: 猫 (访问对象 a2 的实例变量 name)
Animal.species = "动物" # 修改类变量 Animal.species
print(a1.species) # 输出: 动物 (类变量被修改后,所有实例访问到的类变量都改变了)
print(a2.species) # 输出: 动物
练习:清理文件夹内的杂乱文件 (重命名 PNG 文件) (Exercise: Clear Clutter Inside Folder)
重命名指定文件夹下所有 PNG 图片文件,按照序号顺序命名 (例如,1.png
, 2.png
等)。
import os
folder_path = "clutter_folder" # 指定要清理的文件夹路径
files = os.listdir(folder_path) # 获取文件夹下所有文件和目录列表
i = 1 # 初始化序号计数器
for file in files:
if file.endswith(".png"): # 检查文件名是否以 ".png" 结尾
old_filepath = os.path.join(folder_path, file) # 构建旧文件完整路径
new_filepath = os.path.join(folder_path, f"{i}.png") # 构建新文件完整路径
os.rename(old_filepath, new_filepath) # 重命名文件
i += 1 # 序号递增
print("PNG 文件重命名完成!")
练习:图书馆类 (Library Class Exercise)
创建一个 Library
类,包含两个实例变量:no_of_books
(图书数量) 和 books
(图书列表)。实现添加图书和打印图书信息的功能。
class Library:
def __init__(self):
self.no_of_books = 0 # 初始化图书数量为 0
self.books = [] # 初始化图书列表为空列表
def add_book(self, book_title): # 添加图书方法
self.books.append(book_title) # 将图书标题添加到图书列表
self.no_of_books = len(self.books) # 更新图书数量
def show_info(self): # 显示图书馆信息方法
print(f"图书馆藏书数量: {self.no_of_books}") # 打印图书数量
if self.books: # 如果图书列表不为空
print("馆藏图书列表:")
for book in self.books: # 遍历图书列表
print(f"- {book}") # 打印每本图书标题
else:
print("图书馆暂无藏书。")
l1 = Library() # 创建 Library 类的对象 l1
l1.add_book("哈利·波特") # 添加图书
l1.add_book("霍比特人") # 添加图书
l1.show_info() # 显示图书馆信息
# 输出:
# 图书馆藏书数量: 2
# 馆藏图书列表:
# - 哈利·波特
# - 霍比特人
Python 类方法 (Python Class Methods)
- 类方法 (Class methods) 是绑定到类本身而不是类的实例 (对象) 的方法。
- 类方法操作的是类本身,而不是特定的实例。
- 使用
@classmethod
装饰器定义。 - 常用于创建工厂方法或作为备用构造函数。
class Employee:
company = "苹果公司" # 类变量 company
def show(self): # 实例方法 show
print(f"姓名: {self.name}, 公司: {self.company}")
@classmethod # 使用 @classmethod 装饰器定义类方法
def change_company(cls, new_company): # 类方法 change_company,cls 参数指向类本身
cls.company = new_company # 修改类变量 company
e1 = Employee() # 创建 Employee 类的对象 e1
e1.name = "小明" # 设置实例属性 name
e1.show() # 输出: 姓名: 小明, 公司: 苹果公司 (调用实例方法 show)
Employee.change_company("特斯拉") # 通过类名 Employee 调用类方法 change_company,修改类变量 company
e1.show() # 输出: 姓名: 小明, 公司: 特斯拉 (再次调用实例方法 show,类变量 company 已被修改)
类方法作为备用构造函数 (Class Methods as Alternative Constructors)
class Employee:
def __init__(self, name, salary): # 构造函数
self.name = name
self.salary = salary
@classmethod # 使用 @classmethod 装饰器定义类方法
def from_string(cls, employee_string): # 类方法 from_string,cls 参数指向类本身
name, salary = employee_string.split("-") # 从字符串中解析姓名和工资
return cls(name, int(salary)) # 使用 cls 调用类自身的构造函数,创建并返回类的新对象
e1 = Employee("小明", 1200) # 使用构造函数创建 Employee 对象
print(e1.name) # 输出: 小明
print(e1.salary) # 输出: 1200
e2 = Employee.from_string("李四-1500") # 使用类方法 from_string 创建 Employee 对象
print(e2.name) # 输出: 李四
print(e2.salary) # 输出: 1500
常用方法:dir()
, __dict__
, 和 help()
(Useful Methods: dir()
, __dict__
, and help()
)
dir(object)
: 返回对象object
的属性和方法列表 (包括特殊方法)。object.__dict__
: 返回对象object
的属性字典 (仅包含实例属性)。help(object)
: 显示对象object
的帮助文档。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("小明", 30) # 创建 Person 类的对象 p
print(p.__dict__) # 输出: {'name': '小明', 'age': 30} (查看对象 p 的实例属性字典)
print(dir(p)) # 输出: ['__class__', '__delattr__', ..., 'age', 'name'] (查看对象 p 的所有属性和方法)
help(Person) # 查看 Person 类的帮助文档 (docstring)
super()
关键字 (Super Keyword)
super()
函数用于调用父类 (超类) 的方法。- 在类继承中,子类经常需要扩展父类的行为,
super()
可以方便地调用父类的方法,并在其基础上添加新的逻辑。
class Employee: # 父类 Employee
def __init__(self, name, employee_id): # 父类构造函数
self.name = name
self.id = employee_id
class Programmer(Employee): # 子类 Programmer 继承自 Employee
def __init__(self, name, employee_id, language): # 子类构造函数
super().__init__(name, employee_id) # 调用父类 Employee 的构造函数,初始化 name 和 id
self.language = language # 初始化子类 Programmer 的属性 language
harry = Programmer("哈利", 23, "Python") # 创建 Programmer 类的对象 harry
print(harry.name) # 输出: 哈利 (访问继承自父类的属性 name)
print(harry.language) # 输出: Python (访问子类自身的属性 language)
Dunder (魔法) 方法 (Dunder (Magic) Methods)
- Dunder 方法 (Double Under Methods,也称为魔法方法或特殊方法) 是 Python 中以双下划线
__
开头和结尾的特殊方法。 - Dunder 方法用于自定义类的行为,例如运算符重载、对象字符串表示、长度计算等。
__init__(self, ...)
: 构造函数,在创建类的新实例时被调用 (初始化对象)。__str__(self)
&__repr__(self)
: 将对象转换为字符串表示,__str__
用于str()
函数和print()
函数,__repr__
用于repr()
函数和交互式环境输出。__len__(self)
: 返回对象的长度,用于len()
函数。__call__(self, ...)
: 使对象可以像函数一样被调用。
class Employee:
def __init__(self, name): # 构造函数
self.name = name
def __len__(self): # 定义 __len__ 方法,返回姓名长度
return len(self.name)
def __str__(self): # 定义 __str__ 方法,返回对象的友好的字符串表示
return f"员工: {self.name}"
def __repr__(self): # 定义 __repr__ 方法,返回对象的明确的字符串表示 (通常用于调试和开发)
return f"Employee('{self.name}')"
def __call__(self): # 定义 __call__ 方法,使对象可以像函数一样被调用
print(f"你好,我是 {self.name}")
e = Employee("小明") # 创建 Employee 类的对象 e
print(len(e)) # 输出: 2 (调用 __len__ 方法,返回姓名长度)
print(str(e)) # 输出: 员工: 小明 (调用 __str__ 方法,返回友好的字符串表示)
print(repr(e)) # 输出: Employee('小明') (调用 __repr__ 方法,返回明确的字符串表示)
e() # 输出: 你好,我是 小明 (将对象 e 作为函数调用,执行 __call__ 方法)
方法重写 (Method Overriding) (再次强调)
- 方法重写 (Method Overriding) 允许子类重新定义父类的方法,以实现不同的行为。
- 当调用子类对象的重写方法时,会执行子类中定义的版本。
class Shape: # 父类 Shape
def __init__(self, x, y):
self.x = x
self.y = y
def area(self): # 父类方法 area,计算矩形面积
return self.x * self.y
class Circle(Shape): # 子类 Circle 继承自 Shape
def __init__(self, r):
super().__init__(r, r) # 调用父类 Shape 的构造函数
def area(self): # 子类 Circle 重写父类 area 方法,计算圆形面积
return 3.14 * super().area() # 调用父类 Shape 的 area 方法 (计算 r*r),再乘以 3.14
rect = Shape(3, 5) # 创建 Shape 类的对象 rect
print(rect.area()) # 输出: 15 (调用父类 Shape 的 area 方法)
circle = Circle(5) # 创建 Circle 类的对象 circle
print(circle.area()) # 输出: 78.5 (调用子类 Circle 重写的 area 方法)
运算符重载 (Operator Overriding)
- 运算符重载 (Operator Overriding) 允许你自定义运算符 (如
+
,-
,*
,==
等) 对于自定义类对象的操作行为。 - 通过实现 dunder 方法 (如
__add__
,__sub__
,__mul__
,__eq__
等) 来重载运算符。
class Vector:
def __init__(self, i, j, k):
self.i = i
self.j = j
self.k = k
def __add__(self, other): # 重载 + 运算符,定义向量加法
return Vector(self.i + other.i, self.j + other.j, self.k + other.k) # 返回新的 Vector 对象,表示向量和
def __str__(self): # 重载 str() 函数,定义向量的字符串表示
return f"{self.i}i + {self.j}j + {self.k}k"
v1 = Vector(3, 5, 6) # 创建 Vector 对象 v1
v2 = Vector(1, 2, 9) # 创建 Vector 对象 v2
v3 = v1 + v2 # 使用 + 运算符进行向量加法,实际调用 __add__ 方法
print(v3) # 输出: 4i + 7j + 15k (打印向量 v3 的字符串表示,实际调用 __str__ 方法)
注意:
super()
关键字在继承中非常有用,可以扩展父类方法的行为,而不是完全替换它。在子类方法中,先使用super()
调用父类的方法,然后再添加子类特有的逻辑,是一种良好的编程实践。
练习:合并 PDF 文件为一个 PDF 文件 (Exercise: Merge PDFs into a Single PDF)
from PyPDF2 import PdfMerger # 导入 PdfMerger 类
import os
merger = PdfMerger() # 创建 PdfMerger 对象
pdf_files = [file for file in os.listdir() if file.endswith(".pdf")] # 获取当前目录下所有 PDF 文件列表
for pdf in pdf_files: # 遍历 PDF 文件列表
merger.append(pdf) # 将每个 PDF 文件添加到 PdfMerger 对象中
merger.write("Merged-pdf.pdf") # 将合并后的 PDF 内容写入到 "Merged-pdf.pdf" 文件
merger.close() # 关闭 PdfMerger 对象
print("PDF 文件合并完成!")
输出:
- 将当前目录下所有 PDF 文件合并为一个名为
Merged-pdf.pdf
的 PDF 文件。
Time 模块 (Time Module):
-
time
模块提供了一组函数,用于处理时间相关的操作,如时间获取、时间格式化、时间转换等。 -
常用函数 (Common Functions):
-
time.time()
: 返回当前时间的时间戳 (timestamp),即从 Epoch (1970 年 1 月 1 日 00:00:00 UTC) 到现在的秒数。示例 (Example):
import time start_time = time.time() # 获取当前时间戳 print("当前时间戳:", start_time)
-
time.sleep(seconds)
: 使程序暂停执行指定的秒数seconds
。示例 (Example):
import time print("开始") time.sleep(2) # 程序暂停 2 秒 print("2 秒后结束")
-
time.strftime(format[, t])
: 根据指定的格式format
将时间元组t
(可选,默认为当前时间) 格式化为字符串。示例 (Example):
import time t = time.localtime() # 获取当前时间的本地时间元组 formatted_time = time.strftime("%Y-%m-%d, %H:%M:%S", t) # 格式化时间为 "年-月-日, 时:分:秒" 字符串 print(formatted_time)
-
命令行实用工具 (Command Line Utility):
- 命令行程序 (Command-line programs) 对于自动化任务和提高开发工作效率至关重要。
基本示例 (Basic Example):
import argparse # 导入 argparse 模块
parser = argparse.ArgumentParser(description="这是一个示例命令行程序") # 创建 ArgumentParser 对象,并设置程序描述信息
parser.add_argument("arg1", help="第一个参数的描述") # 添加位置参数 arg1,并设置帮助信息
parser.add_argument("arg2", help="第二个参数的描述") # 添加位置参数 arg2,并设置帮助信息
args = parser.parse_args() # 解析命令行参数
print("参数 1:", args.arg1) # 访问解析后的参数 arg1
print("参数 2:", args.arg2) # 访问解析后的参数 arg2
文件下载器示例 (File Downloader Example):
import argparse
import requests
import shutil
def download_file(url, local_filename=None):
if local_filename is None:
local_filename = url.split('/')[-1] # 如果未指定本地文件名,则从 URL 中提取文件名
try:
response = requests.get(url, stream=True) # 发送 GET 请求,stream=True 表示使用流式下载
response.raise_for_status() # 检查请求状态码,如果不是 200 OK 则抛出异常
with open(local_filename, 'wb') as out_file: # 以二进制写入模式打开本地文件
shutil.copyfileobj(response.raw, out_file) # 使用 shutil.copyfileobj 高效复制流数据到文件
print(f"文件下载成功: {local_filename}")
except requests.exceptions.RequestException as e: # 捕获 requests 模块的异常
print(f"文件下载失败: {e}")
finally:
if 'response' in locals() and response:
response.close() # 确保 response 连接被关闭
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="从 URL 下载文件") # 创建 ArgumentParser 对象
parser.add_argument("url", help="文件 URL") # 添加位置参数 url,帮助信息为 "文件 URL"
parser.add_argument("output", help="输出文件名") # 添加位置参数 output,帮助信息为 "输出文件名"
args = parser.parse_args() # 解析命令行参数
download_file(args.url, args.output) # 调用 download_file 函数下载文件
# print("文件已下载:", args.output) # 打印下载完成信息,download_file 函数内部已打印成功信息
输出:
- 从提供的 URL 下载文件,并保存为指定的输出文件名。
海象运算符 (:=
):
- 海象运算符 (Walrus Operator)
:=
是在 Python 3.8 中引入的新特性,允许在表达式内部进行赋值。
示例 (Example):
happy = False
print("之前:", happy)
print("之后:", happy := True) # 使用海象运算符 := 在 print 函数内部将 happy 赋值为 True 并打印赋值结果
foods = list()
while (food := input("你喜欢什么食物?(输入 'quit' 退出): ")) != "quit": # 使用海象运算符 := 将 input() 的返回值赋值给 food 变量,并在 while 条件中判断
foods.append(food)
print("你喜欢的食物列表:", foods)
shutil
模块 (shutil Module):
shutil
模块是 Python 中用于高级文件操作的实用工具模块。
常用函数 (Functions):
shutil.copy(src, dst)
: 复制文件src
到dst
(目标可以是文件或目录)。shutil.copy2(src, dst)
: 复制文件src
到dst
,并保留元数据 (如时间戳)。shutil.copytree(src, dst)
: 递归复制整个目录树src
到dst
。shutil.move(src, dst)
: 移动文件或目录src
到dst
。shutil.rmtree(path)
: 递归删除目录树path
(慎用!)。
Beautiful Soup (bs4): Web Scraping (网页抓取)
- Beautiful Soup 是一个 Python 库,用于从 HTML 和 XML 文件中提取数据,常用于网页抓取 (Web Scraping)。
import requests
from bs4 import BeautifulSoup
url = "https://www.example.com" # 目标网页 URL
try:
r = requests.get(url) # 发送 GET 请求获取网页内容
r.raise_for_status() # 检查请求状态码
soup = BeautifulSoup(r.text, "html.parser") # 使用 BeautifulSoup 解析 HTML 内容,html.parser 是 HTML 解析器
print("格式化后的 HTML:\n", soup.prettify()) # 打印格式化后的 HTML 代码 (美化输出)
headings = soup.find_all("h1") # 查找所有 <h1> 标签
if headings:
print("\n网页标题 (h1 标签):")
for heading in headings: # 遍历找到的 <h1> 标签
print(heading.text.strip()) # 打印 <h1> 标签的文本内容,并去除首尾空白
else:
print("\n网页中没有 h1 标题。")
except requests.exceptions.RequestException as e:
print(f"请求网页失败: {e}")
except Exception as e:
print(f"解析网页内容失败: {e}")
输出:
- 打印整个网页内容的结构化格式 (美化后的 HTML 代码) 和所有
<h1>
标题的文本内容。
生成器 (Generators):
- 生成器 (Generators) 是一种特殊的函数,允许你逐个迭代生成值,而无需一次性将所有值都存储在内存中。
- 生成器使用
yield
关键字产生值,而不是return
。
示例 (Example):
def my_generator(n): # 定义生成器函数 my_generator
for i in range(n):
yield i # 使用 yield 关键字产生值 i
gen = my_generator(5) # 创建生成器对象 gen
print(next(gen)) # 输出: 0 (使用 next() 函数获取生成器的下一个值)
print(next(gen)) # 输出: 1
print(next(gen)) # 输出: 2
for j in gen: # 使用 for 循环迭代生成器,从上次 yield 的位置继续执行
print(j) # 输出: 3, 4 (for 循环会自动处理 StopIteration 异常,当生成器没有更多值时循环结束)
函数缓存 (@lru_cache
):
- 函数缓存 (Function Caching) 使用缓存 (Cache) 存储函数的计算结果,避免对相同输入重复计算,适用于计算开销较大的函数。
- Python 中可以使用
functools.lru_cache
装饰器实现函数缓存。
示例 (Example):
from functools import lru_cache # 导入 lru_cache 装饰器
import time
@lru_cache(maxsize=None) # 使用 @lru_cache 装饰器,maxsize=None 表示缓存大小无限制
def fx(n): # 定义函数 fx,模拟耗时计算
time.sleep(2) # 模拟耗时操作,休眠 2 秒
return n * 5
start_time = time.time()
print(fx(20)) # 第一次调用 fx(20),需要计算 2 秒
print(f"第一次调用 fx(20) 耗时: {time.time() - start_time:.2f} 秒")
start_time = time.time()
print(fx(2)) # 第一次调用 fx(2),需要计算 2 秒
print(f"第一次调用 fx(2) 耗时: {time.time() - start_time:.2f} 秒")
start_time = time.time()
print(fx(6)) # 第一次调用 fx(6),需要计算 2 秒
print(f"第一次调用 fx(6) 耗时: {time.time() - start_time:.2f} 秒")
# 下面的调用将直接从缓存中读取结果,无需重新计算,几乎是瞬间完成
start_time = time.time()
print(fx(20)) # 第二次调用 fx(20),从缓存中读取结果,无需计算
print(f"第二次调用 fx(20) 耗时: {time.time() - start_time:.2f} 秒")
start_time = time.time()
print(fx(2)) # 第二次调用 fx(2),从缓存中读取结果,无需计算
print(f"第二次调用 fx(2) 耗时: {time.time() - start_time:.2f} 秒")
start_time = time.time()
print(fx(6)) # 第二次调用 fx(6),从缓存中读取结果,无需计算
print(f"第二次调用 fx(6) 耗时: {time.time() - start_time:.2f} 秒")
正则表达式 (Regex):
- 正则表达式 (Regular Expressions 或 Regex) 是一种强大的文本模式匹配工具,用于在字符串中查找、替换、验证特定模式的文本。
- Python 中使用
re
模块处理正则表达式。
基本正则表达式 (Basic Regex):
import re
pattern = r"表达式" # raw 字符串,定义正则表达式模式 (例如 r"hello")
text = "你好,世界!" # 要搜索的文本
match = re.search(pattern, text) # 使用 re.search() 函数在 text 中搜索 pattern 模式,返回 Match 对象或 None
if match:
print("找到匹配项!") # 如果找到匹配项
else:
print("未找到匹配项。") # 如果未找到匹配项
示例 (Example):
import re
text = "The cat is in the hat and a bat." # 文本字符串
pattern = r"[a-z]+at" # 正则表达式模式:匹配一个或多个小写字母 (a-z),后跟 "at"
matches = re.findall(pattern, text) # 使用 re.findall() 函数查找文本中所有匹配模式的子字符串,返回列表
print("匹配项:", matches) # 输出: ['cat', 'hat', 'bat']
new_text = re.sub(pattern, "dog", text) # 使用 re.sub() 函数将文本中所有匹配 pattern 的子字符串替换为 "dog",返回替换后的新字符串
print("替换后的文本:", new_text) # 输出: "The dog is in the dog and a dog."
提取邮箱地址 (Extracting Emails):
import re
text = "我的邮箱是 example@example.com,另一个邮箱是 test.user@domain.net。" # 包含邮箱地址的文本
pattern = r"\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b" # 匹配邮箱地址的正则表达式模式 (更精确的邮箱格式匹配)
matches = re.findall(pattern, text) # 查找所有匹配邮箱地址的子字符串
if matches:
print("提取到的邮箱地址:")
for email in matches: # 遍历找到的邮箱地址
print(f"- {email}") # 打印每个邮箱地址
else:
print("未找到邮箱地址。")
AsyncIO (异步 I/O)
asyncio
模块提供了异步 I/O 编程的支持,允许程序在等待 I/O 操作 (如网络请求、文件读写) 完成时,继续执行其他任务,提高程序的并发性和效率。
import asyncio
import requests
async def download_image(url, filename): # 定义异步函数 download_image,使用 async 关键字
print(f"开始下载: {filename}")
response = requests.get(url) # 发送同步 HTTP 请求 (requests 库是同步的)
if response.status_code == 200:
with open(filename, "wb") as f:
f.write(response.content)
print(f"下载完成: {filename}")
else:
print(f"下载失败: {filename}, 状态码: {response.status_code}")
return filename # 返回文件名
async def main(): # 定义主异步函数 main
image_urls = [
("https://www.easygifanimator.net/images/samples/video-to-gif-sample.gif", "image1.gif"),
("https://upload.wikimedia.org/wikipedia/commons/4/4d/Cat_November_2010-1a.jpg", "image2.jpg"),
("https://www.thoughtco.com/thmb/zQDr3lG080p4Z_MA8dtmSjhqQ94=/768x0/filters:no_upscale():max_bytes(150000):strip_icc()/close-up-of-cat-eyes-873370984-5c7ff4b4c929250001f6b383.jpg", "image3.jpg"),
]
tasks = [] # 创建任务列表
for url, filename in image_urls:
task = asyncio.create_task(download_image(url, filename)) # 使用 asyncio.create_task 创建异步任务
tasks.append(task)
downloaded_files = await asyncio.gather(*tasks) # 使用 asyncio.gather 并发执行所有任务,并等待所有任务完成,获取返回值列表
print("下载完成的文件:", downloaded_files)
if __name__ == "__main__":
asyncio.run(main()) # 使用 asyncio.run 运行主异步函数 main
输出:
- image1.gif, image2.jpg, 和 image3.jpg 将被下载到本地目录。
- 打印下载完成的文件名列表。
多线程 (MultiThreading)
- 多线程 (MultiThreading) 允许在一个进程中创建多个线程,并发执行代码,提高程序的并发性。
- Python 中使用
threading
模块实现多线程。 - 创建线程 -> 调用线程对象的
start()
方法启动线程。 join()
方法用于等待线程执行结束。
import threading
def my_func(): # 线程执行的函数
print(f"你好,来自线程 {threading.current_thread().name}") # 打印当前线程的名字
thread = threading.Thread(target=my_func) # 创建新的线程,target 参数指定线程执行的函数
thread.start() # 启动线程
thread.join() # 等待线程执行结束
print("主线程继续执行")
输出:
你好,来自线程 Thread-1
(线程名字可能略有不同,取决于系统分配)主线程继续执行
常用函数 (Functions):
-
threading.Thread(target, args)
:- 创建一个新的线程,
target
参数指定线程要执行的函数,args
参数以元组形式传递给target
函数的参数。
- 创建一个新的线程,
-
threading.Lock()
:- 创建一个锁 (Lock) 对象,用于同步多线程对共享资源的访问,防止数据竞争。
- 锁确保在同一时刻只有一个线程可以访问临界区 (critical section) 代码。
使用锁的示例 (Example with Lock):
import threading
def increment(counter, lock): # 线程执行的函数,counter 是共享计数器,lock 是锁对象
for _ in range(100000): # 循环增加计数器
lock.acquire() # 获取锁,进入临界区
counter[0] += 1 # 增加共享计数器
lock.release() # 释放锁,退出临界区
if __name__ == "__main__":
counter = [0] # 使用列表存储计数器,因为列表是可变对象,可以在线程间共享和修改
lock = threading.Lock() # 创建锁对象
threads = [] # 存储线程对象的列表
for _ in range(2): # 创建两个线程
thread = threading.Thread(target=increment, args=(counter, lock)) # 创建线程,指定执行函数和参数
threads.append(thread) # 添加线程到列表
thread.start() # 启动线程
for thread in threads: # 等待所有线程执行结束
thread.join()
print(f"最终计数器值: {counter[0]}") # 打印最终计数器值,期望值为 200000 (两个线程各增加 100000)
输出:
- 最终计数器值将会是
200000
(理论上,实际运行结果可能因系统调度等因素略有偏差,但使用锁可以很大程度上避免数据竞争)。
使用 ThreadPoolExecutor
(Using ThreadPoolExecutor)
import time
from concurrent.futures import ThreadPoolExecutor # 导入 ThreadPoolExecutor 类
def func(seconds): # 模拟耗时操作的函数
print(f"线程 {threading.current_thread().name} 休眠 {seconds} 秒")
time.sleep(seconds)
return seconds
def main():
time1 = time.perf_counter() # 记录开始时间
# 使用线程池
with ThreadPoolExecutor(max_workers=3) as executor: # 创建线程池,最大工作线程数为 3
futures = []
for i in range(3):
seconds_to_sleep = 4 - i # 休眠时间递减
future = executor.submit(func, seconds_to_sleep) # 提交任务到线程池,返回 Future 对象
futures.append(future)
results = [future.result() for future in futures] # 获取所有 Future 对象的结果,会阻塞等待任务完成
print("任务结果:", results)
time2 = time.perf_counter() # 记录结束时间
print(f"程序总耗时: {time2 - time1:.2f} 秒") # 打印程序总耗时
if __name__ == "__main__":
main()
输出:
- 线程会并发执行
func
函数,总耗时会远小于串行执行的时间,大约在 4 秒左右 (取决于最长的休眠时间)。 - 输出线程休眠信息和程序总耗时。
多进程 (Multiprocessing)
multiprocessing
模块提供了创建和管理进程的功能,允许程序利用多核 CPU 并行执行任务,充分利用系统资源,提高计算密集型任务的性能。
import concurrent.futures
import requests
import os
import time
def download_image(url, name): # 下载图片的函数
start_time = time.time()
try:
print(f"进程 {os.getpid()} 开始下载 {name}") # 打印进程 ID 和下载文件名
response = requests.get(url, stream=True) # 发送 HTTP 请求,stream=True 使用流式下载
response.raise_for_status() # 检查请求状态码
file_path = f"{name}.jpg"
with open(file_path, "wb") as file:
for chunk in response.iter_content(chunk_size=8192): # 分块写入文件
file.write(chunk)
end_time = time.time()
print(f"进程 {os.getpid()} 完成下载 {name},耗时: {end_time - start_time:.2f} 秒")
return f"{name}.jpg" # 返回保存的文件名
except requests.exceptions.RequestException as e:
print(f"进程 {os.getpid()} 下载 {name} 失败: {e}")
return None
if __name__ == "__main__":
image_url = "https://picsum.photos/200/300" # 图片 URL
image_names = [f"image_{i}" for i in range(50)] # 生成 50 个图片文件名
start_time = time.time()
with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor: # 创建进程池,最大工作进程数为 4
processes = []
for name in image_names:
process = executor.submit(download_image, image_url, name) # 提交下载任务到进程池
processes.append(process)
downloaded_files = [process.result() for process in concurrent.futures.as_completed(processes)] # 获取已完成进程的结果
downloaded_files = [file for file in downloaded_files if file] # 过滤掉下载失败的文件
end_time = time.time()
print(f"所有图片下载完成,总耗时: {end_time - start_time:.2f} 秒")
print(f"成功下载的文件数量: {len(downloaded_files)}")
输出:
- 脚本会并发下载 50 张图片,使用多进程可以显著缩短下载时间,尤其在 CPU 密集型任务或 I/O 密集型任务中,多进程可以充分利用多核 CPU 的性能。
- 输出每个进程的下载开始、完成信息以及总耗时和成功下载的文件数量。
多进程 vs 多线程 (Multiprocessing vs Multithreading)
序号 多进程 多线程
1. 添加 CPU 以提高计算能力。 创建单个进程的多个线程以实现并发。
2. 同时执行多个进程。 同时执行进程的多个线程。
3. 分为对称和非对称多进程。 不分为类别。
4. 进程创建耗时较长。 线程创建更经济。
5. 每个进程都拥有单独的地址空间。 所有线程共享一个公共地址空间。
本文在@ARYANK-08的python fundamentals的基础上修改和完善。