1、两大编程思想
编程思想是指导我们如何组织和构建代码的方法论。最主要的两种思想是面向过程编程 (Procedural Programming) 和 面向对象编程 (Object-Oriented Programming, OOP)。
- 面向过程编程 (Procedural Programming):○ 以“过程”(即函数或子程序)为中心,强调程序的执行流程。它将问题分解为一系列的步骤(函数),然后按照顺序执行这些步骤来解决问题。数据和处理数据的函数通常是分离的。
- 特点:
- 自顶向下、逐步求精:从整体问题开始,逐步细化为更小的子问题,每个子问题对应一个函数。
- 函数(过程)是核心:程序由一系列函数组成,通过函数的调用来完成任务。
- 数据与操作分离:数据通常作为参数传递给函数,或者作为全局变量被多个函数访问。
- 优点:结构简单,逻辑清晰(对于小型、线性任务),易于理解和实现。对于特定问题,性能可能更高。
- 适用场景:简单、独立的任务,如脚本、算法实现(例如,计算斐波那契数列、排序算法)。
- 特点:
- 面向对象编程 (Object-Oriented Programming, OOP):以“对象”为中心,将数据(属性)和操作数据的方法(行为)封装在一起,形成一个独立的实体——对象。它模拟现实世界中的实体,通过对象之间的交互来解决问题。
- 4大基本特征:
- 封装 (Encapsulation): 将数据(属性)和操作数据的方法(行为)捆绑在一起,形成一个独立的单元(类或对象)。同时,隐藏对象的内部实现细节,只对外提供公共接口。这保护了数据的完整性,并降低了模块间的耦合。
- 继承 (Inheritance): 允许一个类(子类/派生类)继承另一个类(父类/基类)的属性和方法。子类可以复用父类的代码,并在此基础上添加新的功能或修改现有功能,实现了代码的重用。
- 多态 (Polymorphism): 允许不同类的对象对同一消息(方法调用)作出不同的响应。这意味着可以使用统一的接口来处理不同类型的对象,增加了代码的灵活性和可扩展性。在Python中,多态主要通过“鸭子类型”(Duck Typing)体现:如果一个对象走起来像鸭子,叫起来像鸭子,那么它就是一只鸭子(即只要对象拥有所需的方法,就可以被当作特定类型来处理,而无需显式声明继承关系)。
- 抽象 (Abstraction): 关注对象“做什么”(行为)而不是“如何做”(实现细节)。它通过定义接口或抽象类来隐藏复杂的实现,只暴露必要的特征。
- 优点:
- 模块化和高内聚低耦合: 对象是独立的单元,内部数据和方法紧密相关(高内聚),对象之间通过接口交互(低耦合)。
- 代码重用性高: 通过继承和多态,可以大量复用现有代码。
- 可维护性好: 当需求变化时,通常只需修改少量相关对象,影响范围小。
- 扩展性强: 易于添加新功能和新对象,不影响现有代码。
- 更接近现实世界: 建模能力强,更符合人类的思维方式。
- 适用场景:复杂系统、大型项目、图形用户界面(GUI)、游戏开发、Web开发等需要高度模块化、可扩展和可维护的场景。
- 4大基本特征:
● 面向过程的原理: 程序的执行流是线性的或分支循环的,数据在内存中独立存在,函数通过栈帧(Stack Frame)来管理局部变量和参数,并通过函数指针(或直接地址)跳转到相应的代码段执行。核心是指令序列和内存操作。
● 面向对象的原理: 在运行时,类是对象的蓝图,对象是类的实例。每个对象在内存中都有一块独立的区域,存储其属性值。对象的方法调用实际上是通过对象的类型(类)找到对应的方法代码,并将对象自身的引用(self)作为第一个参数隐式传递给方法,从而让方法能够操作该对象的属性。继承通过查找链(Method Resolution Order, MRO)实现,当查找一个属性或方法时,Python会沿着MRO从当前类向上搜索父类,直到找到为止。多态则依赖于动态绑定(Dynamic Binding),即在运行时根据对象的实际类型来决定调用哪个方法。
(1)面向过程编程示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 面向过程编程示例
balance = 1000
def deposit(amount):
"""存款操作"""
global balance
balance += amount
print(f"成功存入金额:{amount}元,现在的余额是:{balance}元")
def withdraw(amount):
"""取款操作"""
global balance
if amount > balance:
print(f"取款失败,余额不足!余额为:{balance}元,无法取款{amount}元")
else:
balance -= amount
print(f"{amount}元取款成功!余额:{balance}元")
# 按照流程调用函数
deposit(200)
withdraw(500)
withdraw(800)
print(f"最终余额是:{balance}元。")
(2)面向对象编程示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class BankAccount:
def __init__(self, initialBalance = 0):
'''
构造函数,用于初始化账户
self表示当前正在创建的对象实例
'''
self.balance = initialBalance
print(f"账户已经创建,初始余额是:{self.balance}元。")
def deposit(self, amount):
'''
存款方法
'''
if amount > 0:
self.balance += amount
print(f"存入{amount}元,当前余额:{self.balance}元。")
else:
print(f"存款金额必须大于零。")
def withdraw(self, amount):
'''
取款方法
'''
if amount < 0:
print(f"取款金额必须大于零。")
else:
if amount > self.balance:
print(f"取款金额{amount}大于余额{self.balance},取款失败。")
else:
self.balance -= amount
print(f"取款{amount}成功,现余额:{self.balance}")
def get_balance(self):
'''
获取当前余额
'''
return self.balance
# 创建对象
account1 = BankAccount(100)
account2 = BankAccount(2000)
# 通过对象调用方法
account1.deposit(300)
account2.withdraw(3000)
account2.withdraw(500)
ac2_balance = account2.get_balance()
print(f"account2余额:{ac2_balance}")
ac1_balance = account1.get_balance()
print(f"account1余额:{ac1_balance}")
2、类和对象的创建
类是创建对象的蓝图或模板,定义了对象的属性(数据)和方法(行为)。对象是类的具体实例。通过 class 关键字定义类,通过调用类名后加括号 ()
来创建对象。
(1)类(Class)
类是抽象的,它不占用实际内存,只是一个规范或类型。它定义了所有该类型对象共有的属性和行为。
○ 语法: 使用 class 关键字后跟类名(通常使用驼峰命名法,如 MyClass),然后是冒号 :
。类体通常包含属性定义和方法定义。
○ 属性: 类中定义的变量,描述了对象的特征。
○ 方法: 类中定义的函数,描述了对象的行为。
○ self 参数: 在类的方法定义中,第一个参数约定俗成地命名为 self。它是一个指向当前对象实例的引用。当通过对象调用方法时,Python会自动将该对象实例作为第一个参数(即 self)传递给方法。
○ 当Python解释器执行 class 语句时,它会创建一个类对象 (Class Object)。这个类对象本身也是一个Python对象(类型为 type)。
○ 类对象包含了类的所有元数据,包括其名称、基类、方法字典(dict,存储了类属性和方法),以及如何创建实例的指令。
○ 方法的定义(如
def my_method(self):
)在类对象中被存储为普通的函数对象。
(2)对象 (Object / Instance)
对象是类的具体化,是类的实例化结果。每个对象都拥有类定义的属性和方法,但其属性值可以是独立的。
○ 创建: 通过调用类名后加括号 ()
来创建对象,这会触发类的构造函数 init。
○ __init__
方法(构造函数):
■ 这是一个特殊的方法,当创建类的新实例时,它会自动被调用。
■ 它用于初始化新创建对象的属性。
■ self 是 init 方法的第一个参数,它指向正在被初始化的那个新对象。
■ init 方法不返回任何值(隐式返回 None)。
○ 当你调用
MyClass()
时,Python会执行以下步骤:
调用 __new__ 方法: 首先,Python会调用类的 new 方法(如果存在,否则调用其基类的 new)。new 是一个静态方法,负责创建并返回一个新的、空的实例对象。它通常返回一个未初始化的对象实例。
调用 __init__ 方法: 接下来,Python会将新创建的实例对象作为 self 参数,以及你在 MyClass() 调用时传入的其他参数,传递给 init 方法。init 的主要职责是初始化这个新对象的属性。
返回实例: init 方法执行完毕后,新创建并初始化好的对象实例就会被返回,并赋值给你的变量(例如
obj = MyClass()
中的 obj)。
(3)self
● self 并不是Python的关键字,它只是一个约定俗成的参数名。当通过一个对象调用方法时(例如 my_object.my_method()
),Python解释器会自动将 my_object
这个实例作为第一个参数传递给 my_method
。在方法内部,这个参数就被命名为 self,从而允许方法访问和操作该实例的属性 (self.attribute
)。如果没有 self,方法就不知道它应该操作哪个实例的数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Dog:
def __init__(self, name, breed):
'''
初始化狗对象
name: 狗的名字
breed: 狗的品种
'''
self.name = name
self.breed = breed
print(f"一只名为:{self.name}的{self.breed}狗被创建了")
def bark(self):
'''
狗叫方法
'''
print(f"{self.name}汪汪叫")
def describe(self):
'''
描述这只狗
'''
print(f"我叫{self.name}, 我是一只{self.breed}狗")
# 创建对象
dog1 = Dog("Buddy", "Golden Retriever")
dog2 = Dog("Lucy", "Labrador")
# 访问对象的属性
print(f"dog1的名字:{dog1.name}")
print(f"dog2的品种:{dog2.breed}")
# 调用对象的方法
dog1.bark()
dog2.describe()
# 每个对象都有自己独立的属性副本
dog1.name = "Max"
print(f"修改后dog1的名字是:{dog1.name}")
print(f"dog2的名字依然是:{dog2.name}")
# 检查对象类型
print(f"dog1的类型是:{type(dog1)}")
print(f"dog1是Dog的实例吗?{isinstance(dog1, Dog)}")
3、类对象与类属性
类对象是Python在定义类时创建的特殊对象。类属性是直接在类定义内部,但在任何方法之外定义的变量,它们被该类的所有实例共享。
(1)类对象
○ 当在Python中定义一个类时,例如 class MyClass: ...
,Python解释器在内存中实际上创建了一个名为 MyClass 的类对象。这个类对象本身是一个 type 类型的实例。
○ 这个类对象是所有该类实例的“工厂”和“元数据存储库”。它包含了类的方法、类属性以及其他所有与类本身相关的信息。
○ 可以直接通过类名来访问类属性和类方法。
(2)类属性
○ 类属性是直接在类体中定义,但在任何方法之外的变量。
○ 它们属于类本身,而不是类的某个特定实例。这意味着所有该类的实例都共享同一个类属性的副本。修改类属性会影响所有实例。
○ 访问:
-
可以通过
ClassName.attribute_name
直接访问。 -
也可以通过
instance_name.attribute_name
访问。当通过实例访问时,Python会首先在实例的 dict 中查找,如果找不到,就会沿着MRO(方法解析顺序)向上到类的 dict 中查找。
○ 用途: 存储与类相关但不需要每个实例都独立拥有的数据,例如:
-
常量(如 PI = 3.14)
-
所有实例共享的配置信息
-
记录实例数量的计数器
(3)原理
● 类属性的存储: 类属性存储在类对象自身的 __dict__
字典中。每个类对象都有一个 __dict__
,用来存储其类属性和方法。
● 实例属性的存储: 实例属性存储在实例对象自身的 __dict__
字典中。每个实例对象也有一个 __dict__
,用来存储该实例特有的属性。
● 属性查找机制 (Method Resolution Order, MRO):
○ 当你通过 instance_name.attribute_name
访问一个属性时,Python的查找顺序是:
-
首先在
instance_name.__dict__
中查找attribute_name
。 -
如果找到了,就使用实例属性的值。
-
如果没找到,Python会沿着该实例所属类的MRO(方法解析顺序)向上查找,即在
ClassName.__dict__
中查找attribute_name
。 -
如果找到了,就使用类属性的值。
-
如果直到最顶层的基类都未找到,则抛出 AttributeError。
● 修改类属性的陷阱:
○ 当你通过 ClassName.class_attribute = new_value
修改类属性时,会直接修改类对象 __dict__
中的值,从而影响所有实例。
○ 当你通过 instance_name.class_attribute = new_value
修改一个同名的类属性时,Python的查找机制会导致一个实例属性被创建,并存储在 instance_name.__dict__
中。这个新的实例属性会“遮蔽”(shadow)同名的类属性,使得该实例后续访问这个属性时,会优先访问到它自己的实例属性,而其他实例仍然访问类属性。这不会修改类属性本身。
代码学习参考:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Car:
"""
一个表示汽车的类,演示类属性和实例属性。
"""
# 类属性:所有Car实例共享的属性
WHEELS = 4 # 汽车通常有4个轮子,这是一个常量
count = 0 # 记录一共有多少个car实例
def __init__(self, make, model):
self.make = make
self.model = model
Car.count += 1
def display_info(self):
"""
显示汽车信息,包括实例属性和类属性。
"""
print(f"这是一辆{self.make}的{self.model}, 一共有{self.WHEELS}个轮子")
# 访问类属性
print(f"汽车有{Car.WHEELS}个轮子")
print(f"创建的汽车的实例(通过类名访问):{Car.count}")
# 创建实例,观察类属性的变化
car1 = Car("Toyota", "Camry")
car1.display_info()
print(f"创建的汽车的实例(通过类名访问):{Car.count}")
car2 = Car("Honda", "Civic")
car2.display_info()
print(f"创建的汽车的实例(通过类名访问):{Car.count}")
# 通过实例访问类属性
print(f"car1的轮子数量:{car1.WHEELS}") # 会向上寻找,查找类属性
print(f"car2的轮子数量:{car2.WHEELS}")
# 通过类名修改类属性
Car.WHEELS = 6 # 6个轮子的汽车
print(f"通过类名修改WHEELS:{Car.WHEELS}")
car1.display_info()
car2.display_info()
# 通过实例名修改类属性 (会创建同名的实例属性,遮蔽类属性)
car1.WHEELS = 8
print(f"通过 car1 实例修改 WHEELS 为:{car1.WHEELS}")
print(f"car2 WHEELS 为:{car2.WHEELS}")
print(f"Car.WHEELS是:{Car.WHEELS}")
# 检查实例和类的 __dict__
print(f"Car.__dict__ 中的 WHEELS:{Car.__dict__}")
print(f"car1.__dict__ 中的 WHEELS:{car1.__dict__}")
print(f"car2.__dict__ 中的 WHEELS:{car2.__dict__}") # car2 没有自己的 WHEELS 实例属性
4、类方法与静态方法
除了普通的实例方法外,Python还提供了类方法 (@classmethod
) 和静态方法 (@staticmethod
)。它们在定义、调用和访问类或实例数据方面有所不同,适用于不同的场景。
(1)实例方法
最常见的方法类型,第一个参数是 self,指向调用该方法的实例对象。
可以访问和修改实例属性 (self.attribute
) 和类属性 (self.class_attribute
或 ClassName.class_attribute
)。
执行与特定实例相关的操作,通常需要访问实例的数据。
比如:def my_instance_method(self, ...):
● 实例方法绑定: 当通过实例调用实例方法时(例如
obj.method()
),Python会执行一个“绑定”过程。它会创建一个新的“绑定方法”对象,这个对象包含了原始的函数对象和 obj 实例的引用。当这个绑定方法被调用时,obj 会作为第一个参数(self)隐式传递给原始函数。
(2)类方法
使用 @classmethod
装饰器修饰的方法。它的第一个参数约定俗成地命名为 cls
,指向类本身,而不是实例。
可以访问和修改类属性 (cls.class_attribute
),但不能直接访问实例属性(因为没有 self 参数指向实例)。可以通过 cls()
创建新的实例。
可以通过类名 (ClassName.my_class_method()
) 或实例名 (instance_name.my_class_method()
) 调用。
用途:
- 工厂方法 (Factory Methods): 提供额外的构造函数,以不同的方式创建类的实例。例如,从特定格式的数据(如字典、JSON)创建对象。
- 操作类属性:当方法需要修改或访问类属性时。
- 处理类级别的数据,与任何特定实例无关。
语法:
1
2
3
@classmethod
def my_class_method(cls, ...):
# cls 指向类本身
● 类方法绑定: 当通过类或实例调用类方法时(例如
ClassName.classmethod()
或obj.classmethod()
),Python也会执行绑定。它会创建一个新的“绑定方法”对象,这个对象包含了原始的函数对象和 ClassName 类对象的引用。当这个绑定方法被调用时,ClassName 会作为第一个参数(cls)隐式传递给原始函数。
(3)静态方法
使用 @staticmethod
装饰器修饰的方法。它不接受特殊的第一个参数(既没有 self 也没有 cls)。
既不能直接访问实例属性,也不能直接访问类属性。它本质上就是一个普通的函数,只是逻辑上归属于这个类。如果需要访问类或实例数据,必须通过参数显式传入。
可以通过类名 (ClassName.my_static_method()
) 或实例名 (instance_name.my_static_method()
) 调用。
用途:
■ 工具函数/辅助函数: 与类相关,但不需要访问类或实例的特定数据。例如,一个验证函数、一个数学计算函数。
■ 提高代码组织性:将相关的功能放在类中,即使它们不依赖于类的状态。
语法:
1
2
3
@staticmethod
def my_static_method(...):
# 没有 self 或 cls
● 静态方法: 静态方法没有绑定过程。它只是一个普通的函数,被存储在类的命名空间中。无论通过类还是实例调用,它都只是简单地执行,不接收任何隐式的 self 或 cls 参数。这就像在类外部定义了一个函数,然后将其引用放在了类的命名空间里。
(4)选择哪种方法
● 如果方法需要访问或修改实例数据,使用实例方法。
● 如果方法需要访问或修改类数据,或者需要创建类的新实例(工厂方法),使用类方法。
● 如果方法与类相关,但不需要访问类或实例的任何数据,使用静态方法。
(5)代码学习参考
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class Product:
"""
一个产品类,演示实例方法、类方法和静态方法。
"""
TAX_RATE = 0.05 # 税率
product_count = 0 # 产品总数
def __init__(self, name, price):
self.name = name
self.price = price
Product.product_count += 1 # 每次创建实例,产品总数增加
def get_final_price(self):
"""
实例方法:计算含税的最终价格。需要访问实例的 price。
"""
return self.price * (1 + Product.TAX_RATE)
@classmethod
def set_tax_rate(cls, new_rate):
"""
类方法:设置新的税率。需要访问和修改类属性 TAX_RATE。
cls 参数指向类本身 (Product)。
"""
if 0 <= new_rate <= 1:
Product.TAX_RATE = new_rate
print(f"税率已更新为: {Product.TAX_RATE}")
else:
print("错误: 税率必须在 0 到 1 之间。")
@classmethod
def create_from_string(cls, product_string):
"""
类方法:工厂方法,从字符串创建产品实例。
例如: "Laptop:1200"
"""
pro_l = product_string.split(":")
if len(pro_l) == 2:
name = pro_l[0]
try:
price = float(pro_l[1].strip()) # strip去掉前后的空格
return cls(name, price)
except ValueError:
print("错误: 价格必须是数字。")
else:
print("错误: 产品字符串格式不正确。")
@staticmethod
def is_valid_price(price):
"""
静态方法:检查价格是否有效。不依赖于实例或类的数据。
"""
return price > 0
@staticmethod
def get_product_category(product_name):
"""
静态方法:根据产品名称返回类别。与类本身或实例无关的辅助函数。
"""
if "Laptop" in product_name or "Computer" in product_name:
return "Electronics"
elif "Book" in product_name:
return "Books"
else:
return "General"
print("--- 实例方法调用 ---")
p1 = Product("Shirt", 200)
print(f"{p1.name}的最终价格:{p1.get_final_price()}")
print("\n--- 类方法调用 ---")
print(f"初始税率:{Product.TAX_RATE}")
Product.set_tax_rate(0.08)
print(f"新税率:{Product.TAX_RATE}")
print(f"{p1.name}的最终价格:{p1.get_final_price()}")
# 类方法作为工厂方法
p2 = Product.create_from_string("Laptop:1500")
if p2:
print(f"通过工厂方法创建的产品:{p2.name}, 价格为:{p2.price}")
print(f"{p2.name}的最终价格:{p2.get_final_price()}")
print("\n--- 静态方法调用 ---")
print(f"价格 100 是否有效?{Product.is_valid_price(100)}")
print(f"价格 -10 是否有效?{Product.is_valid_price(-10)}")
print(f"产品 'Laptop Pro' 的类别:{Product.get_product_category('Laptop Pro')}")
print(f"产品 'Python Book' 的类别:{Product.get_product_category('Python Book')}")
# 产品总数
print(f"现在创建的产品实例有{Product.product_count}个")
5、动态绑定属性和方法
Python 是一门动态类型语言,支持动态绑定,即可以在运行时为对象(实例、类或模块)添加或修改属性和方法,而无需在定义类或对象时预先声明。这种特性得益于 Python 的动态性和对象系统的灵活性,广泛用于网络编程、调试或动态配置场景。
(1)动态绑定属性
Python 允许在运行时为对象或类添加新的属性,无需在类定义中预先声明。
1
2
3
4
5
6
7
8
9
10
11
12
class Device:
pass
# 创建实例
router = Device()
# 动态绑定属性
router.ip_address = "192.168.1.1"
router.mac_address = "00:14:22:01:23:45"
print(router.ip_address) # 输出: 192.168.1.1
print(router.mac_address) # 输出: 00:14:22:01:23:45
router 是 Device 类的实例,初始时没有 ip_address
或 mac_address
属性。
通过赋值 router.ip_address = "192.168.1.1"
,动态为该实例添加了属性。
这些属性仅属于 router 实例,其他 Device 实例不会自动拥有这些属性。
也可以为类动态绑定属性,
1
2
3
4
5
6
7
8
9
10
11
class Device:
pass
# 为类添加属性
Device.default_gateway = "192.168.1.254"
# 所有实例共享类属性
router1 = Device()
router2 = Device()
print(router1.default_gateway) # 输出: 192.168.1.254
print(router2.default_gateway) # 输出: 192.168.1.254
(2)动态绑定方法
Python 允许在运行时为对象或类绑定方法,这通过将函数绑定到实例或类的属性实现。动态绑定的方法可以像普通方法一样调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Device:
pass
def send_packet(self, packet):
print(f"Sending packet from {self.mac_address}: {packet}")
# 创建实例
router = Device()
router.mac_address = "00:14:22:01:23:45"
# 动态绑定方法
from types import MethodType
router.send = MethodType(send_packet, router)
# 调用动态绑定的方法
router.send("Hello, Network!") # 输出: Sending packet from 00:14:22:01:23:45: Hello, Network!
也可以为类动态绑定方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Device:
pass
def broadcast(self, message):
print(f"Broadcasting from {self.mac_address}: {message}")
# 为类绑定方法
Device.broadcast = broadcast
# 所有实例都可以使用
router1 = Device()
router2 = Device()
router1.mac_address = "00:14:22:01:23:45"
router2.mac_address = "00:14:22:01:23:46"
router1.broadcast("Network Alert!") # 输出: Broadcasting from 00:14:22:01:23:45: Network Alert!
router2.broadcast("Network Alert!") # 输出: Broadcasting from 00:14:22:01:23:46: Network Alert!
- 性能:动态绑定灵活但可能增加运行时开销,频繁修改类结构需谨慎。
- 命名冲突:动态添加的属性或方法可能覆盖现有属性或方法,需检查命名。
- 调试:动态绑定可能使代码难以追踪,建议记录动态添加的属性/方法。
- 线程安全:在多线程网络应用中,动态绑定类属性/方法可能引发竞争条件,需加锁保护。