python语法笔记
Python 问题解答记录
python语法笔记之-字符串 index() 方法语法详解
问题代码
str1 = "Hello,Python";
str2 = "Python";
print(str1.index(str2));
语法详解
1. str.index()
方法基本语法
str.index(sub[, start[, end]])
参数说明:
sub
: 要查找的子字符串start
: 可选参数,开始查找的位置(默认为0)end
: 可选参数,结束查找的位置(默认为字符串长度)
返回值:
- 返回子字符串在原字符串中第一次出现的索引位置
- 如果找不到子字符串,会抛出
ValueError
异常
2. 代码执行过程分析
str1 = "Hello,Python" # 原字符串
str2 = "Python" # 要查找的子字符串
print(str1.index(str2)) # 输出:7
执行步骤:
str1
包含字符串"Hello,Python"
str2
包含字符串"Python"
str1.index(str2)
在str1
中查找str2
第一次出现的位置"Python"
在"Hello,Python"
中从索引7开始出现- 输出结果:
7
3. 字符串索引位置说明
字符串: "Hello,Python"
索引: 012345678901
"Python"
从索引7开始,到索引12结束- 所以
index()
返回第一个字符'P'
的位置:7
4. 相关方法对比
方法 | 功能 | 找不到时的行为 |
---|---|---|
index() | 查找子字符串位置 | 抛出 ValueError |
find() | 查找子字符串位置 | 返回 -1 |
rindex() | 从右向左查找 | 抛出 ValueError |
rfind() | 从右向左查找 | 返回 -1 |
5. 实际应用示例
# 基本用法
text = "Hello, World!"
print(text.index("World")) # 输出:7# 指定搜索范围
text = "Hello, World! Hello, Python!"
print(text.index("Hello", 1)) # 从索引1开始查找,输出:14# 错误处理
try:text = "Hello, World!"position = text.index("Python")print(f"找到位置:{position}")
except ValueError:print("未找到子字符串")# 使用 find() 方法(推荐)
text = "Hello, World!"
position = text.find("Python")
if position != -1:print(f"找到位置:{position}")
else:print("未找到子字符串")
6. 注意事项
-
区分大小写:
index()
方法区分大小写text = "Hello, World!" print(text.index("world")) # ValueError: substring not found
-
只返回第一次出现的位置:如果有多个相同的子字符串,只返回第一个
text = "Hello, Hello, World!" print(text.index("Hello")) # 输出:0(不是7)
-
空字符串:空字符串在任何位置都能找到
text = "Hello" print(text.index("")) # 输出:0
-
建议使用
find()
方法:find()
方法更安全,不会抛出异常text = "Hello, World!" position = text.find("Python") # 返回 -1,不会抛出异常
python语法笔记之-divmod() 函数语法详解
问题代码
a = 100
b = 14
print(divmod(a, b))
语法详解
1. divmod()
函数基本语法
divmod(x, y)
参数说明:
x
: 被除数(dividend)y
: 除数(divisor)
返回值:
- 返回一个元组
(quotient, remainder)
quotient
: 商(整数除法结果)remainder
: 余数
2. 代码执行过程分析
a = 100 # 被除数
b = 14 # 除数
print(divmod(a, b)) # 输出:(7, 2)
执行步骤:
a = 100
,b = 14
divmod(100, 14)
计算 100 除以 14- 商:100 ÷ 14 = 7(整数除法)
- 余数:100 - (14 × 7) = 100 - 98 = 2
- 返回元组:
(7, 2)
3. 数学原理说明
divmod(a, b)
等价于:
quotient = a // b # 整数除法
remainder = a % b # 取余运算
return (quotient, remainder)
验证:
a, b = 100, 14
print(f"a // b = {a // b}") # 7
print(f"a % b = {a % b}") # 2
print(f"divmod(a, b) = {divmod(a, b)}") # (7, 2)
4. 实际应用示例
# 基本用法
print(divmod(10, 3)) # (3, 1)
print(divmod(20, 5)) # (4, 0)
print(divmod(7, 2)) # (3, 1)# 负数情况
print(divmod(-10, 3)) # (-4, 2)
print(divmod(10, -3)) # (-4, -2)
print(divmod(-10, -3)) # (3, -1)# 浮点数情况
print(divmod(10.5, 3)) # (3.0, 1.5)
print(divmod(10, 3.5)) # (2.0, 3.0)# 零除错误
try:print(divmod(10, 0))
except ZeroDivisionError as e:print(f"错误:{e}") # 错误:integer division or modulo by zero
5. 常见应用场景
1. 时间转换
# 将秒数转换为小时和分钟
total_seconds = 3661
hours, remaining_seconds = divmod(total_seconds, 3600)
minutes, seconds = divmod(remaining_seconds, 60)
print(f"{total_seconds}秒 = {hours}小时{minutes}分钟{seconds}秒")
# 输出:3661秒 = 1小时1分钟1秒
2. 数字分解
# 将数字分解为百位、十位、个位
number = 123
hundreds, remainder = divmod(number, 100)
tens, ones = divmod(remainder, 10)
print(f"{number} = {hundreds}×100 + {tens}×10 + {ones}")
# 输出:123 = 1×100 + 2×10 + 3
3. 进制转换
# 十进制转二进制
def decimal_to_binary(n):if n == 0:return "0"binary = ""while n > 0:n, remainder = divmod(n, 2)binary = str(remainder) + binaryreturn binaryprint(decimal_to_binary(10)) # 1010
4. 循环队列实现
# 循环队列索引计算
def get_circular_index(index, size):return divmod(index, size)[1] # 等价于 index % sizequeue_size = 5
for i in range(10):circular_index = get_circular_index(i, queue_size)print(f"原始索引: {i}, 循环索引: {circular_index}")
6. 与其他函数的对比
函数/操作 | 功能 | 返回值 |
---|---|---|
divmod(x, y) | 同时计算商和余数 | (quotient, remainder) |
x // y | 整数除法 | quotient |
x % y | 取余运算 | remainder |
x / y | 浮点除法 | float |
7. 注意事项
-
整数除法:
divmod()
使用整数除法,不是浮点除法print(divmod(10, 3)) # (3, 1) 不是 (3.333..., 1)
-
负数处理:负数的
divmod()
结果遵循 Python 的整数除法规则print(divmod(-10, 3)) # (-4, 2) print(divmod(10, -3)) # (-4, -2)
-
浮点数:如果参数是浮点数,结果也是浮点数
print(divmod(10.5, 3)) # (3.0, 1.5)
-
零除错误:除数为0时会抛出
ZeroDivisionError
try:divmod(10, 0) except ZeroDivisionError:print("不能除以零")
python语法笔记之-字符编码函数语法详解
问题代码
return ? == 'A';
选项:
- A:
ord(65)
- B:
chr(65)
- C:
65+''
- D:
'+65
答案
正确答案:B - chr(65)
语法详解
1. chr()
函数基本语法
chr(i)
参数说明:
i
: 整数,表示 Unicode 码点(0-1,114,111)
返回值:
- 返回对应的 Unicode 字符
示例:
print(chr(65)) # 'A'
print(chr(97)) # 'a'
print(chr(48)) # '0'
print(chr(32)) # ' ' (空格)
2. ord()
函数基本语法
ord(c)
参数说明:
c
: 单个字符
返回值:
- 返回字符的 Unicode 码点
示例:
print(ord('A')) # 65
print(ord('a')) # 97
print(ord('0')) # 48
print(ord(' ')) # 32
3. 各选项详细分析
选项A:ord(65)
# 错误用法
print(ord(65)) # TypeError: ord() expected a character, but int found
ord()
函数需要字符作为参数,不能是数字- 正确的用法:
ord('A')
返回 65
选项B:chr(65)
✅
# 正确用法
print(chr(65)) # 'A'
print(chr(65) == 'A') # True
chr(65)
返回字符 ‘A’- 所以
chr(65) == 'A'
返回True
选项C:65+''
# 语法错误
print(65 + '') # TypeError: unsupported operand type(s) for +: 'int' and 'str'
- Python 中不能直接将整数与字符串相加
- 需要先转换:
str(65) + ''
或65 + ''
(如果 65 是字符串)
选项D:'+65
# 语法错误
print('+65') # 这只是字符串,不是表达式
- 这不是有效的 Python 表达式
- 可能是想表达
'+' + str(65)
或其他含义
4. 字符编码相关函数对比
函数 | 功能 | 参数 | 返回值 | 示例 |
---|---|---|---|---|
chr(i) | 数字转字符 | 整数 | 字符 | chr(65) → 'A' |
ord(c) | 字符转数字 | 字符 | 整数 | ord('A') → 65 |
str(i) | 数字转字符串 | 任意类型 | 字符串 | str(65) → '65' |
int(s) | 字符串转数字 | 字符串 | 整数 | int('65') → 65 |
5. 实际应用示例
1. 字符转换
# 大写字母转小写
def to_lower(char):if 'A' <= char <= 'Z':return chr(ord(char) + 32)return charprint(to_lower('A')) # 'a'
print(to_lower('Z')) # 'z'
2. 字符串处理
# 检查字符类型
def is_uppercase(char):return 'A' <= char <= 'Z'def is_lowercase(char):return 'a' <= char <= 'z'def is_digit(char):return '0' <= char <= '9'print(is_uppercase('A')) # True
print(is_lowercase('a')) # True
print(is_digit('5')) # True
3. 密码学应用
# 简单的凯撒密码
def caesar_cipher(text, shift):result = ""for char in text:if char.isalpha():# 确定基准值(A=65, a=97)base = ord('A') if char.isupper() else ord('a')# 计算新位置new_pos = (ord(char) - base + shift) % 26# 转换回字符result += chr(base + new_pos)else:result += charreturn resultprint(caesar_cipher("HELLO", 3)) # "KHOOR"
print(caesar_cipher("KHOOR", -3)) # "HELLO"
4. 进制转换
# 十六进制字符转换
def hex_to_char(hex_str):return chr(int(hex_str, 16))def char_to_hex(char):return hex(ord(char))print(hex_to_char('41')) # 'A'
print(char_to_hex('A')) # '0x41'
6. ASCII 码表(常用部分)
字符 | ASCII 码 | 说明 |
---|---|---|
'A' | 65 | 大写字母A |
'Z' | 90 | 大写字母Z |
'a' | 97 | 小写字母a |
'z' | 122 | 小写字母z |
'0' | 48 | 数字0 |
'9' | 57 | 数字9 |
' ' | 32 | 空格 |
'\n' | 10 | 换行符 |
'\t' | 9 | 制表符 |
7. 注意事项
-
Unicode 支持:
chr()
和ord()
支持 Unicode,不仅仅是 ASCIIprint(chr(128512)) # '😀' (笑脸表情) print(ord('😀')) # 128512
-
参数范围:
chr()
的参数范围是 0-1,114,111try:print(chr(-1)) except ValueError as e:print(f"错误:{e}") # 错误:chr() arg not in range(0x110000)
-
字符长度:
ord()
只能处理单个字符try:print(ord('AB')) except TypeError as e:print(f"错误:{e}") # 错误:ord() expected a character, but string of length 2 found
-
类型转换:注意字符串和数字的区别
print(chr(65)) # 'A' (字符) print(str(65)) # '65' (字符串) print(int('65')) # 65 (整数)
python语法笔记之-slots 继承机制语法详解
问题代码
class Vector:__slots__ = 'x', 'y'def __init__(self):passclass Vector3d(Vector):__slots__ = 'x', 'z'def __init__(self):passvector = Vector()
vector3d = Vector3d()
语法详解
1. __slots__
基本概念
__slots__
是 Python 类的一个特殊属性,用于:
- 限制类的实例只能拥有指定的属性
- 节省内存空间(避免
__dict__
字典) - 提高属性访问速度
2. 代码执行分析
# 创建 Vector 实例
vector = Vector()
# vector 只能有 x, y 属性# 创建 Vector3d 实例
vector3d = Vector3d()
# vector3d 只能有 x, z 属性(注意:没有 y 属性)
属性测试:
# Vector 实例
vector.x = 1 # 正常
vector.y = 2 # 正常
try:vector.z = 3 # AttributeError: 'Vector' object has no attribute 'z'
except AttributeError as e:print(f"Vector 错误:{e}")# Vector3d 实例
vector3d.x = 1 # 正常
vector3d.z = 3 # 正常
try:vector3d.y = 2 # AttributeError: 'Vector3d' object has no attribute 'y'
except AttributeError as e:print(f"Vector3d 错误:{e}")
3. __slots__
继承机制详解
重要规则:
- 子类的
__slots__
会完全覆盖父类的__slots__
- 子类不会自动继承父类的
__slots__
属性 - 如果需要父类的属性,必须在子类中显式声明
继承关系分析:
class Vector:__slots__ = 'x', 'y' # Vector 实例只能有 x, y 属性class Vector3d(Vector):__slots__ = 'x', 'z' # Vector3d 实例只能有 x, z 属性# 注意:没有 y 属性!
4. 正确的继承方式
方式1:显式包含父类属性
class Vector:__slots__ = 'x', 'y'def __init__(self):passclass Vector3d(Vector):__slots__ = 'x', 'y', 'z' # 显式包含父类的 x, y 属性def __init__(self):super().__init__()# 测试
vector3d = Vector3d()
vector3d.x = 1 # 正常
vector3d.y = 2 # 正常
vector3d.z = 3 # 正常
方式2:使用元组合并
class Vector:__slots__ = 'x', 'y'def __init__(self):passclass Vector3d(Vector):__slots__ = Vector.__slots__ + ('z',) # 合并父类的 slotsdef __init__(self):super().__init__()# 测试
vector3d = Vector3d()
vector3d.x = 1 # 正常
vector3d.y = 2 # 正常
vector3d.z = 3 # 正常
5. 实际应用示例
1. 几何图形类
class Point2D:__slots__ = 'x', 'y'def __init__(self, x, y):self.x = xself.y = ydef distance_to_origin(self):return (self.x**2 + self.y**2)**0.5class Point3D(Point2D):__slots__ = 'x', 'y', 'z' # 包含父类的 x, ydef __init__(self, x, y, z):super().__init__(x, y)self.z = zdef distance_to_origin(self):return (self.x**2 + self.y**2 + self.z**2)**0.5# 测试
p2d = Point2D(3, 4)
p3d = Point3D(3, 4, 5)
print(f"2D距离:{p2d.distance_to_origin()}") # 5.0
print(f"3D距离:{p3d.distance_to_origin()}") # 7.0710678118654755
2. 数据结构类
class Node:__slots__ = 'data', 'next'def __init__(self, data):self.data = dataself.next = Noneclass DoubleNode(Node):__slots__ = 'data', 'next', 'prev' # 包含父类的 data, nextdef __init__(self, data):super().__init__(data)self.prev = None# 测试
node = Node(10)
dnode = DoubleNode(20)
print(f"Node: {node.data}") # 10
print(f"DoubleNode: {dnode.data}") # 20
3. 配置类
class BaseConfig:__slots__ = 'host', 'port'def __init__(self, host, port):self.host = hostself.port = portclass DatabaseConfig(BaseConfig):__slots__ = 'host', 'port', 'username', 'password', 'database'def __init__(self, host, port, username, password, database):super().__init__(host, port)self.username = usernameself.password = passwordself.database = database# 测试
db_config = DatabaseConfig('localhost', 3306, 'user', 'pass', 'mydb')
print(f"数据库:{db_config.host}:{db_config.port}")
6. __slots__
与 __dict__
对比
特性 | 使用 __slots__ | 使用 __dict__ |
---|---|---|
内存使用 | 更少 | 更多 |
属性访问速度 | 更快 | 较慢 |
动态添加属性 | 不允许 | 允许 |
继承复杂性 | 需要显式管理 | 自动继承 |
7. 常见错误和注意事项
错误1:忘记包含父类属性
class Parent:__slots__ = 'a', 'b'class Child(Parent):__slots__ = 'c' # 错误:丢失了 a, b 属性def __init__(self):self.a = 1 # AttributeError!
错误2:重复定义属性
class Parent:__slots__ = 'a', 'b'class Child(Parent):__slots__ = 'a', 'b', 'a' # 错误:重复的 'a'
错误3:与 __dict__
冲突
class MyClass:__slots__ = 'x', 'y'__dict__ = {} # 错误:不能同时使用 __slots__ 和 __dict__
8. 最佳实践
- 明确继承关系:在子类中显式包含需要的父类属性
- 使用元组合并:
__slots__ = Parent.__slots__ + ('new_attr',)
- 考虑内存优化:对于大量实例的类,使用
__slots__
可以节省内存 - 保持灵活性:如果类需要动态添加属性,不要使用
__slots__
9. 性能对比示例
import sys# 不使用 __slots__
class RegularPoint:def __init__(self, x, y):self.x = xself.y = y# 使用 __slots__
class SlottedPoint:__slots__ = 'x', 'y'def __init__(self, x, y):self.x = xself.y = y# 内存使用对比
regular = RegularPoint(1, 2)
slotted = SlottedPoint(1, 2)print(f"RegularPoint 大小:{sys.getsizeof(regular)} 字节")
print(f"SlottedPoint 大小:{sys.getsizeof(slotted)} 字节")
# 通常 SlottedPoint 比 RegularPoint 小 40-50 字节
python语法笔记之-Python 类继承语法详解
问题代码
# 2. 继承
print("\n2. 继承:")class Person:def __init__(self, name, age):self.name = nameself.age = agedef speak(self):return f"{self.name}在说话"class Teacher(Person):def __init__(self, name, age, subject):super().__init__(name, age)self.subject = subjectdef teach(self):return f"{self.name}正在教授{self.subject}"teacher = Teacher("张老师", 35, "数学")
print(teacher.speak())
print(teacher.teach())
语法详解
1. 继承的基本概念
继承是面向对象编程的核心概念之一,允许一个类(子类)继承另一个类(父类)的属性和方法。
继承关系:
Person
是父类(基类)Teacher
是子类(派生类)Teacher
继承Person
的所有属性和方法
2. 父类 Person
分析
class Person:def __init__(self, name, age):self.name = name # 实例属性:姓名self.age = age # 实例属性:年龄def speak(self):return f"{self.name}在说话" # 实例方法:说话
特点:
- 构造函数
__init__
初始化name
和age
属性 speak()
方法返回说话信息- 这是一个通用的"人"类
3. 子类 Teacher
分析
class Teacher(Person): # 继承 Person 类def __init__(self, name, age, subject):super().__init__(name, age) # 调用父类构造函数self.subject = subject # 新增属性:学科def teach(self):return f"{self.name}正在教授{self.subject}" # 新增方法:教学
关键语法:
class Teacher(Person)
- 声明继承关系super().__init__(name, age)
- 调用父类构造函数self.subject = subject
- 添加子类特有的属性teach()
- 添加子类特有的方法
4. 代码执行过程分析
teacher = Teacher("张老师", 35, "数学")
执行步骤:
- 创建
Teacher
实例,传入参数("张老师", 35, "数学")
- 调用
Teacher.__init__()
方法 - 执行
super().__init__(name, age)
,调用父类Person.__init__()
- 父类初始化
self.name = "张老师"
,self.age = 35
- 子类初始化
self.subject = "数学"
- 实例创建完成
结果:
print(teacher.speak()) # 输出:张老师在说话
print(teacher.teach()) # 输出:张老师正在教授数学
5. 继承的属性和方法
继承的属性:
teacher.name
= “张老师”(来自父类)teacher.age
= 35(来自父类)teacher.subject
= “数学”(子类特有)
继承的方法:
teacher.speak()
- 继承自父类teacher.teach()
- 子类特有
6. super()
函数详解
super()
的作用:
- 返回父类的代理对象
- 用于调用父类的方法
- 支持多重继承
语法形式:
super().__init__(args) # Python 3 推荐写法
super(Teacher, self).__init__(args) # Python 2 写法
实际应用:
class Teacher(Person):def __init__(self, name, age, subject):# 调用父类构造函数,初始化 name 和 agesuper().__init__(name, age)# 初始化子类特有属性self.subject = subjectself.salary = 5000 # 可以继续添加更多属性
7. 继承的类型
1. 单继承(当前示例)
class Teacher(Person): # 只继承一个父类pass
2. 多重继承
class Employee:def __init__(self, employee_id):self.employee_id = employee_idclass Teacher(Person, Employee): # 继承多个父类def __init__(self, name, age, subject, employee_id):Person.__init__(self, name, age)Employee.__init__(self, employee_id)self.subject = subject
3. 多层继承
class Student(Person):def __init__(self, name, age, grade):super().__init__(name, age)self.grade = gradeclass GraduateStudent(Student): # 继承 Student,间接继承 Persondef __init__(self, name, age, grade, research_area):super().__init__(name, age, grade)self.research_area = research_area
8. 方法重写(Override)
重写父类方法:
class Teacher(Person):def __init__(self, name, age, subject):super().__init__(name, age)self.subject = subjectdef speak(self): # 重写父类的 speak 方法return f"{self.name}老师说:同学们好!"def teach(self):return f"{self.name}正在教授{self.subject}"# 测试
teacher = Teacher("张老师", 35, "数学")
print(teacher.speak()) # 输出:张老师老师说:同学们好!
9. 实际应用示例
1. 动物类继承体系
class Animal:def __init__(self, name, species):self.name = nameself.species = speciesdef make_sound(self):return "发出声音"class Dog(Animal):def __init__(self, name, breed):super().__init__(name, "狗")self.breed = breeddef make_sound(self):return f"{self.name}汪汪叫"def fetch(self):return f"{self.name}在捡球"class Cat(Animal):def __init__(self, name, color):super().__init__(name, "猫")self.color = colordef make_sound(self):return f"{self.name}喵喵叫"def climb(self):return f"{self.name}在爬树"# 测试
dog = Dog("旺财", "金毛")
cat = Cat("咪咪", "橘色")
print(dog.make_sound()) # 旺财汪汪叫
print(cat.make_sound()) # 咪咪喵喵叫
2. 图形类继承体系
class Shape:def __init__(self, color):self.color = colordef area(self):return 0def perimeter(self):return 0class Rectangle(Shape):def __init__(self, color, width, height):super().__init__(color)self.width = widthself.height = heightdef area(self):return self.width * self.heightdef perimeter(self):return 2 * (self.width + self.height)class Circle(Shape):def __init__(self, color, radius):super().__init__(color)self.radius = radiusdef area(self):import mathreturn math.pi * self.radius ** 2def perimeter(self):import mathreturn 2 * math.pi * self.radius# 测试
rect = Rectangle("红色", 5, 3)
circle = Circle("蓝色", 4)
print(f"矩形面积:{rect.area()}") # 15
print(f"圆形面积:{circle.area():.2f}") # 50.27
10. 继承的优势和注意事项
优势:
- 代码复用:避免重复编写相同的代码
- 层次结构:建立清晰的类层次关系
- 多态性:不同子类可以有不同的实现
- 维护性:修改父类影响所有子类
注意事项:
- 合理设计:继承关系应该符合"是一个"的关系
- 避免过深继承:继承层次不要太深,通常不超过3层
- 使用组合:有时候组合比继承更合适
- 方法重写:重写方法时要考虑是否调用父类方法
11. 继承 vs 组合
继承(is-a 关系):
class Teacher(Person): # Teacher 是一个 Personpass
组合(has-a 关系):
class Teacher:def __init__(self, person, subject):self.person = person # Teacher 有一个 Personself.subject = subjectdef speak(self):return self.person.speak()
python语法笔记之-列表 append() 方法语法详解
问题代码
numbers = [1, 2, 3, 4]
numbers.append([5,6,7,8])
print(len(numbers))
答案
输出结果:5
语法详解
1. list.append()
方法基本语法
list.append(x)
参数说明:
x
: 要添加到列表末尾的元素(可以是任何类型)
返回值:
- 无返回值(None),直接修改原列表
功能:
- 在列表末尾添加一个元素
- 无论
x
是什么类型,都会作为一个整体元素添加
2. 代码执行过程分析
numbers = [1, 2, 3, 4] # 初始列表:[1, 2, 3, 4]
numbers.append([5,6,7,8]) # 添加列表:[5,6,7,8]
print(len(numbers)) # 输出:5
执行步骤:
- 创建列表
numbers = [1, 2, 3, 4]
,长度为 4 - 执行
numbers.append([5,6,7,8])
- 将整个列表
[5,6,7,8]
作为一个元素添加到末尾 - 列表变成
[1, 2, 3, 4, [5,6,7,8]]
- 将整个列表
- 执行
len(numbers)
,返回 5
验证:
numbers = [1, 2, 3, 4]
print(f"原始列表:{numbers},长度:{len(numbers)}")
# 输出:原始列表:[1, 2, 3, 4],长度:4numbers.append([5,6,7,8])
print(f"添加后:{numbers},长度:{len(numbers)}")
# 输出:添加后:[1, 2, 3, 4, [5,6,7,8]],长度:5
3. 常见误解分析
误解1:认为会展开列表
# 错误理解:以为会变成 [1, 2, 3, 4, 5, 6, 7, 8]
# 实际情况:变成 [1, 2, 3, 4, [5,6,7,8]]
误解2:认为会报错
# 错误理解:以为不能添加列表到列表中
# 实际情况:Python 允许列表嵌套,完全合法
4. 不同添加方法的对比
方法 | 语法 | 功能 | 示例 | 结果 |
---|---|---|---|---|
append() | list.append(x) | 添加单个元素 | [1,2].append([3,4]) | [1,2,[3,4]] |
extend() | list.extend(iterable) | 展开添加元素 | [1,2].extend([3,4]) | [1,2,3,4] |
insert() | list.insert(i, x) | 在指定位置插入 | [1,2].insert(1,3) | [1,3,2] |
+ 操作符 | list1 + list2 | 连接两个列表 | [1,2] + [3,4] | [1,2,3,4] |
5. 实际应用示例
1. 基本用法对比
# append() - 添加整个列表作为元素
numbers = [1, 2, 3]
numbers.append([4, 5])
print(numbers) # [1, 2, 3, [4, 5]]
print(len(numbers)) # 4# extend() - 展开列表元素
numbers = [1, 2, 3]
numbers.extend([4, 5])
print(numbers) # [1, 2, 3, 4, 5]
print(len(numbers)) # 5# + 操作符 - 连接列表
numbers = [1, 2, 3]
result = numbers + [4, 5]
print(result) # [1, 2, 3, 4, 5]
print(len(result)) # 5
2. 嵌套列表应用
# 创建二维列表
matrix = []
matrix.append([1, 2, 3])
matrix.append([4, 5, 6])
matrix.append([7, 8, 9])
print(matrix) # [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(len(matrix)) # 3# 访问元素
print(matrix[0]) # [1, 2, 3]
print(matrix[0][1]) # 2
3. 数据分组
# 按类型分组数据
numbers = [1, 2, 3, 4, 5]
strings = ['a', 'b', 'c']
booleans = [True, False]all_data = []
all_data.append(numbers)
all_data.append(strings)
all_data.append(booleans)print(all_data) # [[1, 2, 3, 4, 5], ['a', 'b', 'c'], [True, False]]
print(len(all_data)) # 3# 访问特定类型的数据
print(all_data[0]) # [1, 2, 3, 4, 5] (数字)
print(all_data[1]) # ['a', 'b', 'c'] (字符串)
4. 购物车示例
class ShoppingCart:def __init__(self):self.items = []def add_item(self, name, price, quantity=1):# 将商品信息作为列表添加item = [name, price, quantity]self.items.append(item)def get_total(self):total = 0for item in self.items:total += item[1] * item[2] # price * quantityreturn total# 使用示例
cart = ShoppingCart()
cart.add_item("苹果", 5.0, 3)
cart.add_item("香蕉", 3.0, 2)
cart.add_item("橙子", 4.0, 1)print(f"购物车项目数:{len(cart.items)}") # 3
print(f"总价:{cart.get_total()}") # 23.0
6. 列表操作的其他方法
1. 添加元素的方法
numbers = [1, 2, 3]# append() - 添加单个元素
numbers.append(4)
print(numbers) # [1, 2, 3, 4]# extend() - 展开添加多个元素
numbers.extend([5, 6])
print(numbers) # [1, 2, 3, 4, 5, 6]# insert() - 在指定位置插入
numbers.insert(0, 0)
print(numbers) # [0, 1, 2, 3, 4, 5, 6]# += 操作符
numbers += [7, 8]
print(numbers) # [0, 1, 2, 3, 4, 5, 6, 7, 8]
2. 删除元素的方法
numbers = [1, 2, 3, 4, 5]# remove() - 删除指定值的第一个元素
numbers.remove(3)
print(numbers) # [1, 2, 4, 5]# pop() - 删除并返回指定位置的元素
popped = numbers.pop(1)
print(f"删除的元素:{popped}") # 删除的元素:2
print(numbers) # [1, 4, 5]# del 语句 - 删除指定位置的元素
del numbers[0]
print(numbers) # [4, 5]# clear() - 清空列表
numbers.clear()
print(numbers) # []
7. 性能考虑
append() vs extend() 性能对比:
import time# 使用 append() 添加多个元素
def test_append():numbers = []for i in range(10000):numbers.append(i)return numbers# 使用 extend() 添加多个元素
def test_extend():numbers = []numbers.extend(range(10000))return numbers# 性能测试
start = time.time()
test_append()
append_time = time.time() - startstart = time.time()
test_extend()
extend_time = time.time() - startprint(f"append() 时间:{append_time:.6f}秒")
print(f"extend() 时间:{extend_time:.6f}秒")
# extend() 通常更快,因为减少了函数调用次数
8. 常见错误和注意事项
错误1:混淆 append() 和 extend()
numbers = [1, 2, 3]# 想要添加 4, 5, 6 到列表中
numbers.append([4, 5, 6]) # 错误:会变成 [1, 2, 3, [4, 5, 6]]
print(numbers)# 正确做法
numbers = [1, 2, 3]
numbers.extend([4, 5, 6]) # 正确:会变成 [1, 2, 3, 4, 5, 6]
print(numbers)
错误2:忘记 append() 返回 None
numbers = [1, 2, 3]
result = numbers.append(4)
print(result) # None
print(numbers) # [1, 2, 3, 4]
错误3:在循环中修改列表
numbers = [1, 2, 3, 4, 5]# 错误:在循环中修改列表可能导致问题
for i in range(len(numbers)):if numbers[i] % 2 == 0:numbers.append(numbers[i] * 2) # 可能导致无限循环# 正确做法:使用副本或反向遍历
numbers = [1, 2, 3, 4, 5]
for i in range(len(numbers) - 1, -1, -1): # 反向遍历if numbers[i] % 2 == 0:numbers.append(numbers[i] * 2)
9. 最佳实践
-
选择合适的添加方法:
- 添加单个元素:使用
append()
- 添加多个元素:使用
extend()
或+=
- 在指定位置插入:使用
insert()
- 添加单个元素:使用
-
注意性能:
- 大量添加元素时,
extend()
比多次append()
更高效 - 考虑使用列表推导式或生成器
- 大量添加元素时,
-
避免常见错误:
- 记住
append()
返回None
- 在循环中修改列表要小心
- 区分嵌套列表和扁平列表
- 记住
python语法笔记之-PyCodeObject 对象详解
问题概述
PyCodeObject 是 Python 解释器内部用于表示编译后代码的核心数据结构,它是 Python 字节码执行的基础。
基本概念
1. PyCodeObject 的作用
PyCodeObject 对象是 Python 代码编译后的结果,包含:
- 字节码指令序列
- 常量表
- 变量名表
- 函数名表
- 代码对象的各种元数据
2. 代码编译过程
# Python 代码编译过程
源代码 → 语法树 → 字节码 → PyCodeObject
详细步骤:
- 词法分析:将源代码分解为标记(tokens)
- 语法分析:构建抽象语法树(AST)
- 字节码生成:将 AST 转换为字节码
- PyCodeObject 创建:将字节码和相关信息封装成 PyCodeObject
语法详解
1. 查看 PyCodeObject 的方法
方法1:使用 compile()
函数
# 编译代码并获取 PyCodeObject
source_code = "print('Hello, World!')"
code_obj = compile(source_code, '<string>', 'exec')
print(type(code_obj)) # <class 'code'>
print(code_obj) # <code object <module> at 0x...>
方法2:使用 dis
模块查看字节码
import disdef example_function():x = 10y = 20return x + y# 获取函数的 PyCodeObject
code_obj = example_function.__code__
print(f"代码对象:{code_obj}")
print(f"文件名:{code_obj.co_filename}")
print(f"函数名:{code_obj.co_name}")
print(f"参数数量:{code_obj.co_argcount}")# 反汇编查看字节码
print("\n字节码:")
dis.dis(code_obj)
2. PyCodeObject 的主要属性
def sample_function(a, b, c=10):"""示例函数"""x = a + by = x * creturn ycode_obj = sample_function.__code__# 基本信息
print(f"文件名:{code_obj.co_filename}")
print(f"函数名:{code_obj.co_name}")
print(f"行号:{code_obj.co_firstlineno}")
print(f"参数数量:{code_obj.co_argcount}")
print(f"局部变量数量:{code_obj.co_nlocals}")
print(f"栈大小:{code_obj.co_stacksize}")# 常量表
print(f"\n常量表:{code_obj.co_consts}")# 变量名表
print(f"变量名表:{code_obj.co_varnames}")# 函数名表
print(f"函数名表:{code_obj.co_names}")# 自由变量表
print(f"自由变量表:{code_obj.co_freevars}")# 单元格变量表
print(f"单元格变量表:{code_obj.co_cellvars}")
实际应用示例
1. 代码分析工具
import dis
import inspectdef analyze_function(func):"""分析函数的 PyCodeObject"""code_obj = func.__code__print(f"=== 函数分析:{func.__name__} ===")print(f"文件名:{code_obj.co_filename}")print(f"行号:{code_obj.co_firstlineno}")print(f"参数:{code_obj.co_varnames[:code_obj.co_argcount]}")print(f"局部变量:{code_obj.co_varnames}")print(f"常量:{code_obj.co_consts}")print(f"全局变量:{code_obj.co_names}")print("\n字节码:")dis.dis(func)# 测试函数
def complex_function(x, y, z=5):result = x + yif result > z:print("结果大于默认值")else:print("结果小于等于默认值")return result * 2analyze_function(complex_function)
2. 字节码优化分析
import dis
import timedef original_function():"""原始函数"""result = 0for i in range(1000):result += ireturn resultdef optimized_function():"""优化后的函数"""return sum(range(1000))# 分析字节码差异
print("=== 原始函数字节码 ===")
dis.dis(original_function)print("\n=== 优化函数字节码 ===")
dis.dis(optimized_function)# 性能对比
start = time.time()
for _ in range(10000):original_function()
original_time = time.time() - startstart = time.time()
for _ in range(10000):optimized_function()
optimized_time = time.time() - startprint(f"\n性能对比:")
print(f"原始函数:{original_time:.6f}秒")
print(f"优化函数:{optimized_time:.6f}秒")
print(f"性能提升:{original_time/optimized_time:.2f}倍")
3. 动态代码生成
import typesdef create_function_dynamically():"""动态创建函数"""# 定义字节码code = bytes([0x64, 0x01, 0x00, # LOAD_CONST 10x64, 0x02, 0x00, # LOAD_CONST 20x17, # BINARY_ADD0x53 # RETURN_VALUE])# 创建 PyCodeObjectcode_obj = types.CodeType(0, # argcount0, # posonlyargcount0, # kwonlyargcount0, # nlocals2, # stacksize67, # flags (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)code, # code(None, 1, 2), # consts(), # names(), # varnames'<string>', # filename'dynamic_func', # name1, # firstlinenob'', # lnotab(), # freevars() # cellvars)# 创建函数对象func = types.FunctionType(code_obj, globals(), 'dynamic_func')return func# 测试动态创建的函数
dynamic_func = create_function_dynamically()
print(f"动态函数结果:{dynamic_func()}") # 输出:3
4. 代码混淆和反混淆
import dis
import marshaldef obfuscate_code(source_code):"""简单的代码混淆"""# 编译代码code_obj = compile(source_code, '<string>', 'exec')# 序列化 PyCodeObjectmarshalled = marshal.dumps(code_obj)# 简单的异或加密key = 0x42encrypted = bytes(b ^ key for b in marshalled)return encrypteddef deobfuscate_code(encrypted_data):"""反混淆代码"""# 异或解密key = 0x42decrypted = bytes(b ^ key for b in encrypted_data)# 反序列化code_obj = marshal.loads(decrypted)return code_obj# 测试代码混淆
source = """
x = 10
y = 20
print(f"结果:{x + y}")
"""# 混淆
obfuscated = obfuscate_code(source)
print(f"混淆后大小:{len(obfuscated)} 字节")# 反混淆并执行
deobfuscated = deobfuscate_code(obfuscated)
exec(deobfuscated)
PyCodeObject 属性详解
1. 核心属性说明
属性 | 类型 | 说明 |
---|---|---|
co_code | bytes | 字节码指令序列 |
co_consts | tuple | 常量表 |
co_names | tuple | 全局变量名表 |
co_varnames | tuple | 局部变量名表 |
co_freevars | tuple | 自由变量名表 |
co_cellvars | tuple | 单元格变量名表 |
co_filename | str | 源文件名 |
co_name | str | 函数/类名 |
co_firstlineno | int | 第一行行号 |
co_lnotab | bytes | 行号表 |
co_argcount | int | 参数数量 |
co_posonlyargcount | int | 仅位置参数数量 |
co_kwonlyargcount | int | 仅关键字参数数量 |
co_nlocals | int | 局部变量数量 |
co_stacksize | int | 栈大小 |
co_flags | int | 代码标志 |
2. 字节码指令示例
import disdef example():x = 10y = 20z = x + yreturn zcode_obj = example.__code__print("字节码指令:")
for i, byte in enumerate(code_obj.co_code):if i % 2 == 0:print(f"{i:2d}: {byte:2d} ({dis.opname[byte]})")else:print(f" {byte:2d}")print("\n反汇编结果:")
dis.dis(code_obj)
高级应用
1. 代码注入
import types
import disdef inject_code(target_func, injection_code):"""向函数注入代码"""# 获取原始代码对象original_code = target_func.__code__# 编译注入代码injection_obj = compile(injection_code, '<injection>', 'exec')# 合并常量表new_consts = original_code.co_consts + injection_obj.co_consts# 创建新的代码对象(简化版本)new_code = types.CodeType(original_code.co_argcount,original_code.co_posonlyargcount,original_code.co_kwonlyargcount,original_code.co_nlocals,original_code.co_stacksize,original_code.co_flags,original_code.co_code,new_consts,original_code.co_names,original_code.co_varnames,original_code.co_filename,original_code.co_name,original_code.co_firstlineno,original_code.co_lnotab,original_code.co_freevars,original_code.co_cellvars)# 创建新函数new_func = types.FunctionType(new_code, target_func.__globals__, target_func.__name__)return new_func# 测试代码注入
def target_function():print("原始函数执行")return 42# 注入代码
injected_func = inject_code(target_function, "print('注入的代码执行')")# 执行
result = injected_func()
print(f"结果:{result}")
2. 性能分析工具
import dis
import time
import statisticsdef analyze_performance(func, iterations=1000):"""分析函数性能"""code_obj = func.__code__print(f"=== 性能分析:{func.__name__} ===")print(f"字节码大小:{len(code_obj.co_code)} 字节")print(f"常量数量:{len(code_obj.co_consts)}")print(f"局部变量数量:{code_obj.co_nlocals}")print(f"栈大小:{code_obj.co_stacksize}")# 执行时间分析times = []for _ in range(iterations):start = time.perf_counter()func()end = time.perf_counter()times.append(end - start)print(f"平均执行时间:{statistics.mean(times)*1000:.3f} 毫秒")print(f"标准差:{statistics.stdev(times)*1000:.3f} 毫秒")return times# 测试函数
def fast_function():return sum(range(100))def slow_function():result = 0for i in range(100):result += ireturn result# 分析性能
fast_times = analyze_performance(fast_function)
slow_times = analyze_performance(slow_function)
注意事项和最佳实践
1. 安全考虑
# 危险:执行任意代码
def dangerous_exec(code_string):code_obj = compile(code_string, '<string>', 'exec')exec(code_obj) # 可能执行恶意代码# 安全:限制执行环境
def safe_exec(code_string, allowed_globals=None):if allowed_globals is None:allowed_globals = {}code_obj = compile(code_string, '<string>', 'exec')exec(code_obj, allowed_globals) # 限制全局变量
2. 调试技巧
import dis
import sysdef debug_code_object(code_obj):"""调试代码对象"""print(f"代码对象:{code_obj}")print(f"文件名:{code_obj.co_filename}")print(f"函数名:{code_obj.co_name}")print(f"行号:{code_obj.co_firstlineno}")# 检查字节码if len(code_obj.co_code) > 0:print("字节码指令:")dis.dis(code_obj)# 检查常量if code_obj.co_consts:print(f"常量:{code_obj.co_consts}")# 检查变量if code_obj.co_varnames:print(f"变量:{code_obj.co_varnames}")# 使用示例
def test_function():x = 10y = 20return x + ydebug_code_object(test_function.__code__)
3. 内存优化
import sys
import gcdef analyze_memory_usage(func):"""分析函数的内存使用"""code_obj = func.__code__print(f"=== 内存分析:{func.__name__} ===")print(f"代码对象大小:{sys.getsizeof(code_obj)} 字节")print(f"字节码大小:{len(code_obj.co_code)} 字节")print(f"常量表大小:{sys.getsizeof(code_obj.co_consts)} 字节")print(f"变量名表大小:{sys.getsizeof(code_obj.co_varnames)} 字节")# 创建多个实例测试内存instances = []for i in range(1000):instances.append(func)print(f"1000个函数实例总大小:{sys.getsizeof(instances)} 字节")# 清理del instancesgc.collect()# 测试
def simple_function():passanalyze_memory_usage(simple_function)
python语法笔记之-PyCodeObject 对象数量分析
问题代码
class A:pass
def Fun():pass
a = A()
Fun()
问题
根据名字空间特性,以下代码经过 Python 编译器编译后,一共得到()个 PyCodeObject 对象?
答案
正确答案:3个 PyCodeObject 对象
详细分析
1. PyCodeObject 对象的创建规则
Python 编译器会为以下结构创建 PyCodeObject 对象:
- 模块级别:每个 Python 文件(模块)都会创建一个 PyCodeObject
- 函数定义:每个函数都会创建一个 PyCodeObject
- 类定义:每个类都会创建一个 PyCodeObject
- 方法定义:类中的每个方法都会创建一个 PyCodeObject
- 生成器表达式:某些情况下也会创建独立的 PyCodeObject
2. 代码结构分析
class A: # 类定义 → 创建 1 个 PyCodeObjectpassdef Fun(): # 函数定义 → 创建 1 个 PyCodeObjectpassa = A() # 实例化语句(在模块级别)
Fun() # 函数调用(在模块级别)
分析结果:
- 模块级别:整个文件作为一个模块 → 1个 PyCodeObject
- 类 A:类定义 → 1个 PyCodeObject
- 函数 Fun:函数定义 → 1个 PyCodeObject
总计:3个 PyCodeObject 对象
3. 验证方法
方法1:使用 compile()
函数
source_code = """
class A:pass
def Fun():pass
a = A()
Fun()
"""# 编译整个模块
module_code = compile(source_code, '<string>', 'exec')
print(f"模块代码对象:{module_code}")# 编译类定义
class_code = compile("class A:\n pass", '<string>', 'exec')
print(f"类代码对象:{class_code}")# 编译函数定义
func_code = compile("def Fun():\n pass", '<string>', 'exec')
print(f"函数代码对象:{func_code}")
方法2:使用 dis
模块分析
import dis# 定义测试代码
class A:passdef Fun():pass# 分析模块级别的代码对象
print("=== 模块级别代码对象 ===")
dis.dis(compile("a = A()\nFun()", '<string>', 'exec'))print("\n=== 类代码对象 ===")
dis.dis(A.__dict__['__init__'].__code__ if '__init__' in A.__dict__ else "无")print("\n=== 函数代码对象 ===")
dis.dis(Fun.__code__)
4. 名字空间特性分析
Python 的名字空间层次:
模块名字空间
├── 类 A 的名字空间
│ └── 类方法(如果有)
├── 函数 Fun 的名字空间
└── 全局变量和语句
每个名字空间对应一个 PyCodeObject:
- 模块名字空间 → 1个 PyCodeObject
- 类 A 名字空间 → 1个 PyCodeObject
- 函数 Fun 名字空间 → 1个 PyCodeObject
5. 实际验证示例
示例1:基本结构
import dis# 测试代码
test_code = """
class MyClass:passdef my_function():pass# 执行代码
obj = MyClass()
my_function()
"""# 编译并分析
compiled_code = compile(test_code, '<test>', 'exec')
print(f"编译后的代码对象:{compiled_code}")# 反汇编查看
print("\n反汇编结果:")
dis.dis(compiled_code)
示例2:更复杂的结构
class ComplexClass:def method1(self):passdef method2(self):passdef outer_function():def inner_function():passreturn inner_function# 分析代码对象数量
print("=== 代码对象分析 ===")
print(f"ComplexClass 代码对象:{ComplexClass.__dict__}")
print(f"outer_function 代码对象:{outer_function.__code__}")
print(f"inner_function 代码对象:{outer_function().__code__}")
6. 不同情况下的 PyCodeObject 数量
情况1:只有模块级代码
# 只有模块级代码
x = 10
y = 20
print(x + y)
# PyCodeObject 数量:1个(模块级别)
情况2:包含函数定义
def func():pass
# PyCodeObject 数量:2个(模块 + 函数)
情况3:包含类定义
class MyClass:pass
# PyCodeObject 数量:2个(模块 + 类)
情况4:包含类和方法
class MyClass:def method(self):pass
# PyCodeObject 数量:3个(模块 + 类 + 方法)
情况5:嵌套函数
def outer():def inner():passreturn inner
# PyCodeObject 数量:3个(模块 + outer函数 + inner函数)
7. 编译过程详解
Python 编译器的处理步骤:
- 词法分析:将源代码分解为标记
- 语法分析:构建抽象语法树(AST)
- 代码生成:为每个代码块创建 PyCodeObject
- 优化:对字节码进行优化
代码块识别规则:
- 模块级别的代码作为一个代码块
- 每个函数定义作为一个独立的代码块
- 每个类定义作为一个独立的代码块
- 类中的每个方法作为独立的代码块
8. 内存和性能考虑
PyCodeObject 的内存占用:
import sysdef analyze_code_objects():"""分析代码对象的内存占用"""# 定义测试代码class TestClass:def test_method(self):passdef test_function():pass# 分析内存占用module_size = sys.getsizeof(compile("", '<string>', 'exec'))class_size = sys.getsizeof(TestClass.__dict__['__init__'].__code__)func_size = sys.getsizeof(test_function.__code__)print(f"模块代码对象大小:{module_size} 字节")print(f"类代码对象大小:{class_size} 字节")print(f"函数代码对象大小:{func_size} 字节")print(f"总大小:{module_size + class_size + func_size} 字节")analyze_code_objects()
9. 调试和检查工具
工具1:代码对象检查器
def inspect_code_objects(source_code):"""检查源代码中的代码对象"""try:# 编译代码code_obj = compile(source_code, '<string>', 'exec')print(f"=== 代码对象分析 ===")print(f"代码对象:{code_obj}")print(f"常量:{code_obj.co_consts}")print(f"变量名:{code_obj.co_varnames}")print(f"函数名:{code_obj.co_names}")# 统计代码对象数量count = 1 # 模块级别# 检查常量中的代码对象for const in code_obj.co_consts:if hasattr(const, '__code__'):count += 1print(f"发现代码对象:{const}")print(f"总代码对象数量:{count}")except Exception as e:print(f"编译错误:{e}")# 测试
test_code = """
class A:pass
def Fun():pass
a = A()
Fun()
"""inspect_code_objects(test_code)
工具2:递归代码对象计数器
def count_code_objects(code_obj, visited=None):"""递归计算代码对象数量"""if visited is None:visited = set()if id(code_obj) in visited:return 0visited.add(id(code_obj))count = 1# 检查常量中的代码对象for const in code_obj.co_consts:if hasattr(const, '__code__'):count += count_code_objects(const.__code__, visited)return count# 使用示例
source = """
class A:pass
def Fun():pass
a = A()
Fun()
"""compiled = compile(source, '<string>', 'exec')
total_count = count_code_objects(compiled)
print(f"代码对象总数:{total_count}")
10. 常见误解和澄清
误解1:认为只有函数和类创建代码对象
- 事实:模块级别也会创建代码对象
误解2:认为语句也会创建代码对象
- 事实:只有代码块(函数、类、模块)创建代码对象,语句不创建
误解3:认为变量赋值创建代码对象
- 事实:变量赋值是语句,不创建独立的代码对象
误解4:认为导入语句创建代码对象
- 事实:导入语句在模块级别执行,不创建额外的代码对象
11. 实际应用场景
场景1:代码分析工具
def analyze_code_structure(filename):"""分析 Python 文件的代码结构"""with open(filename, 'r', encoding='utf-8') as f:source = f.read()code_obj = compile(source, filename, 'exec')print(f"=== 文件:{filename} ===")print(f"模块代码对象:{code_obj}")# 统计函数和类functions = []classes = []for name in code_obj.co_names:if name in globals():obj = globals()[name]if hasattr(obj, '__code__'):functions.append(name)elif hasattr(obj, '__dict__'):classes.append(name)print(f"函数数量:{len(functions)}")print(f"类数量:{len(classes)}")print(f"总代码对象数量:{1 + len(functions) + len(classes)}")
场景2:性能优化分析
def optimize_code_analysis(source_code):"""分析代码的优化潜力"""code_obj = compile(source_code, '<string>', 'exec')print("=== 代码优化分析 ===")print(f"字节码大小:{len(code_obj.co_code)} 字节")print(f"常量数量:{len(code_obj.co_consts)}")print(f"局部变量数量:{code_obj.co_nlocals}")print(f"栈大小:{code_obj.co_stacksize}")# 分析代码对象数量对性能的影响code_object_count = 1 # 模块级别for const in code_obj.co_consts:if hasattr(const, '__code__'):code_object_count += 1print(f"代码对象数量:{code_object_count}")print(f"平均每个代码对象大小:{len(code_obj.co_code) / code_object_count:.2f} 字节")
python语法笔记之-列表 sort() 方法语法详解
基本语法
list.sort(key=None, reverse=False)
参数说明
key
:用于排序的函数(可选),对每个元素调用一次,排序时以其返回值为依据reverse
:是否倒序排序,默认为 False(升序),设为 True 时降序
返回值
- 无返回值(返回 None),排序操作在原列表上进行
功能说明
sort()
是列表的原地排序方法,会直接修改原列表,不返回新列表- 排序算法为 Timsort,时间复杂度 O(n log n)
示例
numbers = [3, 1, 4, 2]
numbers.sort()
print(numbers) # [1, 2, 3, 4]numbers.sort(reverse=True)
print(numbers) # [4, 3, 2, 1]words = ['apple', 'banana', 'pear', 'orange']
words.sort(key=len)
print(words) # ['pear', 'apple', 'banana', 'orange']
注意事项
sort()
只能用于列表,不能用于元组、字典等- 排序是原地进行的,原列表会被修改
- 如果需要返回新排序列表而不改变原列表,请用
sorted()
常见错误
numbers = [3, 1, 2]
result = numbers.sort()
print(result) # None
# 正确做法:直接用 numbers.sort(),然后用 numbers 查看结果
高级用法
- 按对象属性排序
class Student:def __init__(self, name, score):self.name = nameself.score = scorestudents = [Student('Tom', 90), Student('Jerry', 85), Student('Alice', 95)]
students.sort(key=lambda s: s.score, reverse=True)
for s in students:print(s.name, s.score)
# 输出:Alice 95, Tom 90, Jerry 85
sort() 与 sorted() 区别
方法 | 是否原地排序 | 返回值 | 适用对象 |
---|---|---|---|
sort() | 是 | None | 仅列表 |
sorted() | 否 | 新排序后的对象 | 任意可迭代 |
典型应用场景
- 数字、字符串、对象等的排序
- 按自定义规则排序(如长度、属性、函数值等)
python语法笔记之-字典 fromkeys() 方法语法详解
基本语法
dict.fromkeys(keys, value=None)
参数说明
keys
:可迭代对象,包含要作为字典键的元素value
:可选参数,所有键的默认值,默认为 None
返回值
- 返回一个新的字典对象
功能说明
fromkeys()
是字典的类方法,用于创建一个新字典- 以给定的键和默认值创建字典
- 所有键共享同一个值对象
基本示例
# 基本用法
keys = ['a', 'b', 'c']
d1 = dict.fromkeys(keys)
print(d1) # {'a': None, 'b': None, 'c': None}d2 = dict.fromkeys(keys, 0)
print(d2) # {'a': 0, 'b': 0, 'c': 0}# 使用字符串作为键
d3 = dict.fromkeys('hello', 1)
print(d3) # {'h': 1, 'e': 1, 'l': 1, 'o': 1}# 使用元组作为键
d4 = dict.fromkeys((1, 2, 3), 'default')
print(d4) # {1: 'default', 2: 'default', 3: 'default'}
注意事项
- 所有键共享同一个值对象(如果值是可变对象,会有问题)
- 重复的键会被覆盖
- 键必须是可哈希的
常见陷阱
# 错误:所有键共享同一个列表对象
d = dict.fromkeys(['a', 'b', 'c'], [])
d['a'].append(1)
print(d) # {'a': [1], 'b': [1], 'c': [1]} - 所有键的值都变了!# 正确做法:使用字典推导式
d = {key: [] for key in ['a', 'b', 'c']}
d['a'].append(1)
print(d) # {'a': [1], 'b': [], 'c': []}# 或者使用 copy 方法
import copy
d = dict.fromkeys(['a', 'b', 'c'], [])
for key in d:d[key] = copy.deepcopy(d[key])
d['a'].append(1)
print(d) # {'a': [1], 'b': [], 'c': []}
高级用法
# 使用函数作为默认值
def create_list():return []d = dict.fromkeys(['a', 'b', 'c'], create_list())
# 注意:这样仍然有问题,因为函数只被调用一次# 正确的做法:使用字典推导式
d = {key: create_list() for key in ['a', 'b', 'c']}# 使用 lambda 表达式
d = {key: lambda: [] for key in ['a', 'b', 'c']}
# 注意:这样创建的是函数对象,不是列表
实际应用场景
# 1. 初始化计数器
counters = dict.fromkeys(['a', 'b', 'c'], 0)
print(counters) # {'a': 0, 'b': 0, 'c': 0}# 2. 初始化标志位
flags = dict.fromkeys(['feature1', 'feature2', 'feature3'], False)
print(flags) # {'feature1': False, 'feature2': False, 'feature3': False}# 3. 初始化配置
config = dict.fromkeys(['host', 'port', 'timeout'], None)
print(config) # {'host': None, 'port': None, 'timeout': None}# 4. 字符频率统计初始化
char_count = dict.fromkeys('abcdefghijklmnopqrstuvwxyz', 0)
print(char_count) # {'a': 0, 'b': 0, ..., 'z': 0}
与其他方法的对比
方法 | 语法 | 特点 | 适用场景 |
---|---|---|---|
fromkeys() | dict.fromkeys(keys, value) | 所有键共享同一值 | 初始化简单值 |
字典推导式 | {k: v for k in keys} | 每个键独立值 | 初始化复杂值 |
setdefault() | dict.setdefault(key, default) | 单个键设置默认值 | 动态添加键值对 |
性能考虑
import time# 测试 fromkeys() 性能
keys = list(range(10000))start = time.time()
d1 = dict.fromkeys(keys, 0)
time1 = time.time() - startstart = time.time()
d2 = {key: 0 for key in keys}
time2 = time.time() - startprint(f"fromkeys() 时间:{time1:.6f}秒")
print(f"字典推导式时间:{time2:.6f}秒")
# fromkeys() 通常更快,因为它是 C 实现的
常见错误和解决方案
# 错误1:使用可变对象作为默认值
d = dict.fromkeys(['a', 'b'], [])
d['a'].append(1)
print(d) # 所有键的值都变了# 解决方案1:使用字典推导式
d = {key: [] for key in ['a', 'b']}# 解决方案2:使用 copy 模块
import copy
d = dict.fromkeys(['a', 'b'], [])
for key in d:d[key] = copy.deepcopy(d[key])# 错误2:期望不同的默认值
d = dict.fromkeys(['a', 'b'], lambda: [])
# 这样创建的是函数对象,不是列表# 解决方案:使用字典推导式
d = {key: [] for key in ['a', 'b']}
最佳实践
- 使用不可变对象作为默认值:如数字、字符串、元组
- 使用字典推导式处理可变对象:如列表、字典、集合
- 注意键的重复:重复的键会被覆盖
- 考虑性能:对于大量键,
fromkeys()
比字典推导式更快
python语法笔记之-range() 函数语法详解
基本语法
range(stop) # 从0开始,步长为1
range(start, stop) # 从start开始,步长为1
range(start, stop, step) # 从start开始,指定步长
参数说明
start
:序列的起始值(可选,默认为0)stop
:序列的结束值(不包含)step
:步长(可选,默认为1)
返回值
- 返回一个 range 对象,这是一个可迭代对象
- 不是列表,但可以转换为列表
功能说明
range()
是 Python 的内置函数,用于生成一个不可变的数字序列- 内存效率高,不会立即生成所有数字
- 支持负数步长
基本示例
# 基本用法
print(list(range(5))) # [0, 1, 2, 3, 4]
print(list(range(1, 6))) # [1, 2, 3, 4, 5]
print(list(range(0, 10, 2))) # [0, 2, 4, 6, 8]# 负步长
print(list(range(5, 0, -1))) # [5, 4, 3, 2, 1]
print(list(range(10, 0, -2))) # [10, 8, 6, 4, 2]# 空序列
print(list(range(0))) # []
print(list(range(1, 1))) # []
print(list(range(5, 1))) # []
常见用法
# 1. for 循环
for i in range(5):print(i, end=' ') # 0 1 2 3 4# 2. 列表推导式
squares = [x**2 for x in range(5)]
print(squares) # [0, 1, 4, 9, 16]# 3. 索引遍历
fruits = ['apple', 'banana', 'orange']
for i in range(len(fruits)):print(f"{i}: {fruits[i]}")# 4. 倒序遍历
for i in range(len(fruits)-1, -1, -1):print(f"{i}: {fruits[i]}")
高级用法
# 1. 生成等差数列
def arithmetic_sequence(start, end, step):return list(range(start, end + 1, step))print(arithmetic_sequence(1, 10, 2)) # [1, 3, 5, 7, 9]# 2. 生成几何数列
def geometric_sequence(start, ratio, count):return [start * (ratio ** i) for i in range(count)]print(geometric_sequence(1, 2, 5)) # [1, 2, 4, 8, 16]# 3. 矩阵索引
def create_matrix(rows, cols):matrix = []for i in range(rows):row = []for j in range(cols):row.append(i * cols + j)matrix.append(row)return matrixprint(create_matrix(3, 3))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8]]# 4. 滑动窗口
def sliding_window(data, window_size):for i in range(len(data) - window_size + 1):yield data[i:i + window_size]data = [1, 2, 3, 4, 5]
for window in sliding_window(data, 3):print(window)
# [1, 2, 3]
# [2, 3, 4]
# [3, 4, 5]
性能特点
import sys# 内存效率对比
large_range = range(1000000)
large_list = list(range(1000000))print(f"range 对象大小:{sys.getsizeof(large_range)} 字节")
print(f"列表大小:{sys.getsizeof(large_list)} 字节")
# range 对象占用很少内存,而列表占用大量内存# 迭代效率
import timestart = time.time()
for i in range(1000000):pass
range_time = time.time() - startstart = time.time()
for i in list(range(1000000)):pass
list_time = time.time() - startprint(f"range 迭代时间:{range_time:.6f}秒")
print(f"列表迭代时间:{list_time:.6f}秒")
# range 迭代通常更快
实际应用场景
# 1. 计数器
for i in range(10):print(f"倒计时:{10-i}")# 2. 索引遍历
names = ['Alice', 'Bob', 'Charlie']
for i in range(len(names)):print(f"{i+1}. {names[i]}")# 3. 重复操作
def repeat_operation(func, times):for _ in range(times):func()# 4. 数值计算
def sum_range(start, end):return sum(range(start, end + 1))print(sum_range(1, 100)) # 5050# 5. 时间模拟
def simulate_time(hours):for hour in range(hours):for minute in range(60):for second in range(60):yield f"{hour:02d}:{minute:02d}:{second:02d}"# 使用示例(只显示前几个)
for time_str in simulate_time(1):print(time_str)break # 避免输出太多
常见错误和注意事项
# 错误1:期望包含结束值
print(list(range(5))) # [0, 1, 2, 3, 4] 不包含5# 错误2:负步长时的边界
print(list(range(5, 1))) # [] 空序列,因为默认步长为1# 错误3:浮点数参数
# range(1.5, 5.5) # TypeError: 'float' object cannot be interpreted as an integer# 正确做法:使用整数
print(list(range(1, 6))) # [1, 2, 3, 4, 5]# 注意事项:range 对象不是列表
r = range(5)
print(type(r)) # <class 'range'>
print(r[2]) # 2 (可以索引)
print(r[1:3]) # range(1, 3) (可以切片)
与其他序列生成方法的对比
方法 | 语法 | 特点 | 适用场景 |
---|---|---|---|
range() | range(start, stop, step) | 内存效率高,整数序列 | 循环、索引 |
list() | list(range()) | 立即生成所有元素 | 需要列表操作 |
enumerate() | enumerate(iterable) | 同时获取索引和值 | 遍历时需索引 |
itertools.count() | count(start, step) | 无限序列 | 无限循环 |
最佳实践
# 1. 使用 range 进行循环
for i in range(10):print(i)# 2. 使用 enumerate 获取索引和值
fruits = ['apple', 'banana', 'orange']
for i, fruit in enumerate(fruits):print(f"{i}: {fruit}")# 3. 使用 range 生成列表
squares = [x**2 for x in range(10)]# 4. 使用 range 进行倒序遍历
for i in range(len(fruits)-1, -1, -1):print(fruits[i])# 5. 使用 range 进行步长遍历
for i in range(0, 100, 10):print(i) # 0, 10, 20, 30, ...
高级技巧
# 1. 自定义 range 类
class CustomRange:def __init__(self, start, stop, step=1):self.start = startself.stop = stopself.step = stepdef __iter__(self):current = self.startwhile current < self.stop:yield currentcurrent += self.step# 使用示例
for i in CustomRange(1, 10, 2):print(i) # 1, 3, 5, 7, 9# 2. 生成器表达式
def fibonacci_range(n):a, b = 0, 1for _ in range(n):yield aa, b = b, a + bprint(list(fibonacci_range(10))) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]# 3. 条件 range
def conditional_range(start, stop, condition):for i in range(start, stop):if condition(i):yield i# 使用示例
even_numbers = list(conditional_range(1, 20, lambda x: x % 2 == 0))
print(even_numbers) # [2, 4, 6, 8, 10, 12, 14, 16, 18]
python语法笔记之-字典 update() 方法语法详解
基本语法
dict.update([other])
参数说明
other
:可以是字典、键值对的可迭代对象,或其他可迭代对象
返回值
- 无返回值(返回 None),直接修改原字典
功能说明
update()
是字典的方法,用于更新字典的内容- 可以添加新的键值对或更新现有的键值对
- 支持多种参数类型
基本示例
# 1. 使用字典更新
d1 = {'a': 1, 'b': 2}
d2 = {'c': 3, 'd': 4}
d1.update(d2)
print(d1) # {'a': 1, 'b': 2, 'c': 3, 'd': 4}# 2. 更新现有键
d1.update({'a': 10, 'e': 5})
print(d1) # {'a': 10, 'b': 2, 'c': 3, 'd': 4, 'e': 5}# 3. 使用键值对列表
d1.update([('f', 6), ('g', 7)])
print(d1) # {'a': 10, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7}# 4. 使用关键字参数
d1.update(h=8, i=9)
print(d1) # {'a': 10, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7, 'h': 8, 'i': 9}
不同参数类型的使用
# 1. 字典对象
config = {'host': 'localhost', 'port': 8080}
config.update({'timeout': 30, 'debug': True})
print(config) # {'host': 'localhost', 'port': 8080, 'timeout': 30, 'debug': True}# 2. 键值对列表
user_info = {'name': 'Alice', 'age': 25}
user_info.update([('email', 'alice@example.com'), ('city', 'Beijing')])
print(user_info) # {'name': 'Alice', 'age': 25, 'email': 'alice@example.com', 'city': 'Beijing'}# 3. 键值对元组
settings = {'theme': 'dark', 'language': 'en'}
settings.update((('font_size', 14), ('auto_save', True)))
print(settings) # {'theme': 'dark', 'language': 'en', 'font_size': 14, 'auto_save': True}# 4. 关键字参数
profile = {'username': 'john_doe'}
profile.update(first_name='John', last_name='Doe', age=30)
print(profile) # {'username': 'john_doe', 'first_name': 'John', 'last_name': 'Doe', 'age': 30}# 5. 混合使用
data = {'id': 1}
data.update({'status': 'active'}, created_at='2024-01-01', updated_at='2024-01-02')
print(data) # {'id': 1, 'status': 'active', 'created_at': '2024-01-01', 'updated_at': '2024-01-02'}
实际应用场景
# 1. 配置管理
def load_config():base_config = {'host': 'localhost','port': 8080,'timeout': 30}# 从文件加载配置file_config = {'port': 9000, # 覆盖默认端口'debug': True}# 从环境变量加载配置env_config = {'host': 'production.server.com','ssl': True}# 按优先级更新配置base_config.update(file_config)base_config.update(env_config)return base_configconfig = load_config()
print(config) # {'host': 'production.server.com', 'port': 9000, 'timeout': 30, 'debug': True, 'ssl': True}# 2. 用户信息合并
def merge_user_profiles(profile1, profile2):"""合并两个用户配置文件"""merged = profile1.copy() # 创建副本避免修改原字典merged.update(profile2)return mergedprofile1 = {'name': 'Alice', 'age': 25, 'email': 'alice@example.com'}
profile2 = {'age': 26, 'city': 'Beijing', 'phone': '123-456-7890'}merged_profile = merge_user_profiles(profile1, profile2)
print(merged_profile) # {'name': 'Alice', 'age': 26, 'email': 'alice@example.com', 'city': 'Beijing', 'phone': '123-456-7890'}# 3. 数据库记录更新
def update_database_record(record_id, updates):"""更新数据库记录"""# 模拟从数据库获取记录record = {'id': record_id,'name': 'Product A','price': 100,'category': 'Electronics','created_at': '2024-01-01'}# 应用更新record.update(updates)record['updated_at'] = '2024-01-02'return recordupdates = {'price': 120, 'category': 'Gadgets', 'stock': 50}
updated_record = update_database_record(1, updates)
print(updated_record)# 4. API 响应处理
def process_api_response(base_response, additional_data):"""处理 API 响应数据"""response = base_response.copy()response.update(additional_data)return responsebase_response = {'status': 'success','code': 200,'message': 'Operation completed'
}additional_data = {'data': {'user_id': 123, 'username': 'john_doe'},'timestamp': '2024-01-01T12:00:00Z'
}final_response = process_api_response(base_response, additional_data)
print(final_response)
高级用法
# 1. 条件更新
def conditional_update(target_dict, source_dict, condition_func):"""根据条件更新字典"""for key, value in source_dict.items():if condition_func(key, value):target_dict[key] = valueconfig = {'debug': False, 'verbose': False, 'log_level': 'INFO'}
updates = {'debug': True, 'verbose': True, 'log_level': 'DEBUG', 'max_retries': 3}# 只更新布尔值
conditional_update(config, updates, lambda k, v: isinstance(v, bool))
print(config) # {'debug': True, 'verbose': True, 'log_level': 'INFO'}# 2. 深度更新(简化版)
def deep_update(target, source):"""深度更新字典"""for key, value in source.items():if key in target and isinstance(target[key], dict) and isinstance(value, dict):deep_update(target[key], value)else:target[key] = valuenested_config = {'database': {'host': 'localhost','port': 3306},'cache': {'enabled': False}
}updates = {'database': {'host': 'production.db.com','ssl': True},'cache': {'enabled': True,'ttl': 3600}
}deep_update(nested_config, updates)
print(nested_config)# 3. 批量更新
def batch_update(target_dict, updates_list):"""批量更新字典"""for update_dict in updates_list:target_dict.update(update_dict)user_data = {'id': 1, 'name': 'Alice'}updates_batch = [{'email': 'alice@example.com'},{'age': 25},{'city': 'Beijing'},{'age': 26} # 覆盖之前的年龄
]batch_update(user_data, updates_batch)
print(user_data) # {'id': 1, 'name': 'Alice', 'email': 'alice@example.com', 'age': 26, 'city': 'Beijing'}
性能考虑
import time# 性能测试:update() vs 直接赋值
def test_update_performance():# 准备测试数据base_dict = {f'key_{i}': i for i in range(1000)}update_dict = {f'new_key_{i}': i for i in range(1000)}# 测试 update() 方法start = time.time()for _ in range(10000):test_dict = base_dict.copy()test_dict.update(update_dict)update_time = time.time() - start# 测试直接赋值start = time.time()for _ in range(10000):test_dict = base_dict.copy()for key, value in update_dict.items():test_dict[key] = valueassign_time = time.time() - startprint(f"update() 方法时间:{update_time:.6f}秒")print(f"直接赋值时间:{assign_time:.6f}秒")print(f"性能比:{assign_time/update_time:.2f}")test_update_performance()
常见错误和注意事项
# 错误1:期望返回值
d1 = {'a': 1}
result = d1.update({'b': 2})
print(result) # None# 错误2:使用不可哈希的键
try:d1.update({[1, 2]: 'value'}) # TypeError: unhashable type: 'list'
except TypeError as e:print(f"错误:{e}")# 错误3:参数类型错误
try:d1.update(123) # TypeError: 'int' object is not iterable
except TypeError as e:print(f"错误:{e}")# 注意事项1:update() 会修改原字典
original = {'a': 1}
copy_dict = original.copy()
copy_dict.update({'b': 2})
print(f"原字典:{original}") # {'a': 1}
print(f"副本:{copy_dict}") # {'a': 1, 'b': 2}# 注意事项2:重复键会被覆盖
d1 = {'a': 1, 'b': 2}
d1.update({'a': 10, 'c': 3})
print(d1) # {'a': 10, 'b': 2, 'c': 3}
与其他方法的对比
方法 | 语法 | 特点 | 适用场景 |
---|---|---|---|
update() | dict.update(other) | 批量更新,原地修改 | 合并多个字典 |
dict() | dict(**d1, **d2) | 创建新字典 | Python 3.5+ |
` | ` 操作符 | d1 | d2 | 创建新字典 |
` | =` 操作符 | d1 |= d2 | 原地更新 |
最佳实践
# 1. 使用 copy() 避免修改原字典
def safe_update(base_dict, updates):"""安全更新字典,不修改原字典"""result = base_dict.copy()result.update(updates)return result# 2. 使用类型检查
def validate_update_dict(updates):"""验证更新字典的有效性"""if not isinstance(updates, dict):raise TypeError("更新数据必须是字典类型")for key, value in updates.items():if not isinstance(key, (str, int, float, bool, tuple)):raise TypeError(f"键 {key} 必须是可哈希类型")return True# 3. 使用默认值
def update_with_defaults(target_dict, updates, defaults=None):"""使用默认值更新字典"""if defaults is None:defaults = {}# 先应用默认值target_dict.update(defaults)# 再应用更新target_dict.update(updates)return target_dict# 使用示例
config = {'host': 'localhost'}
updates = {'port': 8080}
defaults = {'timeout': 30, 'retries': 3}final_config = update_with_defaults(config, updates, defaults)
print(final_config) # {'host': 'localhost', 'timeout': 30, 'retries': 3, 'port': 8080}
python语法笔记之-正则表达式 groups 语法详解
问题代码
import restrs = 'Type:This is Python'
res = re.match('Type:(\w+) is (\w+)', strs)
print(res.groups())
print(res.group(1))
print(res.group(2))
输出结果
('This', 'Python')
This
Python
语法详解
1. 正则表达式模式分析
'Type:(\w+) is (\w+)'
模式分解:
Type:
- 字面匹配字符串 “Type:”(\w+)
- 第一个捕获组:匹配一个或多个单词字符is
- 字面匹配字符串 " is "(\w+)
- 第二个捕获组:匹配一个或多个单词字符
2. groups() 方法详解
res.groups()
返回一个元组,包含所有捕获组的内容:
print(res.groups()) # ('This', 'Python')
特点:
- 返回所有捕获组的值组成的元组
- 不包含完整匹配的字符串
- 按捕获组的顺序排列
3. group() 方法详解
res.group(n)
返回指定编号的捕获组:
print(res.group(0)) # 'Type:This is Python' # 完整匹配
print(res.group(1)) # 'This' # 第一个捕获组
print(res.group(2)) # 'Python' # 第二个捕获组
编号规则:
group(0)
- 完整匹配的字符串group(1)
- 第一个捕获组group(2)
- 第二个捕获组- 以此类推…
4. 捕获组的概念
捕获组是用括号 ()
包围的正则表达式部分:
# 示例1:基本捕获组
pattern = r'(\d{3})-(\d{3})-(\d{4})'
text = "123-456-7890"
match = re.match(pattern, text)
print(match.groups()) # ('123', '456', '7890')# 示例2:嵌套捕获组
pattern = r'(\w+(\d+))'
text = "abc123"
match = re.match(pattern, text)
print(match.groups()) # ('abc123', '123')
5. 实际应用示例
示例1:解析日志文件
import relog_line = "2024-01-01 10:30:45 [INFO] User login successful"
pattern = r'(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) \[(\w+)\] (.+)'match = re.match(pattern, log_line)
if match:date, time, level, message = match.groups()print(f"日期: {date}")print(f"时间: {time}")print(f"级别: {level}")print(f"消息: {message}")
示例2:解析邮箱地址
email = "user@example.com"
pattern = r'([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+)\.([a-zA-Z]{2,})'match = re.match(pattern, email)
if match:username, domain, tld = match.groups()print(f"用户名: {username}")print(f"域名: {domain}")print(f"顶级域名: {tld}")
示例3:解析配置文件
config_line = "database.host=localhost"
pattern = r'([^.]+)\.([^=]+)=(.+)'match = re.match(pattern, config_line)
if match:section, key, value = match.groups()print(f"节: {section}")print(f"键: {key}")print(f"值: {value}")
6. 命名捕获组
使用 (?P<name>pattern)
语法:
pattern = r'Type:(?P<first>\w+) is (?P<second>\w+)'
text = 'Type:This is Python'match = re.match(pattern, text)
if match:print(match.groups()) # ('This', 'Python')print(match.group('first')) # 'This'print(match.group('second')) # 'Python'print(match.groupdict()) # {'first': 'This', 'second': 'Python'}
7. 非捕获组
使用 (?:pattern)
语法:
# 普通捕获组
pattern1 = r'(\w+) is (\w+)'
text = "This is Python"
match1 = re.match(pattern1, text)
print(match1.groups()) # ('This', 'Python')# 非捕获组
pattern2 = r'(?:\w+) is (\w+)'
match2 = re.match(pattern2, text)
print(match2.groups()) # ('Python',) - 只有第二个组被捕获
8. 高级用法
示例1:条件匹配
# 匹配 "Type:xxx is yyy" 或 "Type:xxx are yyy"
pattern = r'Type:(\w+) (?:is|are) (\w+)'
text1 = "Type:This is Python"
text2 = "Type:These are examples"match1 = re.match(pattern, text1)
match2 = re.match(pattern, text2)print(match1.groups()) # ('This', 'Python')
print(match2.groups()) # ('These', 'examples')
示例2:重复捕获组
# 匹配多个数字组
pattern = r'(\d+)(?:,(\d+))*'
text = "1,2,3,4"match = re.match(pattern, text)
print(match.groups()) # ('1', '4') - 只捕获第一个和最后一个
9. 常见错误和注意事项
错误1:访问不存在的组
pattern = r'(\w+)'
text = "Hello"
match = re.match(pattern, text)try:print(match.group(2)) # IndexError: no such group
except IndexError as e:print(f"错误:{e}")
错误2:匹配失败时访问组
pattern = r'(\d+)'
text = "Hello"
match = re.match(pattern, text)if match: # 总是检查匹配是否成功print(match.groups())
else:print("没有匹配")
注意事项:
# 1. groups() 返回元组,group() 返回字符串
match = re.match(r'(\w+)', "Hello")
print(type(match.groups())) # <class 'tuple'>
print(type(match.group(1))) # <class 'str'># 2. 空匹配的处理
pattern = r'(\w*)'
text = ""
match = re.match(pattern, text)
print(match.groups()) # ('',) - 空字符串
10. 性能优化建议
# 1. 编译正则表达式以提高性能
import repattern = re.compile(r'Type:(\w+) is (\w+)')
text = "Type:This is Python"match = pattern.match(text) # 比 re.match(pattern, text) 更快
if match:print(match.groups())# 2. 使用 findall() 获取所有匹配
text = "Type:This is Python, Type:That is Java"
pattern = r'Type:(\w+) is (\w+)'matches = re.findall(pattern, text)
print(matches) # [('This', 'Python'), ('That', 'Java')]
11. 实际应用场景
场景1:数据提取
# 从文本中提取价格信息
text = "商品价格: $99.99, 折扣价: $79.99"
pattern = r'\$(\d+\.\d+)'prices = re.findall(pattern, text)
print(f"找到的价格: {prices}") # ['99.99', '79.99']
场景2:数据验证
# 验证电话号码格式
def validate_phone(phone):pattern = r'^(\d{3})-(\d{3})-(\d{4})$'match = re.match(pattern, phone)if match:area, prefix, line = match.groups()return True, f"区号: {area}, 前缀: {prefix}, 线路: {line}"return False, "格式错误"print(validate_phone("123-456-7890")) # (True, '区号: 123, 前缀: 456, 线路: 7890')
print(validate_phone("123-456")) # (False, '格式错误')
场景3:文本处理
# 解析日志文件
log_lines = ["2024-01-01 10:30:45 [INFO] User login successful","2024-01-01 10:31:12 [ERROR] Database connection failed","2024-01-01 10:32:00 [WARN] High memory usage detected"
]pattern = r'(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) \[(\w+)\] (.+)'for line in log_lines:match = re.match(pattern, line)if match:date, time, level, message = match.groups()print(f"[{level}] {message}")
12. 总结
正则表达式中的 groups
相关方法:
方法 | 功能 | 返回值 |
---|---|---|
groups() | 返回所有捕获组 | 元组 |
group(0) | 返回完整匹配 | 字符串 |
group(n) | 返回第n个捕获组 | 字符串 |
groupdict() | 返回命名捕获组 | 字典 |
关键要点:
- 捕获组用括号
()
定义 groups()
返回所有捕获组的元组group(n)
返回指定编号的捕获组- 编号从1开始,0表示完整匹配
- 总是检查匹配是否成功再访问组
python语法笔记之-原始字符串(Raw String)语法详解
问题代码
print r"\nwoow"
输出结果
\nwoow
语法详解
1. 原始字符串的概念
原始字符串(Raw String) 是 Python 中的一种字符串字面量,使用前缀 r
或 R
表示。在原始字符串中,反斜杠 \
不会被转义,而是作为普通字符处理。
2. 代码执行分析
print r"\nwoow" # 输出:\nwoow
print "\nwoow" # 输出:换行 + woow
对比分析:
r"\nwoow"
- 原始字符串,\n
被当作两个字符\
和n
"\nwoow"
- 普通字符串,\n
被转义为换行符
3. 转义字符对比
转义序列 | 普通字符串 | 原始字符串 | 说明 |
---|---|---|---|
\n | 换行符 | \n | 换行 |
\t | 制表符 | \t | 制表符 |
\r | 回车符 | \r | 回车 |
\\ | \ | \\ | 反斜杠 |
\" | " | \" | 双引号 |
4. 实际应用示例
示例1:文件路径
# 普通字符串 - 需要双反斜杠
path1 = "C:\\Users\\Documents\\file.txt"
print(path1) # C:\Users\Documents\file.txt# 原始字符串 - 更简洁
path2 = r"C:\Users\Documents\file.txt"
print(path2) # C:\Users\Documents\file.txt
示例2:正则表达式
import re# 普通字符串 - 需要转义
pattern1 = "\\d+\\s+\\w+"
text = "123 abc"
match1 = re.search(pattern1, text)# 原始字符串 - 更清晰
pattern2 = r"\d+\s+\w+"
match2 = re.search(pattern2, text)print(match1.group() if match1 else "无匹配") # 123 abc
print(match2.group() if match2 else "无匹配") # 123 abc
示例3:Windows 路径处理
# 处理 Windows 路径
windows_path = r"C:\Program Files\Python\Scripts"
print(windows_path) # C:\Program Files\Python\Scripts# 分割路径
path_parts = windows_path.split('\\')
print(path_parts) # ['C:', 'Program Files', 'Python', 'Scripts']
5. 高级用法
示例1:多行原始字符串
# 多行原始字符串
multiline_raw = r"""
这是一个多行
原始字符串
包含 \n \t \r 等字符
"""
print(multiline_raw)
示例2:正则表达式中的复杂模式
import re# 复杂的正则表达式
email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
email = "user@example.com"if re.match(email_pattern, email):print("有效的邮箱地址")
else:print("无效的邮箱地址")
示例3:SQL 查询字符串
# SQL 查询
sql_query = r"""
SELECT name, age, city
FROM users
WHERE age > 18 AND city = 'Beijing'
"""
print(sql_query)
6. 常见错误和注意事项
错误1:在原始字符串中使用引号
# 错误:原始字符串中的引号问题
try:print(r"这是一个"问题"的字符串") # SyntaxError
except SyntaxError as e:print(f"语法错误:{e}")# 正确:使用不同类型的引号
print(r'这是一个"正确"的字符串') # 这是一个"正确"的字符串
print(r"这是一个'正确'的字符串") # 这是一个'正确'的字符串
错误2:原始字符串末尾的反斜杠
# 错误:原始字符串末尾的反斜杠
try:print(r"C:\Users\") # SyntaxError
except SyntaxError as e:print(f"语法错误:{e}")# 正确:使用字符串连接或转义
print(r"C:\Users" + "\\") # C:\Users\
print(r"C:\Users\\") # C:\Users\
注意事项:
# 1. 原始字符串中的反斜杠数量
print(r"\\") # \\
print("\\\\") # \\# 2. 原始字符串中的其他转义序列
print(r"\x41") # \x41 (不是字符 'A')
print("\x41") # A# 3. 原始字符串中的 Unicode 转义
print(r"\u0041") # \u0041 (不是字符 'A')
print("\u0041") # A
7. 实际应用场景
场景1:配置文件路径
# 配置文件路径
config_path = r"C:\App\config\settings.ini"
backup_path = r"C:\App\backup\settings_backup.ini"print(f"配置文件:{config_path}")
print(f"备份文件:{backup_path}")
场景2:日志文件路径
import os
from datetime import datetime# 日志文件路径
log_dir = r"C:\Logs\Application"
log_file = os.path.join(log_dir, f"app_{datetime.now().strftime('%Y%m%d')}.log")print(f"日志文件:{log_file}")
场景3:网络路径
# 网络共享路径
network_path = r"\\server\share\documents"
unc_path = r"\\192.168.1.100\public\files"print(f"网络路径:{network_path}")
print(f"UNC路径:{unc_path}")
场景4:正则表达式模式
import re# 复杂的正则表达式模式
phone_pattern = r'^(\+?1)?[-.\s]?\(?([0-9]{3})\)?[-.\s]?([0-9]{3})[-.\s]?([0-9]{4})$'
phone_numbers = ["123-456-7890","(123) 456-7890","123.456.7890","+1-123-456-7890"
]for phone in phone_numbers:if re.match(phone_pattern, phone):print(f"有效号码:{phone}")else:print(f"无效号码:{phone}")
8. 性能考虑
import time# 性能测试:原始字符串 vs 普通字符串
def test_raw_string_performance():# 测试原始字符串start = time.time()for _ in range(1000000):path = r"C:\Users\Documents\file.txt"raw_time = time.time() - start# 测试普通字符串start = time.time()for _ in range(1000000):path = "C:\\Users\\Documents\\file.txt"normal_time = time.time() - startprint(f"原始字符串时间:{raw_time:.6f}秒")print(f"普通字符串时间:{normal_time:.6f}秒")print(f"性能差异:{normal_time/raw_time:.2f}倍")test_raw_string_performance()
9. 最佳实践
# 1. 文件路径使用原始字符串
file_path = r"C:\Users\Documents\file.txt"# 2. 正则表达式使用原始字符串
import re
pattern = r"\d{3}-\d{3}-\d{4}"# 3. 多行字符串使用原始字符串
sql_query = r"""
SELECT * FROM users
WHERE age > 18
ORDER BY name
"""# 4. 包含大量反斜杠的字符串使用原始字符串
windows_command = r"cmd /c dir C:\Users\*.*"# 5. 避免在原始字符串末尾使用反斜杠
# 错误:path = r"C:\Users\"
# 正确:path = r"C:\Users" + "\\"
10. 与其他字符串类型的对比
字符串类型 | 前缀 | 特点 | 适用场景 |
---|---|---|---|
普通字符串 | 无 | 支持转义字符 | 一般文本 |
原始字符串 | r 或 R | 不转义反斜杠 | 文件路径、正则表达式 |
字节字符串 | b 或 B | 字节序列 | 二进制数据 |
Unicode字符串 | u 或 U | Unicode字符 | 国际化文本 |
11. 总结
原始字符串的优势:
- 简化路径表示:不需要双反斜杠
- 提高可读性:正则表达式更清晰
- 减少错误:避免转义字符错误
- 提高性能:解析速度更快
使用建议:
- 文件路径和目录路径
- 正则表达式模式
- 包含大量反斜杠的字符串
- 多行字符串
- 网络路径和 UNC 路径
注意事项:
- 原始字符串中的引号需要配对
- 末尾反斜杠需要特殊处理
- 某些转义序列在原始字符串中不起作用
python语法笔记之-浅拷贝(Shallow Copy)语法详解
问题代码
a = [1, [2, 3], 4]
b = a[:]
a[0] = 5
a[1][1] = 6
执行结果分析
print(f"a = {a}") # a = [5, [2, 6], 4]
print(f"b = {b}") # b = [1, [2, 6], 4]
语法详解
1. 浅拷贝的概念
浅拷贝(Shallow Copy) 是创建一个新对象,但它包含的是对原始对象中元素的引用。对于嵌套的可变对象,浅拷贝只复制引用,不复制对象本身。
2. 代码执行过程分析
# 步骤1:创建原始列表
a = [1, [2, 3], 4]
# a 包含:整数1,列表[2,3]的引用,整数4# 步骤2:浅拷贝
b = a[:] # 等价于 b = a.copy() 或 b = list(a)
# b 包含:整数1的副本,列表[2,3]的引用,整数4的副本# 步骤3:修改不可变元素
a[0] = 5
# a 变成 [5, [2, 3], 4]
# b 仍然是 [1, [2, 3], 4] - 不受影响# 步骤4:修改嵌套的可变元素
a[1][1] = 6
# a 变成 [5, [2, 6], 4]
# b 变成 [1, [2, 6], 4] - 受影响!
3. 内存结构分析
浅拷贝前:
a → [1, [2, 3], 4]
浅拷贝后:
a → [1, [2, 3], 4]
b → [1, [2, 3], 4] # 新列表,但包含对相同嵌套对象的引用
修改后:
a → [5, [2, 6], 4] # 修改了第一个元素和嵌套列表
b → [1, [2, 6], 4] # 第一个元素不变,但嵌套列表被共享修改
4. 不同拷贝方式的对比
import copy# 原始列表
original = [1, [2, 3], 4]# 1. 浅拷贝 - 切片操作
shallow1 = original[:]# 2. 浅拷贝 - copy() 方法
shallow2 = original.copy()# 3. 浅拷贝 - list() 构造函数
shallow3 = list(original)# 4. 浅拷贝 - copy.copy()
shallow4 = copy.copy(original)# 5. 深拷贝 - copy.deepcopy()
deep = copy.deepcopy(original)# 测试修改
original[0] = 10
original[1][0] = 20print(f"原始列表: {original}") # [10, [20, 3], 4]
print(f"浅拷贝1: {shallow1}") # [1, [20, 3], 4]
print(f"浅拷贝2: {shallow2}") # [1, [20, 3], 4]
print(f"浅拷贝3: {shallow3}") # [1, [20, 3], 4]
print(f"浅拷贝4: {shallow4}") # [1, [20, 3], 4]
print(f"深拷贝: {deep}") # [1, [2, 3], 4] - 完全独立
5. 实际应用示例
示例1:配置管理
# 默认配置
default_config = {'host': 'localhost','port': 8080,'database': {'name': 'mydb','user': 'admin'}
}# 创建环境特定配置(浅拷贝)
dev_config = default_config.copy()
dev_config['port'] = 3000
dev_config['database']['user'] = 'dev_user'print(f"默认配置: {default_config}")
print(f"开发配置: {dev_config}")
# 注意:database 配置被共享修改了!
示例2:游戏状态管理
# 游戏状态
game_state = {'player': {'health': 100, 'position': [10, 20]},'enemies': [{'health': 50, 'position': [5, 15]}],'score': 0
}# 保存游戏状态(浅拷贝)
saved_state = game_state.copy()# 修改当前状态
game_state['score'] = 100
game_state['player']['health'] = 80
game_state['enemies'][0]['health'] = 30print(f"当前状态: {game_state}")
print(f"保存状态: {saved_state}")
# 注意:嵌套对象被共享修改了!
示例3:列表操作
# 学生成绩列表
students = [{'name': 'Alice', 'grades': [85, 90, 88]},{'name': 'Bob', 'grades': [92, 87, 91]},{'name': 'Charlie', 'grades': [78, 85, 82]}
]# 创建备份(浅拷贝)
backup = students[:]# 修改原始数据
students[0]['name'] = 'Alice Smith'
students[1]['grades'][0] = 95print("原始数据:")
for student in students:print(f" {student['name']}: {student['grades']}")print("备份数据:")
for student in backup:print(f" {student['name']}: {student['grades']}")
# 注意:grades 列表被共享修改了!
6. 深拷贝 vs 浅拷贝
import copy# 复杂嵌套结构
nested_data = {'numbers': [1, 2, 3],'strings': ['a', 'b', 'c'],'nested': {'list': [10, 20, 30],'dict': {'x': 100, 'y': 200}}
}# 浅拷贝
shallow = nested_data.copy()# 深拷贝
deep = copy.deepcopy(nested_data)# 修改原始数据
nested_data['numbers'][0] = 999
nested_data['nested']['list'][0] = 888
nested_data['nested']['dict']['x'] = 777print("原始数据:")
print(f" numbers: {nested_data['numbers']}")
print(f" nested.list: {nested_data['nested']['list']}")
print(f" nested.dict: {nested_data['nested']['dict']}")print("浅拷贝:")
print(f" numbers: {shallow['numbers']}")
print(f" nested.list: {shallow['nested']['list']}")
print(f" nested.dict: {shallow['nested']['dict']}")print("深拷贝:")
print(f" numbers: {deep['numbers']}")
print(f" nested.list: {deep['nested']['list']}")
print(f" nested.dict: {deep['nested']['dict']}")
7. 常见错误和注意事项
错误1:误以为浅拷贝是完全独立的
# 错误理解
a = [1, [2, 3], 4]
b = a[:]
a[1][0] = 999
print(f"a: {a}") # [1, [999, 3], 4]
print(f"b: {b}") # [1, [999, 3], 4] - 意外被修改!
错误2:嵌套列表的浅拷贝陷阱
# 嵌套列表
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
copy_matrix = matrix[:]# 修改一行
copy_matrix[0][0] = 999print("原始矩阵:")
for row in matrix:print(row)print("拷贝矩阵:")
for row in copy_matrix:print(row)
# 两个矩阵都被修改了!
注意事项:
# 1. 不可变对象不受影响
a = [1, "hello", (2, 3)]
b = a[:]
a[0] = 999
a[1] = "world"
# a[2] = (4, 5) # 元组不可变,不能修改print(f"a: {a}") # [999, 'world', (2, 3)]
print(f"b: {b}") # [1, 'hello', (2, 3)] - 不受影响# 2. 可变对象被共享
a = [1, [2, 3], {'x': 10}]
b = a[:]
a[1].append(4)
a[2]['y'] = 20print(f"a: {a}") # [1, [2, 3, 4], {'x': 10, 'y': 20}]
print(f"b: {b}") # [1, [2, 3, 4], {'x': 10, 'y': 20}] - 受影响!
8. 性能考虑
import time
import copy# 创建大型嵌套结构
large_data = {'list': list(range(10000)),'nested': {'data': list(range(1000)),'more_data': list(range(1000))}
}# 测试浅拷贝性能
start = time.time()
shallow_copy = large_data.copy()
shallow_time = time.time() - start# 测试深拷贝性能
start = time.time()
deep_copy = copy.deepcopy(large_data)
deep_time = time.time() - startprint(f"浅拷贝时间: {shallow_time:.6f}秒")
print(f"深拷贝时间: {deep_time:.6f}秒")
print(f"性能差异: {deep_time/shallow_time:.2f}倍")
# 深拷贝通常比浅拷贝慢很多
9. 最佳实践
import copy# 1. 明确选择拷贝类型
def create_backup(data, deep=False):"""创建数据备份"""if deep:return copy.deepcopy(data)else:return data.copy()# 2. 处理嵌套结构
def safe_copy_nested_list(original):"""安全拷贝嵌套列表"""if not original:return []result = []for item in original:if isinstance(item, list):result.append(safe_copy_nested_list(item))else:result.append(item)return result# 3. 使用深拷贝避免意外修改
def update_config(base_config, updates):"""更新配置,避免修改原始配置"""config = copy.deepcopy(base_config)config.update(updates)return config# 使用示例
base_config = {'host': 'localhost', 'database': {'name': 'mydb'}}
updates = {'port': 8080, 'database': {'user': 'admin'}}new_config = update_config(base_config, updates)
print(f"原始配置: {base_config}")
print(f"新配置: {new_config}")
10. 实际应用场景
场景1:数据备份
# 用户数据
user_data = {'profile': {'name': 'Alice', 'age': 25},'preferences': {'theme': 'dark', 'language': 'en'},'history': ['page1', 'page2', 'page3']
}# 创建备份
backup = user_data.copy()# 修改原始数据
user_data['profile']['age'] = 26
user_data['history'].append('page4')print("原始数据:")
print(f" 年龄: {user_data['profile']['age']}")
print(f" 历史: {user_data['history']}")print("备份数据:")
print(f" 年龄: {backup['profile']['age']}")
print(f" 历史: {backup['history']}")
场景2:模板系统
# 邮件模板
email_template = {'subject': 'Welcome','body': 'Hello {name}, welcome to our service!','attachments': [],'settings': {'priority': 'normal', 'format': 'html'}
}# 创建个性化邮件
def create_personalized_email(template, name):email = template.copy() # 浅拷贝email['body'] = email['body'].format(name=name)email['attachments'].append(f'{name}_welcome.pdf')return email# 使用模板
alice_email = create_personalized_email(email_template, 'Alice')
bob_email = create_personalized_email(email_template, 'Bob')print("Alice邮件:", alice_email)
print("Bob邮件:", bob_email)
print("原始模板:", email_template)
场景3:游戏状态管理
# 游戏状态
class GameState:def __init__(self):self.players = [{'health': 100, 'position': [0, 0]}]self.enemies = [{'health': 50, 'position': [10, 10]}]self.score = 0def save_state(self):"""保存游戏状态"""return {'players': self.players.copy(),'enemies': self.enemies.copy(),'score': self.score}def load_state(self, saved_state):"""加载游戏状态"""self.players = saved_state['players']self.enemies = saved_state['enemies']self.score = saved_state['score']# 使用示例
game = GameState()
saved = game.save_state()# 修改游戏状态
game.players[0]['health'] = 80
game.enemies[0]['position'][0] = 15print("当前状态:", game.players, game.enemies)
print("保存状态:", saved['players'], saved['enemies'])
11. 总结
浅拷贝的特点:
- 创建新对象:外层容器是新的
- 共享嵌套对象:嵌套的可变对象被共享
- 性能较好:比深拷贝快
- 内存节省:不复制嵌套对象
使用场景:
- 需要独立的外层容器
- 嵌套对象可以共享
- 性能要求较高
- 内存使用有限制
注意事项:
- 嵌套的可变对象会被共享修改
- 不可变对象不受影响
- 需要深拷贝时使用
copy.deepcopy()
- 理解数据结构的可变性
python语法笔记之-解包操作符(*)语法详解
问题代码
def fn(a, b):return a + blst = [1, 2]
f = fn(*lst)
print(f)
输出结果
3
语法详解
1. 解包操作符的概念
解包操作符(Unpacking Operator) *
用于将可迭代对象(如列表、元组)展开为独立的参数传递给函数。
2. 代码执行过程分析
def fn(a, b):return a + blst = [1, 2]
f = fn(*lst) # 等价于 fn(1, 2)
print(f) # 输出:3
执行步骤:
- 定义函数
fn(a, b)
,接受两个参数 - 创建列表
lst = [1, 2]
- 使用
*lst
将列表解包为独立参数 - 函数调用变为
fn(1, 2)
- 返回
1 + 2 = 3
3. 解包操作符的用法
基本用法:
# 1. 函数参数解包
def add(a, b, c):return a + b + cnumbers = [1, 2, 3]
result = add(*numbers) # 等价于 add(1, 2, 3)
print(result) # 6# 2. 列表解包
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = [*list1, *list2] # [1, 2, 3, 4, 5, 6]
print(combined)# 3. 元组解包
point = (10, 20)
x, y = point # 解包赋值
print(f"x: {x}, y: {y}") # x: 10, y: 20
4. 实际应用示例
示例1:可变参数函数
def calculate_sum(*args):return sum(args)# 使用解包操作符
numbers = [1, 2, 3, 4, 5]
result = calculate_sum(*numbers)
print(result) # 15# 等价于
result = calculate_sum(1, 2, 3, 4, 5)
print(result) # 15
示例2:字典解包
def print_info(name, age, city):print(f"姓名: {name}, 年龄: {age}, 城市: {city}")# 使用字典解包
person = {'name': 'Alice', 'age': 25, 'city': 'Beijing'}
print_info(**person) # 姓名: Alice, 年龄: 25, 城市: Beijing# 等价于
print_info(name='Alice', age=25, city='Beijing')
示例3:列表合并
# 合并多个列表
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]# 使用解包操作符
merged = [*list1, *list2, *list3]
print(merged) # [1, 2, 3, 4, 5, 6, 7, 8, 9]# 等价于
merged = list1 + list2 + list3
print(merged) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
5. 高级用法
示例1:函数装饰器
def log_function_call(func):def wrapper(*args, **kwargs):print(f"调用函数: {func.__name__}")print(f"参数: {args}, {kwargs}")result = func(*args, **kwargs)print(f"返回值: {result}")return resultreturn wrapper@log_function_call
def add_numbers(a, b, c):return a + b + c# 使用解包操作符
numbers = [1, 2, 3]
result = add_numbers(*numbers)
示例2:类方法调用
class Point:def __init__(self, x, y):self.x = xself.y = ydef distance_to(self, other):return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5# 创建点对象
p1 = Point(0, 0)
coords = [3, 4]
p2 = Point(*coords) # 等价于 Point(3, 4)distance = p1.distance_to(p2)
print(f"距离: {distance}") # 5.0
示例3:数据转换
# 将字符串转换为整数列表
number_strings = ['1', '2', '3', '4', '5']
numbers = [int(x) for x in number_strings]# 使用解包操作符计算最大值
max_value = max(*numbers)
print(f"最大值: {max_value}") # 5# 使用解包操作符计算最小值
min_value = min(*numbers)
print(f"最小值: {min_value}") # 1
6. 常见错误和注意事项
错误1:参数数量不匹配
def fn(a, b, c):return a + b + clst = [1, 2] # 只有2个元素
try:result = fn(*lst) # TypeError: fn() missing 1 required positional argument: 'c'
except TypeError as e:print(f"错误:{e}")
错误2:解包空列表
def fn(a, b):return a + bempty_list = []
try:result = fn(*empty_list) # TypeError: fn() missing 2 required positional arguments: 'a' and 'b'
except TypeError as e:print(f"错误:{e}")
注意事项:
# 1. 解包操作符只能用于可迭代对象
try:result = fn(*123) # TypeError: 'int' object is not iterable
except TypeError as e:print(f"错误:{e}")# 2. 解包操作符的位置很重要
def fn(a, b, c):return a + b + cnumbers = [1, 2, 3]
result = fn(*numbers) # 正确
print(result) # 6# 3. 可以与普通参数混合使用
def fn(a, b, c, d):return a + b + c + dnumbers = [1, 2]
result = fn(*numbers, 3, 4) # 解包 + 普通参数
print(result) # 10
7. 性能考虑
import time# 性能测试:解包操作符 vs 手动传递参数
def test_function(a, b, c, d, e):return a + b + c + d + e# 准备测试数据
numbers = [1, 2, 3, 4, 5]# 测试解包操作符
start = time.time()
for _ in range(1000000):result = test_function(*numbers)
unpack_time = time.time() - start# 测试手动传递参数
start = time.time()
for _ in range(1000000):result = test_function(1, 2, 3, 4, 5)
manual_time = time.time() - startprint(f"解包操作符时间: {unpack_time:.6f}秒")
print(f"手动传递时间: {manual_time:.6f}秒")
print(f"性能差异: {unpack_time/manual_time:.2f}倍")
8. 实际应用场景
场景1:数据处理
# 处理CSV数据
csv_data = [['Alice', '25', 'Beijing'],['Bob', '30', 'Shanghai'],['Charlie', '35', 'Guangzhou']
]def process_person(name, age, city):return f"{name} ({age}岁) 来自 {city}"# 使用解包操作符处理每一行
for row in csv_data:person_info = process_person(*row)print(person_info)
场景2:配置管理
# 数据库配置
db_config = {'host': 'localhost','port': 3306,'database': 'mydb','username': 'admin','password': 'password'
}def connect_database(host, port, database, username, password):return f"连接到 {database} 在 {host}:{port} 使用 {username}"# 使用字典解包
connection_string = connect_database(**db_config)
print(connection_string)
场景3:数学计算
import math# 计算点到原点的距离
def distance_to_origin(x, y, z=0):return math.sqrt(x**2 + y**2 + z**2)# 2D点
point_2d = [3, 4]
distance_2d = distance_to_origin(*point_2d)
print(f"2D距离: {distance_2d}") # 5.0# 3D点
point_3d = [3, 4, 5]
distance_3d = distance_to_origin(*point_3d)
print(f"3D距离: {distance_3d}") # 7.0710678118654755
9. 与其他解包方式的对比
# 1. 列表解包
numbers = [1, 2, 3, 4, 5]
first, *middle, last = numbers
print(f"第一个: {first}") # 1
print(f"中间: {middle}") # [2, 3, 4]
print(f"最后一个: {last}") # 5# 2. 字典解包
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
merged = {**dict1, **dict2}
print(merged) # {'a': 1, 'b': 2, 'c': 3, 'd': 4}# 3. 字符串解包
word = "Hello"
letters = [*word]
print(letters) # ['H', 'e', 'l', 'l', 'o']
10. 最佳实践
# 1. 使用解包操作符简化函数调用
def process_data(name, age, email, phone=None):return f"处理 {name} ({age}岁) 的数据,邮箱: {email}"# 从数据库获取的数据
user_data = ['Alice', 25, 'alice@example.com']# 使用解包操作符
result = process_data(*user_data)
print(result)# 2. 合并多个列表
def combine_lists(*lists):return [item for sublist in lists for item in sublist]list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]combined = combine_lists(list1, list2, list3)
print(combined) # [1, 2, 3, 4, 5, 6, 7, 8, 9]# 3. 动态函数调用
def dynamic_call(func, args):return func(*args)def add(a, b):return a + bdef multiply(a, b, c):return a * b * c# 动态调用不同函数
result1 = dynamic_call(add, [1, 2])
result2 = dynamic_call(multiply, [2, 3, 4])print(f"加法结果: {result1}") # 3
print(f"乘法结果: {result2}") # 24
11. 总结
解包操作符的优势:
- 简化代码:减少重复的参数传递
- 提高可读性:代码更简洁清晰
- 灵活性:支持动态参数传递
- 性能良好:与手动传递参数性能相近
使用场景:
- 函数参数传递
- 列表和字典合并
- 数据转换和处理
- 动态函数调用
- 配置管理
注意事项:
- 参数数量必须匹配
- 只能用于可迭代对象
- 解包操作符的位置很重要
- 可以与普通参数混合使用
python语法笔记之-字典和集合语法详解
问题代码
dicts = {}
dicts[(1, 2)] = ({3, (4, 5)})
print(dicts)
输出结果
{(1, 2): {3, (4, 5)}}
语法详解
1. 代码结构分析
dicts = {} # 创建空字典
dicts[(1, 2)] = ({3, (4, 5)}) # 添加键值对
print(dicts) # 输出字典内容
组成部分:
- 键(Key):
(1, 2)
- 一个元组 - 值(Value):
{3, (4, 5)}
- 一个集合,包含整数3和元组(4, 5)
2. 数据类型详解
键的类型 - 元组:
key = (1, 2)
print(type(key)) # <class 'tuple'>
print(key) # (1, 2)
值的类型 - 集合:
value = {3, (4, 5)}
print(type(value)) # <class 'set'>
print(value) # {3, (4, 5)}
3. 字典键的要求
可哈希性(Hashable):
# 可哈希的类型(可以作为字典键)
valid_keys = {(1, 2): "元组","string": "字符串",123: "整数",3.14: "浮点数",True: "布尔值",None: "None值"
}# 不可哈希的类型(不能作为字典键)
try:invalid_dict = {[1, 2]: "列表", # TypeError: unhashable type: 'list'{1, 2}: "集合", # TypeError: unhashable type: 'set'{1: 2}: "字典" # TypeError: unhashable type: 'dict'}
except TypeError as e:print(f"错误:{e}")
4. 集合的特点
无序性和唯一性:
# 集合的特点
set_example = {1, 2, 2, 3, 3, 3} # 重复元素会被自动去除
print(set_example) # {1, 2, 3}# 集合可以包含不同类型的元素
mixed_set = {1, "hello", (1, 2), 3.14}
print(mixed_set) # {1, 3.14, 'hello', (1, 2)}
5. 实际应用示例
示例1:坐标映射
# 使用坐标作为键,存储相关信息
coordinate_data = {}# 添加坐标信息
coordinate_data[(0, 0)] = {"type": "origin", "visited": True}
coordinate_data[(1, 1)] = {"type": "point", "visited": False}
coordinate_data[(2, 3)] = {"type": "target", "visited": False}print(coordinate_data)
# {(0, 0): {'type': 'origin', 'visited': True},
# (1, 1): {'type': 'point', 'visited': False},
# (2, 3): {'type': 'target', 'visited': False}}
示例2:学生成绩管理
# 使用学生ID和课程作为键
student_scores = {}# 添加学生成绩
student_scores[("Alice", "Math")] = {85, 90, 88}
student_scores[("Bob", "English")] = {92, 87, 91}
student_scores[("Charlie", "Science")] = {78, 85, 82}print(student_scores)
# {('Alice', 'Math'): {88, 85, 90},
# ('Bob', 'English'): {87, 91, 92},
# ('Charlie', 'Science'): {82, 85, 78}}
示例3:游戏状态管理
# 游戏中的位置和状态
game_state = {}# 添加游戏位置信息
game_state[(10, 20)] = {"player", "enemy", "item"}
game_state[(15, 25)] = {"wall", "door"}
game_state[(5, 10)] = {"treasure", "trap"}print(game_state)
# {(10, 20): {'enemy', 'item', 'player'},
# (15, 25): {'door', 'wall'},
# (5, 10): {'trap', 'treasure'}}
6. 高级用法
示例1:嵌套数据结构
# 复杂的嵌套结构
complex_dict = {}# 添加复杂数据
complex_dict[(1, 2, 3)] = {"numbers": {1, 2, 3, 4, 5},"strings": {"hello", "world"},"tuples": {(1, 2), (3, 4), (5, 6)}
}print(complex_dict)
# {(1, 2, 3): {'numbers': {1, 2, 3, 4, 5},
# 'strings': {'hello', 'world'},
# 'tuples': {(1, 2), (3, 4), (5, 6)}}}
示例2:函数参数缓存
# 使用函数参数作为缓存键
function_cache = {}def expensive_function(a, b, c):# 检查缓存key = (a, b, c)if key in function_cache:return function_cache[key]# 模拟复杂计算result = a * b + cfunction_cache[key] = resultreturn result# 测试缓存
print(expensive_function(1, 2, 3)) # 5
print(expensive_function(1, 2, 3)) # 5 (从缓存获取)
print(function_cache) # {(1, 2, 3): 5}
示例3:图数据结构
# 图的邻接表表示
graph = {}# 添加图的边
graph[(0, 1)] = {"weight": 5, "type": "road"}
graph[(1, 2)] = {"weight": 3, "type": "bridge"}
graph[(2, 0)] = {"weight": 7, "type": "tunnel"}print(graph)
# {(0, 1): {'weight': 5, 'type': 'road'},
# (1, 2): {'weight': 3, 'type': 'bridge'},
# (2, 0): {'weight': 7, 'type': 'tunnel'}}
7. 常见错误和注意事项
错误1:使用不可哈希类型作为键
try:invalid_dict = {}invalid_dict[[1, 2]] = "value" # TypeError: unhashable type: 'list'
except TypeError as e:print(f"错误:{e}")
错误2:集合中的不可哈希元素
try:invalid_set = {1, [2, 3]} # TypeError: unhashable type: 'list'
except TypeError as e:print(f"错误:{e}")
注意事项:
# 1. 元组作为键时,元组内的元素也必须是可哈希的
valid_tuple_key = (1, 2, "hello") # 正确
try:invalid_tuple_key = (1, [2, 3]) # 错误:列表不可哈希dict_with_invalid_key = {invalid_tuple_key: "value"}
except TypeError as e:print(f"错误:{e}")# 2. 集合是无序的
set1 = {1, 2, 3}
set2 = {3, 1, 2}
print(set1 == set2) # True# 3. 字典键的唯一性
test_dict = {}
test_dict[(1, 2)] = "first"
test_dict[(1, 2)] = "second" # 覆盖前一个值
print(test_dict) # {(1, 2): 'second'}
8. 性能考虑
import time# 性能测试:元组键 vs 字符串键
def test_dict_performance():# 测试元组键tuple_dict = {}start = time.time()for i in range(10000):tuple_dict[(i, i+1)] = ituple_time = time.time() - start# 测试字符串键string_dict = {}start = time.time()for i in range(10000):string_dict[f"{i}_{i+1}"] = istring_time = time.time() - startprint(f"元组键时间: {tuple_time:.6f}秒")print(f"字符串键时间: {string_time:.6f}秒")print(f"性能差异: {tuple_time/string_time:.2f}倍")test_dict_performance()
9. 实际应用场景
场景1:数据库查询结果缓存
# 缓存数据库查询结果
query_cache = {}def cached_query(table, conditions):# 创建缓存键cache_key = (table, tuple(sorted(conditions.items())))# 检查缓存if cache_key in query_cache:print("从缓存获取结果")return query_cache[cache_key]# 模拟数据库查询print("执行数据库查询")result = {"data": f"查询{table}表,条件{conditions}"}# 存储到缓存query_cache[cache_key] = resultreturn result# 测试缓存
result1 = cached_query("users", {"age": 25, "city": "Beijing"})
result2 = cached_query("users", {"age": 25, "city": "Beijing"}) # 从缓存获取
场景2:配置管理
# 多环境配置管理
config_cache = {}def get_config(environment, service, version):cache_key = (environment, service, version)if cache_key in config_cache:return config_cache[cache_key]# 模拟配置加载config = {"host": f"{environment}-{service}.example.com","port": 8080,"version": version}config_cache[cache_key] = configreturn config# 使用配置
prod_config = get_config("prod", "api", "v1.0")
dev_config = get_config("dev", "api", "v1.0")
场景3:游戏状态管理
# 游戏中的位置状态
position_states = {}def update_position_state(x, y, entities):position_key = (x, y)position_states[position_key] = set(entities)def get_position_state(x, y):position_key = (x, y)return position_states.get(position_key, set())# 更新位置状态
update_position_state(10, 20, ["player", "enemy", "item"])
update_position_state(15, 25, ["wall", "door"])# 获取位置状态
print(get_position_state(10, 20)) # {'enemy', 'item', 'player'}
print(get_position_state(15, 25)) # {'door', 'wall'}
10. 最佳实践
# 1. 使用有意义的键名
# 好的做法
user_sessions = {}
user_sessions[("user123", "session456")] = {"login_time": "2024-01-01", "status": "active"}# 2. 使用类型提示
from typing import Dict, Tuple, Set# 类型提示
coordinate_data: Dict[Tuple[int, int], Set[str]] = {}# 3. 使用默认值
def safe_get_position_data(x, y, default=None):position_key = (x, y)return coordinate_data.get(position_key, default)# 4. 使用字典推导式
# 创建坐标网格
grid = {(x, y): set() for x in range(5) for y in range(5)}
print(grid)
11. 总结
字典键值对的特点:
- 键必须是可哈希的:元组、字符串、数字等
- 值可以是任意类型:包括集合、列表、字典等
- 键的唯一性:相同键会覆盖前一个值
- 无序性:字典本身是无序的(Python 3.7+保持插入顺序)
集合的特点:
- 无序性:元素没有固定顺序
- 唯一性:自动去除重复元素
- 可哈希元素:集合中的元素必须是可哈希的
- 可变性:可以添加和删除元素
使用场景:
- 坐标映射和位置管理
- 缓存和性能优化
- 配置管理
- 图数据结构
- 游戏状态管理
注意事项:
- 确保键是可哈希的
- 注意集合中元素的类型
- 合理使用缓存避免内存泄漏
- 考虑性能影响
python语法笔记之-@property装饰器语法详解
问题代码
class Rectangle:__count = 0def __init__(self, width, height):Rectangle.__count += 1self.__width = widthself.__height = height@propertydef area(self):return self.__height * self.__widthrectangle = Rectangle(200, 100)
print(rectangle.area) # 20000
语法详解
1. @property 装饰器的作用
@property
是一个内置装饰器,用于将方法转换为属性,让您可以像访问属性一样访问方法。
基本语法:
@property
def method_name(self):return computed_value
2. 代码执行分析
class Rectangle:__count = 0 # 类变量,记录创建的矩形数量def __init__(self, width, height):Rectangle.__count += 1 # 每次创建实例时计数加1self.__width = width # 私有属性self.__height = height # 私有属性@propertydef area(self):return self.__height * self.__width # 计算面积# 创建实例
rectangle = Rectangle(200, 100)
print(rectangle.area) # 20000 - 像访问属性一样访问方法
执行过程:
- 创建
Rectangle
实例,__count
变为 1 - 设置私有属性
__width = 200
,__height = 100
- 调用
rectangle.area
时,自动执行area()
方法 - 返回计算结果
200 * 100 = 20000
3. @property 的优势
传统方法 vs @property:
# 传统方法
class Rectangle1:def __init__(self, width, height):self.width = widthself.height = heightdef get_area(self):return self.width * self.heightrect1 = Rectangle1(200, 100)
print(rect1.get_area()) # 需要调用方法# 使用 @property
class Rectangle2:def __init__(self, width, height):self.width = widthself.height = height@propertydef area(self):return self.width * self.heightrect2 = Rectangle2(200, 100)
print(rect2.area) # 像访问属性一样
4. 完整的属性装饰器
@property 的完整语法:
class Circle:def __init__(self, radius):self.__radius = radius@propertydef radius(self):"""获取半径"""return self.__radius@radius.setterdef radius(self, value):"""设置半径"""if value < 0:raise ValueError("半径不能为负数")self.__radius = value@radius.deleterdef radius(self):"""删除半径"""del self.__radius@propertydef area(self):"""计算面积"""import mathreturn math.pi * self.__radius ** 2@propertydef circumference(self):"""计算周长"""import mathreturn 2 * math.pi * self.__radius# 使用示例
circle = Circle(5)
print(circle.radius) # 5 - 获取半径
print(circle.area) # 78.54... - 计算面积
print(circle.circumference) # 31.42... - 计算周长circle.radius = 10 # 设置半径
print(circle.area) # 314.16... - 面积自动更新
5. 实际应用示例
示例1:温度转换
class Temperature:def __init__(self, celsius):self.__celsius = celsius@propertydef celsius(self):"""摄氏度"""return self.__celsius@celsius.setterdef celsius(self, value):self.__celsius = value@propertydef fahrenheit(self):"""华氏度(只读属性)"""return self.__celsius * 9/5 + 32@propertydef kelvin(self):"""开尔文(只读属性)"""return self.__celsius + 273.15# 使用示例
temp = Temperature(25)
print(f"摄氏度: {temp.celsius}°C") # 25°C
print(f"华氏度: {temp.fahrenheit}°F") # 77.0°F
print(f"开尔文: {temp.kelvin}K") # 298.15Ktemp.celsius = 30
print(f"华氏度: {temp.fahrenheit}°F") # 86.0°F(自动更新)
示例2:银行账户
class BankAccount:def __init__(self, balance=0):self.__balance = balanceself.__transactions = []@propertydef balance(self):"""账户余额(只读)"""return self.__balance@propertydef is_overdrawn(self):"""是否透支(只读)"""return self.__balance < 0@propertydef transaction_count(self):"""交易次数(只读)"""return len(self.__transactions)def deposit(self, amount):"""存款"""if amount > 0:self.__balance += amountself.__transactions.append(f"存款: +{amount}")return Truereturn Falsedef withdraw(self, amount):"""取款"""if amount > 0 and self.__balance >= amount:self.__balance -= amountself.__transactions.append(f"取款: -{amount}")return Truereturn False# 使用示例
account = BankAccount(1000)
print(f"余额: {account.balance}") # 1000
print(f"交易次数: {account.transaction_count}") # 0account.deposit(500)
print(f"余额: {account.balance}") # 1500
print(f"交易次数: {account.transaction_count}") # 1account.withdraw(2000)
print(f"余额: {account.balance}") # -500
print(f"是否透支: {account.is_overdrawn}") # True
示例3:学生成绩管理
class Student:def __init__(self, name):self.__name = nameself.__scores = {}@propertydef name(self):"""学生姓名(只读)"""return self.__name@propertydef average_score(self):"""平均分(只读)"""if not self.__scores:return 0return sum(self.__scores.values()) / len(self.__scores)@propertydef highest_score(self):"""最高分(只读)"""if not self.__scores:return 0return max(self.__scores.values())@propertydef lowest_score(self):"""最低分(只读)"""if not self.__scores:return 0return min(self.__scores.values())@propertydef grade(self):"""等级(只读)"""avg = self.average_scoreif avg >= 90:return 'A'elif avg >= 80:return 'B'elif avg >= 70:return 'C'elif avg >= 60:return 'D'else:return 'F'def add_score(self, subject, score):"""添加成绩"""if 0 <= score <= 100:self.__scores[subject] = scorereturn Truereturn False# 使用示例
student = Student("张三")
student.add_score("数学", 85)
student.add_score("英语", 92)
student.add_score("物理", 78)print(f"姓名: {student.name}")
print(f"平均分: {student.average_score:.1f}")
print(f"最高分: {student.highest_score}")
print(f"最低分: {student.lowest_score}")
print(f"等级: {student.grade}")
6. 高级用法
示例1:缓存属性
class ExpensiveCalculation:def __init__(self, data):self.__data = dataself.__cached_result = None@propertydef expensive_result(self):"""昂贵的计算结果(带缓存)"""if self.__cached_result is None:# 模拟复杂计算import timetime.sleep(1) # 模拟耗时操作self.__cached_result = sum(self.__data) * 2return self.__cached_resultdef clear_cache(self):"""清除缓存"""self.__cached_result = None# 使用示例
calc = ExpensiveCalculation([1, 2, 3, 4, 5])
print(calc.expensive_result) # 第一次计算,耗时1秒
print(calc.expensive_result) # 第二次访问,直接返回缓存结果
示例2:验证属性
class Person:def __init__(self, name, age):self.__name = nameself.__age = age@propertydef name(self):return self.__name@name.setterdef name(self, value):if not isinstance(value, str):raise TypeError("姓名必须是字符串")if len(value.strip()) == 0:raise ValueError("姓名不能为空")self.__name = value.strip()@propertydef age(self):return self.__age@age.setterdef age(self, value):if not isinstance(value, int):raise TypeError("年龄必须是整数")if value < 0 or value > 150:raise ValueError("年龄必须在0-150之间")self.__age = value@propertydef is_adult(self):"""是否成年(只读)"""return self.__age >= 18# 使用示例
person = Person("张三", 25)
print(f"姓名: {person.name}")
print(f"年龄: {person.age}")
print(f"是否成年: {person.is_adult}")# 修改属性
person.name = "李四"
person.age = 30# 验证错误
try:person.age = -5
except ValueError as e:print(f"错误: {e}")try:person.name = ""
except ValueError as e:print(f"错误: {e}")
示例3:计算属性链
class Rectangle:def __init__(self, width, height):self.__width = widthself.__height = height@propertydef width(self):return self.__width@width.setterdef width(self, value):if value <= 0:raise ValueError("宽度必须大于0")self.__width = value@propertydef height(self):return self.__height@height.setterdef height(self, value):if value <= 0:raise ValueError("高度必须大于0")self.__height = value@propertydef area(self):"""面积"""return self.__width * self.__height@propertydef perimeter(self):"""周长"""return 2 * (self.__width + self.__height)@propertydef is_square(self):"""是否为正方形"""return self.__width == self.__height@propertydef diagonal(self):"""对角线长度"""import mathreturn math.sqrt(self.__width ** 2 + self.__height ** 2)# 使用示例
rect = Rectangle(3, 4)
print(f"宽度: {rect.width}")
print(f"高度: {rect.height}")
print(f"面积: {rect.area}")
print(f"周长: {rect.perimeter}")
print(f"是否为正方形: {rect.is_square}")
print(f"对角线长度: {rect.diagonal:.2f}")# 修改尺寸,所有计算属性自动更新
rect.width = 5
print(f"新面积: {rect.area}")
print(f"新周长: {rect.perimeter}")
7. 常见错误和注意事项
错误1:忘记 @property 装饰器
class WrongExample:def __init__(self, value):self.__value = valuedef area(self): # 没有 @propertyreturn self.__value ** 2obj = WrongExample(5)
# print(obj.area) # TypeError: 'int' object is not callable
print(obj.area()) # 必须调用方法
错误2:在 setter 中忘记验证
class BadExample:def __init__(self, age):self.__age = age@propertydef age(self):return self.__age@age.setterdef age(self, value):self.__age = value # 没有验证# 可能导致无效数据
obj = BadExample(25)
obj.age = -100 # 没有验证,可能导致问题
注意事项:
# 1. @property 方法不能接受参数(除了self)
class Example:@propertydef method(self, param): # 错误:不能有参数return param# 2. @property 方法通常应该是幂等的
class GoodExample:def __init__(self, data):self.__data = data@propertydef processed_data(self):# 好的:幂等操作return sorted(self.__data)@propertydef bad_method(self):# 坏的:非幂等操作self.__data.append(0) # 修改了对象状态return self.__data
8. 性能考虑
import timeclass PerformanceTest:def __init__(self, data):self.__data = data@propertydef expensive_calculation(self):# 模拟昂贵计算time.sleep(0.1)return sum(self.__data)def regular_method(self):# 普通方法time.sleep(0.1)return sum(self.__data)# 性能测试
test = PerformanceTest([1, 2, 3, 4, 5])# 多次访问 @property
start = time.time()
for _ in range(10):result = test.expensive_calculation
property_time = time.time() - start# 多次调用普通方法
start = time.time()
for _ in range(10):result = test.regular_method()
method_time = time.time() - startprint(f"@property 时间: {property_time:.3f}秒")
print(f"普通方法时间: {method_time:.3f}秒")
9. 最佳实践
# 1. 使用 @property 进行数据验证
class ValidatedProperty:def __init__(self, value):self.__value = value@propertydef value(self):return self.__value@value.setterdef value(self, new_value):if not isinstance(new_value, (int, float)):raise TypeError("值必须是数字")self.__value = new_value# 2. 使用 @property 进行计算
class ComputedProperty:def __init__(self, x, y):self.__x = xself.__y = y@propertydef distance_from_origin(self):import mathreturn math.sqrt(self.__x ** 2 + self.__y ** 2)# 3. 使用 @property 提供只读属性
class ReadOnlyProperty:def __init__(self, data):self.__data = data@propertydef data(self):return self.__data.copy() # 返回副本,防止外部修改# 4. 使用 @property 进行类型转换
class TypeConversion:def __init__(self, value):self.__value = value@propertydef as_string(self):return str(self.__value)@propertydef as_int(self):return int(self.__value)@propertydef as_float(self):return float(self.__value)
10. 总结
@property 装饰器的特点:
- 将方法转换为属性:可以像访问属性一样访问方法
- 自动计算:每次访问时都会重新计算
- 封装性:隐藏内部实现细节
- 验证性:可以在 setter 中进行数据验证
- 只读性:可以创建只读属性
使用场景:
- 计算属性:基于其他属性计算得出的值
- 数据验证:在设置属性时进行验证
- 只读属性:防止外部修改的属性
- 类型转换:提供不同格式的数据访问
- 缓存属性:带缓存的昂贵计算
注意事项:
- @property 方法不能接受参数(除了self)
- 通常应该是幂等操作
- 避免在 @property 中修改对象状态
- 考虑性能影响,特别是昂贵计算
- 合理使用 setter 和 deleter
优势:
- 更自然的语法:像访问属性一样访问方法
- 更好的封装:隐藏内部实现
- 数据验证:确保数据有效性
- 向后兼容:可以改变实现而不影响接口
python语法笔记之-下划线命名约定语法详解
问题代码
class Example:def __init__(self):self._foo = "单下划线属性" # 单下划线前缀self.__bar = "双下划线属性" # 双下划线前缀self.__baz__ = "双下划线前后缀" # 双下划线前后缀def _private_method(self):return "单下划线方法"def __private_method(self):return "双下划线方法"def __str__(self):return "魔法方法"obj = Example()
print(obj._foo) # 可以访问
print(obj._Example__bar) # 通过改写名称访问
print(obj.__baz__) # 可以访问
print(obj._private_method()) # 可以调用
print(obj._Example__private_method()) # 通过改写名称调用
print(str(obj)) # 调用魔法方法
语法详解
1. 三种下划线命名约定的区别
单下划线前缀 _foo
:
- 含义:内部使用,不推荐外部访问
- 访问性:可以正常访问
- 名称改写:无
- 继承:子类可以访问
双下划线前缀 __foo
:
- 含义:私有成员
- 访问性:不能直接访问
- 名称改写:
_ClassName__foo
- 继承:子类不能直接访问
双下划线前后缀 __foo__
:
- 含义:魔法方法/特殊方法
- 访问性:可以正常访问
- 名称改写:无
- 继承:子类可以访问
2. 代码执行分析
class Example:def __init__(self):self._foo = "单下划线属性" # 内部属性self.__bar = "双下划线属性" # 私有属性self.__baz__ = "双下划线前后缀" # 魔法属性def _private_method(self):return "单下划线方法" # 内部方法def __private_method(self):return "双下划线方法" # 私有方法def __str__(self):return "魔法方法" # 魔法方法# 创建实例
obj = Example()# 单下划线:可以正常访问
print(obj._foo) # "单下划线属性"
print(obj._private_method()) # "单下划线方法"# 双下划线:不能直接访问
# print(obj.__bar) # AttributeError
# print(obj.__private_method()) # AttributeError# 双下划线:通过改写名称访问
print(obj._Example__bar) # "双下划线属性"
print(obj._Example__private_method()) # "双下划线方法"# 双下划线前后缀:可以正常访问
print(obj.__baz__) # "双下划线前后缀"
print(str(obj)) # "魔法方法"
3. 详细对比分析
单下划线前缀 _foo
:
class SingleUnderscore:def __init__(self):self._internal_attr = "内部属性"self.public_attr = "公有属性"def _internal_method(self):return "内部方法"def public_method(self):return "公有方法"obj = SingleUnderscore()# 可以正常访问
print(obj._internal_attr) # "内部属性"
print(obj._internal_method()) # "内部方法"# 查看对象属性
print(dir(obj)) # 包含 _internal_attr 和 _internal_method
双下划线前缀 __foo
:
class DoubleUnderscore:def __init__(self):self.__private_attr = "私有属性"self.public_attr = "公有属性"def __private_method(self):return "私有方法"def public_method(self):return "公有方法"obj = DoubleUnderscore()# 不能直接访问
try:print(obj.__private_attr)
except AttributeError as e:print(f"错误: {e}") # 'DoubleUnderscore' object has no attribute '__private_attr'try:print(obj.__private_method())
except AttributeError as e:print(f"错误: {e}") # 'DoubleUnderscore' object has no attribute '__private_method'# 通过改写名称访问
print(obj._DoubleUnderscore__private_attr) # "私有属性"
print(obj._DoubleUnderscore__private_method()) # "私有方法"# 查看对象属性
print(dir(obj)) # 包含 _DoubleUnderscore__private_attr 和 _DoubleUnderscore__private_method
双下划线前后缀 __foo__
:
class MagicUnderscore:def __init__(self):self.__magic_attr__ = "魔法属性"self.public_attr = "公有属性"def __str__(self):return "魔法方法"def __len__(self):return 42def public_method(self):return "公有方法"obj = MagicUnderscore()# 可以正常访问
print(obj.__magic_attr__) # "魔法属性"
print(str(obj)) # "魔法方法"
print(len(obj)) # 42# 查看对象属性
print(dir(obj)) # 包含 __magic_attr__, __str__, __len__ 等
4. 继承中的行为差异
单下划线在继承中:
class Parent:def __init__(self):self._internal_attr = "父类内部属性"self.public_attr = "父类公有属性"def _internal_method(self):return "父类内部方法"class Child(Parent):def __init__(self):super().__init__()self._child_attr = "子类内部属性"def access_parent_internal(self):# 子类可以访问父类的单下划线成员print(self._internal_attr) # "父类内部属性"print(self._internal_method()) # "父类内部方法"print(self._child_attr) # "子类内部属性"child = Child()
child.access_parent_internal()# 外部也可以访问
print(child._internal_attr) # "父类内部属性"
print(child._child_attr) # "子类内部属性"
双下划线在继承中:
class Parent:def __init__(self):self.__private_attr = "父类私有属性"self.public_attr = "父类公有属性"def __private_method(self):return "父类私有方法"class Child(Parent):def __init__(self):super().__init__()self.__private_attr = "子类私有属性" # 不同的名称改写def access_parent_private(self):# 子类不能直接访问父类的双下划线成员try:print(self.__private_attr)except AttributeError as e:print(f"错误: {e}")# 但可以通过改写名称访问print(self._Parent__private_attr) # "父类私有属性"print(self._Child__private_attr) # "子类私有属性"print(self._Parent__private_method()) # "父类私有方法"child = Child()
child.access_parent_private()# 外部访问
print(child._Parent__private_attr) # "父类私有属性"
print(child._Child__private_attr) # "子类私有属性"
双下划线前后缀在继承中:
class Parent:def __init__(self):self.__magic_attr__ = "父类魔法属性"def __str__(self):return "父类魔法方法"class Child(Parent):def __init__(self):super().__init__()self.__magic_attr__ = "子类魔法属性" # 覆盖父类的魔法属性def __str__(self):return "子类魔法方法" # 覆盖父类的魔法方法def access_magic(self):# 子类可以访问父类的魔法成员print(self.__magic_attr__) # "子类魔法属性"(被覆盖)print(str(self)) # "子类魔法方法"(被覆盖)child = Child()
child.access_magic()# 外部也可以访问
print(child.__magic_attr__) # "子类魔法属性"
print(str(child)) # "子类魔法方法"
5. 实际应用示例
示例1:数据封装
class BankAccount:def __init__(self, account_number, balance=0):self._account_number = account_number # 内部属性self.__balance = balance # 私有属性self.__transactions = [] # 私有属性def _validate_amount(self, amount):"""验证金额(内部方法)"""return isinstance(amount, (int, float)) and amount > 0def __add_transaction(self, transaction):"""添加交易记录(私有方法)"""self.__transactions.append(transaction)def deposit(self, amount):"""存款(公有方法)"""if self._validate_amount(amount):self.__balance += amountself.__add_transaction(f"存款: +{amount}")return Truereturn Falsedef withdraw(self, amount):"""取款(公有方法)"""if self._validate_amount(amount) and self.__balance >= amount:self.__balance -= amountself.__add_transaction(f"取款: -{amount}")return Truereturn Falsedef get_balance(self):"""获取余额(公有方法)"""return self.__balancedef get_transactions(self):"""获取交易记录(公有方法)"""return self.__transactions.copy()# 使用示例
account = BankAccount("12345", 1000)# 可以访问内部属性
print(account._account_number) # "12345"# 不能直接访问私有属性
try:print(account.__balance)
except AttributeError:print("不能直接访问私有属性")# 通过公有方法访问
print(account.get_balance()) # 1000account.deposit(500)
account.withdraw(200)
print(account.get_transactions()) # ['存款: +500', '取款: -200']
示例2:配置管理
class Config:def __init__(self):self._config = {} # 内部配置字典self.__defaults = { # 私有默认值'host': 'localhost','port': 8080,'debug': False}self.__load_defaults() # 私有方法def __load_defaults(self):"""加载默认配置(私有方法)"""self._config.update(self.__defaults)def _validate_key(self, key):"""验证配置键(内部方法)"""return isinstance(key, str) and key.strip()def set(self, key, value):"""设置配置(公有方法)"""if self._validate_key(key):self._config[key] = valuereturn Truereturn Falsedef get(self, key, default=None):"""获取配置(公有方法)"""return self._config.get(key, default)def get_all(self):"""获取所有配置(公有方法)"""return self._config.copy()# 使用示例
config = Config()# 可以访问内部属性
print(config._config) # {'host': 'localhost', 'port': 8080, 'debug': False}# 不能直接访问私有属性
try:print(config.__defaults)
except AttributeError:print("不能直接访问私有属性")# 通过公有方法访问
config.set('host', 'example.com')
print(config.get('host')) # 'example.com'
示例3:魔法方法使用
class Vector:def __init__(self, x, y):self.__x = x # 私有属性self.__y = y # 私有属性def __str__(self):"""字符串表示(魔法方法)"""return f"Vector({self.__x}, {self.__y})"def __repr__(self):"""详细字符串表示(魔法方法)"""return f"Vector(x={self.__x}, y={self.__y})"def __add__(self, other):"""加法运算(魔法方法)"""if isinstance(other, Vector):return Vector(self.__x + other.__x, self.__y + other.__y)return NotImplementeddef __eq__(self, other):"""相等比较(魔法方法)"""if isinstance(other, Vector):return self.__x == other.__x and self.__y == other.__yreturn Falsedef __len__(self):"""长度(魔法方法)"""return 2def get_x(self):"""获取x坐标(公有方法)"""return self.__xdef get_y(self):"""获取y坐标(公有方法)"""return self.__y# 使用示例
v1 = Vector(3, 4)
v2 = Vector(1, 2)# 魔法方法的使用
print(str(v1)) # "Vector(3, 4)"
print(repr(v1)) # "Vector(x=3, y=4)"
print(v1 + v2) # Vector(4, 6)
print(v1 == v2) # False
print(len(v1)) # 2# 不能直接访问私有属性
try:print(v1.__x)
except AttributeError:print("不能直接访问私有属性")# 通过公有方法访问
print(v1.get_x()) # 3
print(v1.get_y()) # 4
6. 常见错误和注意事项
错误1:误解双下划线的私有性
class Example:def __init__(self):self.__private_attr = "私有属性"obj = Example()# 错误:认为完全无法访问
# print(obj.__private_attr) # AttributeError# 正确:通过改写名称可以访问
print(obj._Example__private_attr) # "私有属性"
错误2:在外部代码中过度使用内部成员
class Library:def __init__(self):self._books = []self.__max_books = 100def _is_full(self):return len(self._books) >= self.__max_booksdef add_book(self, book):if not self._is_full():self._books.append(book)return Truereturn False# 错误用法:直接访问内部成员
library = Library()
# library._books.append("新书") # 不推荐:绕过了验证逻辑
# print(library._is_full()) # 不推荐:直接调用内部方法# 正确用法:使用公有接口
library.add_book("新书") # 推荐:通过公有方法
注意事项:
# 1. 单下划线不是真正的私有
class Example:def __init__(self):self._data = "内部数据"obj = Example()
print(obj._data) # 可以访问# 2. 双下划线会进行名称改写
class Example:def __init__(self):self.__data = "私有数据"obj = Example()
# print(obj.__data) # AttributeError
print(obj._Example__data) # 可以通过改写后的名称访问# 3. 继承中的名称改写
class Parent:def __init__(self):self.__data = "父类私有数据"class Child(Parent):def __init__(self):super().__init__()self.__data = "子类私有数据" # 不同的名称改写child = Child()
print(child._Parent__data) # "父类私有数据"
print(child._Child__data) # "子类私有数据"# 4. 魔法方法可以正常访问
class Example:def __init__(self):self.__magic__ = "魔法属性"def __str__(self):return "魔法方法"obj = Example()
print(obj.__magic__) # "魔法属性"
print(str(obj)) # "魔法方法"
7. 最佳实践
# 1. 使用单下划线表示内部成员
class GoodExample:def __init__(self):self._internal_data = [] # 内部数据self.public_data = [] # 公有数据def _internal_method(self):return "内部方法" # 内部方法def public_method(self):return "公有方法" # 公有方法# 2. 使用双下划线表示真正的私有成员
class SecureExample:def __init__(self):self.__private_data = [] # 私有数据self.public_data = [] # 公有数据def __private_method(self):return "私有方法" # 私有方法def public_method(self):return "公有方法" # 公有方法# 3. 使用双下划线前后缀定义魔法方法
class MagicExample:def __init__(self, data):self.__data = datadef __str__(self):return f"MagicExample({self.__data})"def __len__(self):return len(self.__data)def __getitem__(self, index):return self.__data[index]# 4. 提供公有接口访问私有成员
class DataManager:def __init__(self):self.__data = []self.__max_size = 100def add_item(self, item):"""添加项目(公有接口)"""if len(self.__data) < self.__max_size:self.__data.append(item)return Truereturn Falsedef get_items(self):"""获取所有项目(公有接口)"""return self.__data.copy() # 返回副本def get_count(self):"""获取项目数量(公有接口)"""return len(self.__data)# 5. 文档化内部成员
class Calculator:def __init__(self):self.__history = [] # 计算历史记录(私有)def __add_to_history(self, operation, result):"""添加操作到历史记录(私有方法)"""self.__history.append((operation, result))def add(self, a, b):"""加法运算(公有方法)"""result = a + bself.__add_to_history(f"{a} + {b}", result)return resultdef get_history(self):"""获取计算历史(公有方法)"""return self.__history.copy()
8. 总结
三种下划线命名约定的对比:
类型 | 语法 | 含义 | 访问性 | 名称改写 | 继承 |
---|---|---|---|---|---|
单下划线前缀 | _foo | 内部使用 | 可以访问 | 无 | 子类可访问 |
双下划线前缀 | __foo | 私有成员 | 不能直接访问 | _ClassName__foo | 子类不能直接访问 |
双下划线前后缀 | __foo__ | 魔法方法 | 可以访问 | 无 | 子类可访问 |
使用建议:
- 单下划线前缀
_foo
:用于内部成员,表示"不推荐外部访问" - 双下划线前缀
__foo
:用于真正的私有成员,防止意外访问 - 双下划线前后缀
__foo__
:用于魔法方法和特殊属性
注意事项:
- Python中没有真正的私有成员,所有成员都可以通过某种方式访问
- 双下划线前缀通过名称改写实现"私有",但改写后的名称仍然可以访问
- 单下划线前缀只是一种命名约定,不提供任何访问限制
- 魔法方法通常由Python解释器自动调用,不应该手动调用
最佳实践:
- 使用单下划线表示内部成员
- 使用双下划线表示真正的私有成员
- 提供公有接口访问私有成员
- 文档化内部成员的用途和限制
- 避免在外部代码中直接访问内部成员
python语法笔记之-format()方法语法详解
基本语法
str.format(*args, **kwargs)
语法详解
1. 基本用法
# 位置参数
"{} {}".format("Hello", "World") # "Hello World"# 索引参数
"{0} {1}".format("Hello", "World") # "Hello World"
"{1} {0}".format("Hello", "World") # "World Hello"# 关键字参数
"{name} {age}".format(name="张三", age=25) # "张三 25"
2. 格式说明符
# 数字格式
"{:.2f}".format(3.14159) # "3.14"
"{:d}".format(42) # "42"
"{:x}".format(255) # "ff"# 对齐和宽度
"{:>10}".format("Hello") # " Hello"
"{:<10}".format("Hello") # "Hello "
"{:^10}".format("Hello") # " Hello "# 填充字符
"{:*>10}".format("Hello") # "*****Hello"
"{:0<10}".format("Hello") # "Hello00000"
3. 常用格式
# 字符串
"{:s}".format("text") # "text"# 整数
"{:d}".format(42) # "42"
"{:b}".format(42) # "101010"
"{:o}".format(42) # "52"
"{:x}".format(42) # "2a"# 浮点数
"{:.2f}".format(3.14159) # "3.14"
"{:.2e}".format(1234.56) # "1.23e+03"
"{:.2%}".format(0.1234) # "12.34%"
4. 复合格式
# 组合使用
"{:>10.2f}".format(3.14159) # " 3.14"
"{:*<10s}".format("Hello") # "Hello*****"
"{:0>8x}".format(255) # "000000ff"
5. 字典和对象
# 字典
person = {"name": "张三", "age": 25}
"{name} {age}".format(**person) # "张三 25"# 对象属性
class Person:def __init__(self, name, age):self.name = nameself.age = agep = Person("李四", 30)
"{0.name} {0.age}".format(p) # "李四 30"
6. 总结
format() 方法特点:
- 支持位置参数和关键字参数
- 提供丰富的格式说明符
- 支持数字、字符串、对齐等格式化
- 比
%
操作符更灵活
常用格式说明符:
s
- 字符串d
- 十进制整数f
- 浮点数x
- 十六进制b
- 二进制o
- 八进制>
- 右对齐<
- 左对齐^
- 居中对齐
python语法笔记之-浅拷贝与深拷贝语法详解
问题代码
import copy
a = [1, 2, 3, 4, ['a', 'b']]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
a.append(5)
a[4].append('c')
语法详解
1. 三种复制方式的区别
直接赋值 b = a
:
- 创建引用,指向同一个对象
- 修改任一变量都会影响另一个
浅拷贝 copy.copy(a)
:
- 创建新对象,但嵌套对象仍共享引用
- 修改顶层元素不影响原对象,修改嵌套对象会影响原对象
深拷贝 copy.deepcopy(a)
:
- 创建完全独立的新对象
- 修改任何元素都不会影响原对象
2. 代码执行分析
import copy# 原始列表
a = [1, 2, 3, 4, ['a', 'b']]
print(f"原始列表 a: {a}") # [1, 2, 3, 4, ['a', 'b']]# 直接赋值
b = a
print(f"直接赋值 b: {b}") # [1, 2, 3, 4, ['a', 'b']]# 浅拷贝
c = copy.copy(a)
print(f"浅拷贝 c: {c}") # [1, 2, 3, 4, ['a', 'b']]# 深拷贝
d = copy.deepcopy(a)
print(f"深拷贝 d: {d}") # [1, 2, 3, 4, ['a', 'b']]# 修改原始列表
a.append(5) # 添加顶层元素
a[4].append('c') # 修改嵌套列表print(f"修改后 a: {a}") # [1, 2, 3, 4, ['a', 'b', 'c'], 5]
print(f"修改后 b: {b}") # [1, 2, 3, 4, ['a', 'b', 'c'], 5] (受影响)
print(f"修改后 c: {c}") # [1, 2, 3, 4, ['a', 'b', 'c']] (嵌套对象受影响)
print(f"修改后 d: {d}") # [1, 2, 3, 4, ['a', 'b']] (完全独立)
3. 内存结构分析
import copya = [1, 2, 3, 4, ['a', 'b']]# 直接赋值 - 同一对象
b = a
print(f"a is b: {a is b}") # True# 浅拷贝 - 新对象,但嵌套对象共享
c = copy.copy(a)
print(f"a is c: {a is c}") # False
print(f"a[4] is c[4]: {a[4] is c[4]}") # True# 深拷贝 - 完全独立
d = copy.deepcopy(a)
print(f"a is d: {a is d}") # False
print(f"a[4] is d[4]: {a[4] is d[4]}") # False
4. 不同数据类型的拷贝行为
import copy# 列表
list_a = [1, 2, [3, 4]]
list_b = copy.copy(list_a)
list_c = copy.deepcopy(list_a)# 字典
dict_a = {'a': 1, 'b': [2, 3]}
dict_b = copy.copy(dict_a)
dict_c = copy.deepcopy(dict_a)# 元组(不可变,但可能包含可变元素)
tuple_a = (1, 2, [3, 4])
tuple_b = copy.copy(tuple_a)
tuple_c = copy.deepcopy(tuple_a)# 集合
set_a = {1, 2, 3}
set_b = copy.copy(set_a)
set_c = copy.deepcopy(set_a)
5. 实际应用场景
浅拷贝适用场景:
# 配置管理
config = {'host': 'localhost','port': 8080,'options': {'timeout': 30}
}# 创建配置副本
dev_config = copy.copy(config)
dev_config['host'] = 'dev.example.com'
dev_config['options']['timeout'] = 60# 原配置不受影响(顶层),但嵌套对象受影响
print(config['host']) # 'localhost'
print(config['options']['timeout']) # 60 (受影响)
深拷贝适用场景:
# 游戏状态
game_state = {'player': {'health': 100, 'inventory': ['sword', 'shield']},'enemies': [{'type': 'goblin', 'health': 50}]
}# 保存游戏状态
saved_state = copy.deepcopy(game_state)# 修改当前状态
game_state['player']['health'] = 80
game_state['enemies'][0]['health'] = 30# 保存的状态完全独立
print(saved_state['player']['health']) # 100
print(saved_state['enemies'][0]['health']) # 50
6. 性能考虑
import copy
import time# 创建大型嵌套结构
def create_large_structure(depth, width):if depth == 0:return [i for i in range(width)]return [create_large_structure(depth - 1, width) for _ in range(width)]large_data = create_large_structure(3, 10)# 测试浅拷贝性能
start = time.time()
shallow_copy = copy.copy(large_data)
shallow_time = time.time() - start# 测试深拷贝性能
start = time.time()
deep_copy = copy.deepcopy(large_data)
deep_time = time.time() - startprint(f"浅拷贝时间: {shallow_time:.6f}秒")
print(f"深拷贝时间: {deep_time:.6f}秒")
print(f"深拷贝是浅拷贝的 {deep_time/shallow_time:.1f} 倍")
7. 常见错误和注意事项
错误1:误解浅拷贝
import copyoriginal = [1, 2, [3, 4]]
shallow = copy.copy(original)# 错误:认为浅拷贝完全独立
shallow[2].append(5)
print(original[2]) # [3, 4, 5] (受影响)
错误2:循环引用
import copy# 创建循环引用
a = [1, 2]
a.append(a)# 深拷贝可以处理循环引用
b = copy.deepcopy(a)
print(b) # [1, 2, [...]]
注意事项:
# 1. 不可变对象的拷贝
import copy# 字符串、数字、元组(不包含可变元素)的拷贝
string_a = "hello"
string_b = copy.copy(string_a)
string_c = copy.deepcopy(string_a)
print(string_a is string_b) # True
print(string_a is string_c) # True# 2. 自定义对象的拷贝
class Person:def __init__(self, name, friends=None):self.name = nameself.friends = friends or []person1 = Person("Alice")
person1.friends.append("Bob")# 浅拷贝
person2 = copy.copy(person1)
person2.name = "Charlie"
person2.friends.append("David")print(person1.name) # "Alice"
print(person1.friends) # ["Bob", "David"] (受影响)# 深拷贝
person3 = copy.deepcopy(person1)
person3.friends.append("Eve")print(person1.friends) # ["Bob", "David"]
print(person3.friends) # ["Bob", "David", "Eve"]
8. 最佳实践
import copy# 1. 选择合适的拷贝方式
def process_data(data, need_deep_copy=False):if need_deep_copy:return copy.deepcopy(data)else:return copy.copy(data)# 2. 使用列表推导式进行浅拷贝
original = [1, 2, [3, 4]]
shallow = [item for item in original] # 等价于 copy.copy()# 3. 使用切片进行浅拷贝
shallow_slice = original[:] # 等价于 copy.copy()# 4. 使用dict()和list()进行浅拷贝
dict_copy = dict(original_dict)
list_copy = list(original_list)# 5. 自定义深拷贝
def custom_deep_copy(obj):if isinstance(obj, dict):return {key: custom_deep_copy(value) for key, value in obj.items()}elif isinstance(obj, list):return [custom_deep_copy(item) for item in obj]elif isinstance(obj, (int, float, str, bool, type(None))):return objelse:return copy.deepcopy(obj)
9. 总结
三种复制方式的对比:
方式 | 语法 | 对象独立性 | 嵌套对象独立性 | 性能 | 适用场景 |
---|---|---|---|---|---|
直接赋值 | b = a | 否 | 否 | 最快 | 需要共享对象 |
浅拷贝 | copy.copy(a) | 是 | 否 | 快 | 简单数据结构 |
深拷贝 | copy.deepcopy(a) | 是 | 是 | 慢 | 复杂嵌套结构 |
使用建议:
- 直接赋值:需要共享对象时使用
- 浅拷贝:简单数据结构,不包含嵌套可变对象时使用
- 深拷贝:复杂嵌套结构,需要完全独立时使用
注意事项:
- 浅拷贝只复制顶层对象,嵌套对象仍共享引用
- 深拷贝可以处理循环引用
- 不可变对象的拷贝通常返回原对象
- 深拷贝性能较慢,特别是大型嵌套结构
python语法笔记之-闭包语法详解
问题代码
def adder(x):def wrapper(y):return x + yreturn wrapper
adder5 = adder(5)
print(adder5(adder5(6))) # 16
语法详解
1. 闭包基本语法
def outer_function(x): # 外部函数def inner_function(y): # 内部函数return x + y # 访问外部变量xreturn inner_function # 返回内部函数
2. 代码执行分析
def adder(x):def wrapper(y):return x + yreturn wrapper# 创建闭包
adder5 = adder(5) # x=5,返回wrapper函数
result = adder5(6) # y=6,返回5+6=11
final = adder5(adder5(6)) # adder5(6)=11,然后adder5(11)=16
print(final) # 16
3. 闭包特点
- 内部函数:定义在外部函数内部
- 访问外部变量:内部函数可以访问外部函数的变量
- 返回函数:外部函数返回内部函数
- 保持状态:闭包会"记住"外部变量的值
4. 常见用法
# 计数器
def counter():count = 0def increment():nonlocal countcount += 1return countreturn increment# 配置函数
def multiply_by(factor):def multiply(x):return x * factorreturn multiply# 装饰器基础
def decorator(func):def wrapper(*args, **kwargs):print("开始执行")result = func(*args, **kwargs)print("执行结束")return resultreturn wrapper
5. 总结
闭包语法要点:
- 外部函数定义内部函数
- 内部函数访问外部变量
- 外部函数返回内部函数
- 闭包保持外部变量的状态
使用场景:
- 函数工厂
- 装饰器
- 状态保持
- 配置函数