本文目录
- 多重继承与 MRO (Method Resolution Order, 方法解析顺序)
Python 面向对象编程
面向对象编程基础概念
面向对象编程 OOP, Object-Oriented Programming 是一种程序设计范式,在 Python、C++、Java、C# 等编程语言中都有实现。面向对象程序设计的思想在这些编程语言中是通用的,只是语法和实现方式上有略微的差别。
传统的程序设计范式多为面向过程编程,如 C语言,其思想是按照业务逻辑顺序组织程序代码,程序由一个个函数(或过程)组成,控制流程依次执行。
而面向对象编程,是在面向过程编程的基础之上发展而来,并且实现了更加结构化和模块化的代码组织方式。面向对象编程通过引入 类(Class) 和 对象(Object) 的概念,进一步抽象了现实世界的事物和行为,使得程序设计更加贴近现实世界的模型。
面向对象编程的主要特性
类(Class)与对象(Object):
在面向过程编程中,代码是通过函数来组织的,而在面向对象编程中,代码是通过类和对象来组织的。
- 类:是面向对象编程的基础,是对象的蓝图或模板,定义了对象的属性(成员变量)和方法(成员函数)。
- 对象:是类的实例,代表了类中的具体实体,包含了类中定义的属性和行为。
封装(Encapsulation):是面向对象编程(OOP)的基本特性之一,它指的是将数据(属性)和操作数据的代码(方法)封装在一起,并限制外部对数据的直接访问,而只能通过特定的方法进行访问和修改。封装的核心目标是隐藏实现细节,仅暴露必要的接口,以保护对象的内部状态不被外部干扰,增强代码的安全性、可维护性和可扩展性。
继承(Inheritance):是面向对象编程(OOP)中的另一个重要特性,它允许一个类(子类)继承另一个类(父类)的属性和方法,从而实现代码复用和功能扩展。继承是通过建立类与类之间的层次关系来组织代码的,它使得子类能够继承父类的行为(方法)和状态(属性),并可以根据需要重写或扩展这些功能。Python 、C++ 、Java 等支持面向对象编程语言中的继承实现略有不同,如 Python 和 C++ 支持多继承,而 Java 仅支持单继承。
多态(Polymorphism):多态是指相同的方法在不同的对象上具有不同的表现。它可以通过方法重载(函数参数不同)或方法重写(子类重写父类的方法)来实现。多态让程序更加灵活,可以通过同一接口处理不同类型的对象,使得代码具有更好的扩展性和维护性。
抽象(Abstraction):抽象是指将复杂的实现细节隐藏,只暴露必要的接口。抽象使得对象的行为可以被简化为接口,而不关心其具体实现。抽象通过类和接口来实现,让开发者可以专注于使用类的功能,而不需要了解其具体实现。
类和对象
类的定义:
classCar:
"""汽车类"""
def__init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
defdisplay_info(self):
returnf"{self.year}{self.make}{self.model}"
self.make :表示对象的 make 属性,对象的所有方法均可访问该属性。__init__(self, make, model, year) :构造方法,初始化对象。在每次实例创建时调用,可以用来初始化对象的某些属性。也可以没有参数,在创建对象的时候什么都不做。
类的实例化(对象的创建):
对象是类的实例,通过调用类名来创建对象。
my_car = Car("Toyota", "Corolla", 2020)
print(my_car.display_info()) # 输出:2020 Toyota Corolla
my_car = Car("Toyota", "Corolla", 2020):实例化了一个 Car 对象并传入参数初始化对象的属性。
类的属性与方法:
classPerson:
def__init__(self, name, age):
self.name = name
self.age = age
defgreet(self):
returnf"Hello, my name is {self.name} and I am {self.age} years old."
def greet(self) :实例方法,定义了一个对象的行为。
__init__() 构造方法
__init__() 是一个特殊的方法(构造函数),用于初始化对象。它在对象被创建时自动调用,通常用来为对象的属性赋值。
classDog:
def__init__(self, name, breed):
self.name = name
self.breed = breed
defbark(self):
returnf"{self.name} is barking!"
def __init__(self, name, breed)::构造方法,接收参数并初始化对象的属性。self.name = name:将传入的参数赋给对象的属性。
对象的生命周期
对象的生命周期包括创建、使用和销毁三个阶段:
- 创建:当我们实例化一个对象时,类的构造方法
__init__ 被调用,完成对象的初始化。 - 销毁:当对象不再被引用,Python 会自动销毁该对象,调用
__del__() 析构方法(如有定义)。
Python 使用垃圾回收机制来管理对象生命周期,确保不再使用的对象能够被及时销毁。
classPerson:
def__del__(self):
print(f"{self.name} is being deleted.") # 或者执行资源释放操作
__del__ 方法在对象被销毁时自动调用,可以用来释放资源。
classResourceManager:
"""资源管理器"""
def__init__(self, name):
self.name = name
print(f"资源 {self.name} 已创建")
defuse_resource(self):
"""使用资源"""
print(f"正在使用资源 {self.name}")
def__del__(self):
"""析构方法"""
print(f"资源 {self.name} 已被销毁")
# 演示对象生命周期
defcreate_and_use_resource():
resource = ResourceManager("数据库连接")
resource.use_resource()
# 函数结束时,resource 对象被销毁,__del__ 被调用
create_and_use_resource()
输出:
资源 数据库连接 已创建
正在使用资源 数据库连接
资源 数据库连接 已被销毁
对象的内存管理
Python 使用引用计数和垃圾回收(GC)机制来管理对象的内存。
- 引用计数:每个对象都有一个引用计数,当对象的引用计数为 0 时,该对象会被垃圾回收机制销毁。
- 垃圾回收:除了引用计数外,Python 还使用垃圾回收器来回收不可达的对象,尤其是在存在循环引用时。
可以通过 gc 模块来手动控制垃圾回收:
import gc
gc.collect() # 强制进行垃圾回收
import gc
classCircularRef:
"""循环引用演示"""
def__init__(self, name):
self.name = name
self.partner = None
def__del__(self):
print(f"{self.name} 被回收")
# 创建循环引用
a = CircularRef("A")
b = CircularRef("B")
a.partner = b
b.partner = a
# 删除引用
del a
del b
# 手动触发垃圾回收
print("手动触发垃圾回收...")
gc.collect() # 回收循环引用的对象
封装 Encapsulation
封装是面向对象编程中的一个基本原则,它指的是将对象的属性和方法封装在类内部,限制外部直接访问对象的内部实现。封装的目的是保护对象的状态,确保数据的完整性和安全性。
私有属性、公开属性:
私有属性:以双下划线 __ 开头的属性,表示私有属性,不能直接在外部访问。
classCar:
def__init__(self, make, model):
self.make = make
self.__model = model
defget_model(self):
return self.__model # 通过公开方法访问私有属性
私有方法、公开方法:
私有方法:以双下划线开头的函数,表示私有方法, 不能在类的外部调用。
classAccount:
def__init__(self, balance):
self.__balance = balance
defdeposit(self, amount):
if amount > 0:
self.__balance += amount
else:
print("Amount must be positive.")
def__private_method(self):
print("This is a private method.")
实例属性与类属性
实例属性:属于实例对象,通常在 __init__() 构造方法中定义。每个对象有独立的实例属性。
类属性:属于类本身,所有实例共享同一个类属性。可以通过类名访问,也可以通过实例访问,但推荐通过类名访问。
classPerson:
species = "Homo sapiens"
def__init__(self, name, age):
self.name = name
self.age = age
classStudent:
"""学生类"""
school = "Python编程学院"# 类属性
total_students = 0# 类属性
def__init__(self, name, student_id):
self.name = name # 实例属性
self.student_id = student_id # 实例属性
Student.total_students += 1# 修改类属性
@classmethod
defget_total_students(cls):
"""获取学生总数(类方法)"""
return cls.total_students
@staticmethod
defis_valid_student_id(student_id):
"""验证学号格式(静态方法)"""
return isinstance(student_id, str) and len(student_id) == 8
# 创建学生对象
student1 = Student("张三", "20230001")
student2 = Student("李四", "20230002")
print(student1.school) # 通过实例访问类属性
print(Student.school) # 通过类访问类属性
print(Student.get_total_students()) # 输出: 2
print(Student.is_valid_student_id("20230001")) # 输出: True
species:是类属性,所有 Person 对象共享该属性。name 和 age 是实例属性,每个 Person 对象有自己的 name 和 age 。
self 和 cls 的区别
self:指向当前实例对象,用于访问实例属性和方法。
cls:指向类本身,用于访问类属性和类方法。
classExample:
class_attr = 0# 类属性
def__init__(self, val):
self.val = val # 实例属性
@classmethod # 使用 @classmethod 装饰器的方法称之为类方法。
defclass_method(cls):# 第一个参数通常是 cls 表示类本身
print(f"Class attribute: {cls.class_attr}")
类方法通常用于操作类属性,或实现一些类级别的功能。类方法可以通过类本身或类的实例来调用,但通常推荐通过类名来调用,以表示类级别的操作。
@property 装饰器
@property 装饰器用于将方法转换成属性,使得方法可以像属性一样访问,避免显式调用方法。
classCircle:
def__init__(self, radius):
self._radius = radius
# 单下划线开头的属性不算是私有属性,它只是一种命名规定,表示期望只在类内部使用,但类外部使用也无妨
@property
defradius(self):
return self._radius
@radius.setter
defradius(self, value):
if value >= 0:
self._radius = value
else:
print("Radius cannot be negative.")
@property 将 radius 方法变成了属性,可以像访问普通属性一样访问它。@radius.setter 用于定义设置属性的方式。
为什么需要@property?
classPerson:
def__init__(self, name, age):
self.name = name
self.age = age # 直接暴露属性,无法做验证
p = Person("Jackey Song", -100) # 年龄负数也能设置不合理!
传统解决方案(getter/setter方法)
classPerson:
def__init__(self, name, age):
self.name = name
self._age = age
defget_age(self):
return self._age
defset_age(self, age):
if age < 0:
raise ValueError("年龄不能为负数")
self._age = age
p = Person("Jackey Song", 24)
p.set_age(18) # 设置: 调用方法
print(p.get_age()) # 获取: 调用方法
问题:代码变得冗长,不够优雅。
使用@property的优雅方案:
classPerson:
def__init__(self, name, age):
self.name = name
self._age = age
# _age 弱私有属性,约定在类内部访问,但外部仍可访问。__age 双下划线开头则强制不可从外部访问。
@property # 装饰器,将方法变成属性
defage(self):
"""获取年龄"""
return self._age
@age.setter # 设置属性值
defage(self, value):
"""设置年龄,带验证"""
if value < 0:
raise ValueError("年龄不能为负数")
self._age = value
@age.deleter # 删除属性
defage(self):
"""删除年龄属性"""
print("删除年龄")
del self._age
p = Person("Jackey Song", 24)
print(p.age) # 像属性一样访问,实际上调用了 getter
p.age = 18# 像属性一样赋值,实际上调用了 setter
p.age = -5# ValueError: 年龄不能为负数
@property 的三种用法
- 只读属性,只有 getter,即
@property - 可读写属性,getter + setter,
@property + @[attribute].setter - 可删除属性,getter + setter + deleter,
@property + @[attribute].setter + @[attribute].deleter
常见应用示例:
classStudent:
def__init__(self, name, score):
self.name = name
self._score = score
@property
defscore(self):
return self._score
@score.setter
defscore(self, value):
ifnot0 <= value <= 100:
raise ValueError("分数必须在 0-100 之间")
self._score = value
@property
defgrade(self):
"""根据分数计算等级"""
if self.score >= 90:
return"A"
elif self.score >= 80:
return"B"
elif self.score >= 70:
return"C"
elif self.score >= 60:
return"D"
else:
return"F"
s = Student("李四", 85)
print(s.grade) # B
s.score = 95
print(s.grade) # A
classDataProcessor:
def__init__(self, data):
self.data = data
self._result = None
@property
defresult(self):
"""延迟计算,只计算一次"""
if self._result isNone:
print("正在计算结果...")
self._result = sum(self.data) / len(self.data)
return self._result
dp = DataProcessor([1, 2, 3, 4, 5])
print(dp.result) # 第一次:正在计算结果... 3.0
print(dp.result) # 第二次:直接返回缓存 3.0
classProduct:
def__init__(self, price):
self._price = price
# 如果最初是直接属性
# self.price = price
# 后续想添加验证,但不想改变 API
@property
defprice(self):
return self._price
@price.setter
defprice(self, value):
if value < 0:
raise ValueError("价格不能为负数")
self._price = value
# 原有代码不需要修改
p = Product(100)
print(p.price) # 100
p.price = 120# 仍然可以赋值
注意事项
@property属性与实例属性同名的问题:
classExample:
def__init__(self, value):
self.value = value # 无限递归
@property
defvalue(self):
return self.value
@value.setter
defvalue(self, value):
self.value = value
# 正确写法
classExample:
def__init__(self, value):
self._value = value
@property
defvalue(self):
return self._value
@value.setter
defvalue(self, value):
self._value = value
方法调用与实例化
实例方法与类方法的调用方式:
实例方法:通过对象调用实例方法,通常第一个参数是 self。
classMyClass():
def__init__(self):
... ...
obj = MyClass()
obj.instance_method() # 通过实例调用
类方法:通过类名调用类方法,通常第一个参数是 cls。
MyClass.class_method() # 通过类名调用
实例化时属性初始化
obj = MyClass() 通过类的蓝图创建对一个对象的时候,__init__() 构造方法会被自动调用,用来初始化对象的某些属性。
classPerson:
def__init__(self, name, age):
self.name = name
self.age = age
jackey = Person("Jackey Song", 24) # 创建 jackey 对象时,自动调用 __init__() 方法,传入姓名和年龄
print(jackey.name) # Jackey Song
print(jackey.age) # 24
静态方法 @staticmethod
Python 的类中通常有三类方法,
- 实例方法:最常见,第一个参数
self 表示调用它的对象。 - 类方法:第一个参数
cls ,表示类本身,定义时需要有 @classmethod 装饰器。 - 静态方法:由
@staticmethod 装饰的方法。
@staticmethod 是 Python 中的一个装饰器,用于将方法定义为静态方法。静态方法不需要传递 self 实例或 cls 类作为第一个参数,它就像是定义在类内部的普通函数。适用场景:功能逻辑上属于这个类,但不需要访问实例属性或类属性。如果放在类的外部,会破坏封装性;如果定义为实例方法,会多出一个无用的 self 参数。
classMethodTypes:
"""方法类型演示"""
class_variable = "类变量"
def__init__(self, name):
self.name = name
self.instance_variable = "实例变量"
definstance_method(self):
"""实例方法"""
print(f"实例方法 - self: {self}")
print(f"访问实例变量: {self.instance_variable}")
print(f"访问类变量: {self.class_variable}")
@classmethod
defclass_method(cls):
"""类方法"""
print(f"类方法 - cls: {cls}")
print(f"访问类变量: {cls.class_variable}")
# 无法访问实例变量
@staticmethod
defstatic_method():
"""静态方法"""
print("静态方法 - 无需 self 或 cls")
# 无法直接访问实例变量或类变量
# 调用示例
obj = MethodTypes("测试对象")
obj.instance_method() # 通过实例调用实例方法
MethodTypes.class_method() # 通过类调用类方法
MethodTypes.static_method() # 通过类调用静态方法
# 类方法也可以通过实例调用
obj.class_method()
obj.static_method()
面向对象核心
继承 Inheritance
继承允许我们创建一个新类,基于一个或多个现有类,从而复用代码并建立类之间的层次关系。
子类会自动获得父类所有的属性和方法,并可以添加新的或修改已有的。
classAnimal:# 父类
def__init__(self, name):
self.name = name
defspeak(self):
print(f"{self.name} 发出声音")
classDog(Animal):# 子类,继承 Animal
defbark(self):
print(f"{self.name} 汪汪叫!")
# 使用
dog = Dog("旺财")
dog.speak() # 继承的方法:旺财 发出声音
dog.bark() # 子类自己的方法:旺财 汪汪叫!
继承的语法
# 单继承
classChildClass(ParentClass):
pass
# 多继承
classChildClass(Parent1, Parent2, Parent3):
pass
继承的优势
方法重写 Method Overriding
子类可以重新定义父类中已有的方法,称为方法重写。
classAnimal:
defspeak(self):
print("动物在叫")
classCat(Animal):
defspeak(self):# 重写父类的 speak 方法
print("喵喵喵")
classDog(Animal):
defspeak(self):# 重写父类的 speak 方法
print("汪汪汪")
# 多态:同一个方法名,不同行为
animals = [Cat(), Dog()]
for a in animals:
a.speak()
# 输出:
# 喵喵喵
# 汪汪汪
在重写时扩展父类方法:即执行父类的逻辑,又添加新的逻辑。
classVehicle:
def__init__(self, brand, model):
self.brand = brand
self.model = model
definfo(self):
returnf"{self.brand}{self.model}"
classCar(Vehicle):
def__init__(self, brand, model, doors):
# 调用父类的 __init__ 方法
super().__init__(brand, model)
self.doors = doors
definfo(self):
# 先获取父类的信息,再添加自己的信息
base_info = super().info()
returnf"{base_info}, 车门数: {self.doors}"
car = Car("Toyota", "Camry", 4)
print(car.info()) # Toyota Camry, 车门数: 4
super() 的使用
super() 用于调用父类的方法,在单继承和多继承中都非常重要。
基本用法:
classParent:
def__init__(self, name):
self.name = name
print(f"Parent __init__: {name}")
defgreet(self):
print(f"Hello from Parent, I'm {self.name}")
classChild(Parent):
def__init__(self, name, age):
super().__init__(name) # 调用父类的 __init__() 方法
self.age = age
print(f"Child __init__: age={age}")
defgreet(self):
super().greet()
print(f"Hello from Child, I'm {self.age} years old.")
c = Child("小明", 10)
c.greet()
使用 super() 而不是直接 Parent.method(self) 的好处:
# ❌ 不推荐:硬编码父类名
classChild(Parent):
defmethod(self):
Parent.method(self) # 如果父类改名,这里也要改
# ✅ 推荐:使用 super()
classChild(Parent):
defmethod(self):
super().method() # 自动找到正确的父类
多重继承与 MRO (Method Resolution Order, 方法解析顺序)
Python 支持多重继承,即一个子类可以继承多个父类。
多重继承:
classA:
defmethod(self):
print("A's method")
classB:
defmethod(self):
print("B's method")
classC(A, B):# 继承 A 和 B
pass
c = C()
c.method() # 输出: A's method(因为 A 在继承列表中排在前面)
菱形继承问题:
Python 通过 C3线性化算法 来处理多重继承的 MRO。MRO 的规则基于广度优先的继承顺序,将菱形的继承关系线性化,可以通过 class.__mro__ 或 class.mro() 查看。
classA:
defhello(self):
print("Hello from A")
classB(A):
defhello(self):
super().hello()
print("Hello from B")
classC(A):
defhello(self):
super().hello()
print("Hello from C")
classD(B, C):
defhello(self):
super().hello()
print("Hello from D")
d = D()
d.hello()
print(D.__mro__)
print(D.mro())
输出:
Hello from A
Hello from C
Hello from B
Hello from D
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
复杂多重继承示例:
classAnimal:
def__init__(self, name):
self.name = name
print("Animal __init__")
defmove(self):
print("Animal moving")
classFlyable:
def__init__(self, wingspan):
self.wingspan = wingspan
print("Flyable __init__")
defmove(self):
print("Flying")
classSwimmable:
def__init__(self, depth):
self.depth = depth
print("Swimmable __init__")
defmove(self):
print("Swimming")
classDuck(Animal, Flyable, Swimmable):
def__init__(self, name, wingspan, depth):
# 使用 super() 按 MRO 顺序调用父类
super().__init__(name)
# 需要显式调用其他父类的初始化
Flyable.__init__(self, wingspan)
Swimmable.__init__(self, depth)
defmove(self):
# 可以选择调用特定父类的方法
super().move() # 按 MRO 顺序调用第一个 move
print("Duck waddling")
# 查看 Duck 的 MRO
print(Duck.__mro__)
# (<class '__main__.Duck'>, <class '__main__.Animal'>, <class '__main__.Flyable'>, <class '__main__.Swimmable'>, <class 'object'>)
duck = Duck("唐老鸭", 30, 5)
duck.move()
(<class '__main__.Duck'>, <class '__main__.Animal'>, <class '__main__.Flyable'>, <class '__main__.Swimmable'>, <class 'object'>)
Animal__init__
Flyable__init__
Swimmable__init__
Animalmoving
Duckwaddling
super() 在多重继承中会按照 MRO 顺序 调用下一个类:
classBase:
defmethod(self):
print("Base.method")
classA(Base):
defmethod(self):
print("A.method start")
super().method()
print("A.method end")
classB(Base):
defmethod(self):
print("B.method start")
super().method()
print("B.method end")
classC(A, B):
defmethod(self):
print("C.method start")
super().method()
print("C.method end")
c = C()
c.method()
print(C.__mro__)
输出:
C.method start
A.method start
B.method start
Base.method
B.method end
A.method end
C.method end
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
object 类是所有类的基类。
多态 Polymorphism
多态 = 多种形态。同一个东西在不同的场景下表现出不同的行为。
在 Python 多态 即同一个方法名,在不同的类中有着不同的实现。
classDog:
defspeak(self):
return"汪汪汪"
classCat:
defspeak(self):
return"喵喵喵"
classBird:
defspeak(self):
return"叽叽叽"
# 多态:同一个方法名 speak(),不同的行为
dog = Dog()
cat = Cat()
bird = Bird()
print(dog.speak())
print(cat.speak())
print(bird.speak())
继承中的多态:
classShape:
"""形状基类"""
defarea(self):
pass
classRectangle(Shape):
def__init__(self, width, height):
self.width = width
self.height = height
defarea(self):
return self.width * self.height
classCircle(Shape):
def__init__(self, radius):
self.radius =radius
defarea(self):
return3.14 * self.radius ** 2
classTriangle(Shape):
def__init__(self, base, height):
self.base = base
self.height = height
defarea(self):
return self.base * self.height / 2
# 多态: 所有形状都有 area 方法,但实现各不相同
shapes = [Rectangle(10, 5), Circle(7), Triangle(6, 4)]
for shape in shapes:
print(f"面积: {shape.area()}")
鸭子类型 Duck Typing
Python 的多态不依赖于继承!只要一个对象 "看起来像鸭子,走起来像鸭子",它就是鸭子。
classDuck:
defquack(self):
print("呱呱呱")
defswim(self):
print("鸭子游泳")
classPerson:
defquack(self):
print("人在模仿鸭子叫")
defswim(self):
print("人在游泳")
defmake_it_quack(thing):
"""只要对象有 quack 方法,就能调用"""
thing.quack()
duck = Duck()
person = Person()
make_it_quack(duck)
make_it_quack(person)
"如果它走起来像鸭子,叫起来像鸭子,那么它就是鸭子。" —— 我们关心的不是对象是什么类型,而是它能做什么。
运算符多态:
Python 中的运算符本身就是多态的:
# + 运算符的多态
print(1 + 2) # 整数加法
print("Hello " + "World") # 字符串拼接
print([1, 2] + [3, 4]) # 列表合并
# len() 函数的多态
print(len("Python")) # 字符串的长度 6
print(len([1, 2, 3])) # 列表长度 3
print(len({"a": 1, "b": 2}))# 字典长度 2
动态绑定:
方法的调用在运行时才确定,而不是在编译时。
classAnimal:
defspeak(self):
return"动物叫"
classDog(Animal):
defspeak(self):
return"汪汪汪"
classCat(Animal):
defspeak(self):
return"喵喵喵"
defmake_sound(animal: Animal):
# 类型注解只是一个提示,而非硬性规定只能传入 Animal 类型。animal 只是一个参数名字,调用哪个方法在运行时确定的
return animal.speak()
dog = Dog()
cat = Cat()
print(make_sound(dog))
print(make_sound(cat))
Python 中如何实现方法重载
方法重载 :在同一个类中定义多个同名但参数不同的方法。调用的时候根据传入的参数不同来调用不同的方法。
C++/Java 支持方法重载,但 Python 不支持,不过可以通过一些技巧实现类似 C++/Java 中方法重载的效果。Java 中方法重载如下:
classCalculator{
intadd(int a, int b){ return a + b;}
intadd(int a, int b, int c){ return a + b + c; }
doubleadd(double a, double b){ return a + b; }
}
Python 中定义多个同名的方法,后面的会覆盖前面的:
classCalculator:
defadd(self, a, b):
print("两个参数")
return a + b
defadd(self, a, b, c):
print("三个参数")
return a + b + c
calc = Calculator()
# print(calc.add(1, 2)) # TypeError
print(calc.add(1, 2, 3))
Python 中可以通过 *args 可变参数,来实现类似方法重载的效果:
classCalculator:
defadd(self, *args):
"""接受任意数量的参数"""
if len(args) == 2:
return args[0] + args[1]
elif len(args) == 3:
return args[0] + args[1] + args[2]
else:
return sum(args)
或者使用 functools.singledispatchmethod 单分派泛型方法(Python 3.8+):
from functools import singledispatchmethod
classProcessor:
@singledispatchmethod
defprocess(self, arg):
print(f"默认处理: {arg}")
@process.register(int)
def_(self, arg):
print(f"处理整数: {arg}")
@process.register(str)
def_(self, arg):
print(f"处理字符串: {arg}")
@process.register(list)
def_(self, arg):
print(f"处理列表,长度: {len(arg)}")
p = Processor()
p.process(100) # 处理整数: 100
p.process("hello") # 处理字符串: hello
p.process([1,2,3]) # 处理列表,长度: 3
p.process(3.14) # 默认处理: 3.14
区分 functools.singledispatch 单分派泛型函数用于函数装饰器:
from functools import singledispatch
@singledispatch
defprocess(arg):
print(f"默认处理: {arg}")
@process.register(int)
def_(arg):
print(f"处理整数: {arg}")
@process.register(str)
def_(arg):
print(f"处理字符串: {arg}")
@process.register(list)
def_(arg):
print(f"处理列表,长度: {len(arg)}")
# 直接调用函数
process(100) # 处理整数: 100
process("hello") # 处理字符串: hello
process([1,2,3]) # 处理列表,长度: 3
process(3.14) # 默认处理: 3.14
抽象 Abstraction
抽象:隐藏实现细节,只暴露必要的行为接口。
classAnimal:
defspeak(self):
pass# 空实现,但这样不够强制
classDog(Animal):
pass# 忘记实现 speak
dog = Dog()
dog.speak() # 不会报错,但也没做任何事(静默失败)
使用 abc 模块创建抽象类:
有时候,父类本身不应该被实例化,它只是一个 "模板" 或 "契约"。
from abc import ABC, abstractmethod
classAnimal(ABC):
"""抽象类: 不能被实例化"""
@abstractmethod
defspeak(self):
"""抽象方法: 子类必须实现"""
pass
@abstractmethod
defmove(self):
"""抽象方法: 子类必须实现"""
pass
defsleep(self):
"""普通方法: 子类中可以直接继承使用"""
return"睡觉中..."
# a = Animal() # ❌ TypeError: Can't instantiate abstract class Animal
classDog(Animal):
defspeak(self):
return"汪汪汪"
defmove(self):
return"用腿跑"
classBird(Animal):
defspeak(self):
return"叽叽叽"
defmove(self):
return"用翅膀飞"
dog = Dog()
bird = Bird()
print(dog.speak()) # 汪汪汪
print(dog.move()) # 用腿跑
print(dog.sleep()) # 睡觉中...
from abc import ABC, abstractmethod
classPayment(ABC):
"""支付抽象基类"""
@abstractmethod
defpay(self, amount):
"""支付方法,子类必须实现"""
pass
@abstractmethod
defrefund(self, amount):
"""退款方法,子类必须实现"""
pass
classAlipay(Payment):
defpay(self, amount):
print(f"支付宝支付 {amount} 元")
defrefund(self, amount):
print(f"支付宝退款 {amount} 元")
classWechatPay(Payment):
defpay(self, amount):
print(f"微信支付 {amount} 元")
defrefund(self, amount):
print(f"微信退款 {amount} 元")
# p = Payment() # ❌ TypeError: 不能实例化抽象类
alipay = Alipay()
wechat = WechatPay()
defprocess_payment(payment_method, amount):
payment_method.pay(amount)
process_payment(alipay, 100) # 支付宝支付 100 元
process_payment(wechat, 50) # 微信支付 50 元
属性和类的抽象
from abc import ABC, abstractmethod
classVehicle(ABC):
@property
@abstractmethod
defwheels(self):
"""抽象属性"""
pass
@classmethod
@abstractmethod
defget_type(cls):
"""抽象类方法"""
pass
classCar(Vehicle):
@property
defwheels(self):
return4
@classmethod
defget_type(cls):
return"汽车"
classBike(Vehicle):
@property
defwheels(self):
return2
@classmethod
defget_type(cls):
return"自行车"
print(Car.get_type()) # 汽车
print(Car().wheels) # 4
接口与协议
接口定义了“一个对象应该提供哪些方法”,但不提供具体实现。它是一份契约:只要一个类实现了接口要求的所有方法,它就可以被当作该接口类型使用。
Python 中并没有 interface 关键字。Python中实现类似接口的效果有三种方式:
协议 protocol:Python 3.8+ 引入,通过 typing.Protocol 实现。它提供了一种静态类型检查的接口定义方式,同时保持鸭子类型的灵活性。
from typing import Protocol
classDrawable(Protocol):
"""定义 Drawable 协议"""
defdraw(self) -> None:
...
defget_size(self) -> tuple:
...
# 不需要显式继承!只要实现了这些方法就自动符合协议
classCircle:
def__init__(self, radius):
self.radius = radius
defdraw(self) -> None:
print(f"画圆,半径: {self.radius}")
defget_size(self) -> tuple:
return (self.radius * 2, self.radius * 2)
classSquare:
def__init__(self, side):
self.side = side
defdraw(self) -> None:
print(f"画正方形,边长: {self.side}")
defget_size(self) -> tuple:
return (self.side, self.side)
classBadShape:
defdraw(self) -> None:
print("画图形")
# 缺少 get_size 方法,不符合协议
defrender(shape: Drawable) -> None:
"""只接受符合 Drawable 协议的对象"""
shape.draw()
print(f"尺寸: {shape.get_size()}")
render(Circle(5)) # ✅ Circle 符合 Drawable 协议
render(Square(10)) # ✅ Square 符合 Drawable 协议
# render(BadShape()) # mypy 静态检查会报错
协议 VS 抽象基类:
Mixin 类的使用
Mixin 是一种设计模式,它是一个只提供特定功能的小型类,目的是被其他类继承,为子类添加额外的能力(方法),而不是作为独立的基类使用。Mixin 就是可插拔的功能模块。
classJSONMixin:
"""JSON 序列化 Mixin"""
defto_json(self):
"""转换为JSON字符串"""
import json
return json.dumps(self.__dict__) # self.__dict__ 将当前实例中的所有属性和值转化为键值对存进字典
classXMLMixin:
"""XML 序列化 Mixin"""
defto_xml(self):
"""转换为XML字符串"""
xml = "<object>"
for key, value in self.__dict__.items():
xml += f"<{key}>{value}</{key}>"
xml += "</object>"
return xml
classPerson(JSONMixin, XMLMixin):
"""人类(混入序列化能力)"""
def__init__(self, name, age):
self.name = name
self.age = age
classProduct(JSONMixin):
"""产品类(只混入JSON序列化)"""
def__init__(self, name, price):
self.name = name
self.price = price
# 使用示例
person = Person("张三", 25)
print(person.to_json()) # {"name": "张三", "age": 25}
print(person.to_xml()) # <object><name>张三</name><age>25</age></object>
product = Product("笔记本电脑", 5999)
print(product.to_json()) # {"name": "笔记本电脑", "price": 5999}
# print(product.to_xml()) # AttributeError: Product 没有 to_xml 方法
特殊方法与魔法方法
在 Python 中,特殊方法(又叫魔法方法,Megic Methods)是以双下划线 __ 开头和结尾的一种方法。它们通常用于实现 Python 中的内建操作,如对象创建、表示、比较、运算符重载等。通过这些方法,Python 类可以自定义一些特殊行为,以适应语言的特性。
构造与销毁方法
__new__ 与 __init__
- 它在
__init__ 之前调用。通常我们不需要手动重写 __new__ ,但当我们想要控制对象的创建(例如单例模式或元类时),我们会重写它。
- 是对象实例化后调用的方法,用于初始化对象的状态,如为实例变量赋值。
__init__ 只在对象创建之后执行,不能控制对象的创建过程。- 它不像
__new__ 返回实例,而是对实例进行初始化。
classMyClass:
def__new__(cls):# __new__ 方法的 cls 参数指的就是该类本身
print("__new__被调用")
return super().__new__(cls) # 调用 object.__new__() , object 类默认是所有类的鼻祖
def__init__(self):
print("__init__被调用")
obj = MyClass()
__del__
- 当对象生命周期结束时,Python 会自动调用
__del__ 方法进行清理操作。 - 通常用来释放外部资源(如文件句柄、数据库连接等)。
- 需要注意的是,
__del__ 在 Python 中并不总是会被调用,特别是循环引用的情况下,可能需要手动触发 gc.collect() 来进行垃圾回收。
classMyClass:
def__def__(self):
print("对象被销毁")
obj = MyClass()
defobj # 强制销毁对象
表示方法
__repr__:用于提供一个详细且正式的字符串表示,旨在让开发者能够通过字符串重建对象。通常 __repr__ 应该返回一个合法的 Python 表达式。
__str__:用于返回用户友好的字符串表示,通常用于打印输出。__str__ 提供的是更简洁、可读性强的字符串。
| | |
|---|
__str__(self) | print(obj) | |
__repr__(self) | repr(obj) | |
classMyClass:
def__repr__(self):
return"MyClass()"
def__str__(self):
return"这是一个 MyClass 对象"
obj = MyClass()
print(repr(obj)) # 输出: MyClass()
print(str(obj)) # 输出: 这是一个 MyClass 对象
classPerson:
def__init__(self, name, age):
self.name = name
self.age = age
def__str__(self):
returnf"姓名: {self.name}, 年龄: {self.age}"
def__repr__(self):
returnf"Person('{self.name}', {self.age})"
p = Person("张三", 25)
print(p) # 姓名: 张三, 年龄: 25
print(repr(p)) # Person('张三', 25)
运算符重载
运算符重载:在类中实现运算符的魔法方法,可以用运算符来操作对象之间运算符级运算。
| | |
|---|
__add__(self, other) | + | |
__sub__(self, other) | - | |
__mul__(self, other) | * | |
__truediv__(self, other) | / | |
__eq__(self, other) | == | |
__lt__(self, other) | < | |
__getitem__(self, key) | obj[key] | |
__setitem__(self, key, value) | obj[key] = value | |
classVector:
def__init__(self, x, y):
self.x = x
self.y = y
def__add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def__eq__(self, other):
return self.x == other.x and self.y == other.y
def__getitem__(self, index):
if index == 0:
return self.x
elif index == 1:
return self.y
raise IndexError("索引超出范围")
def__str__(self):
returnf"Vector({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2 # 调用 __add__
print(v3) # Vector(4, 6)
print(v1 == v2) # False
print(v1[0]) # 1(调用 __getitem__)
容器类型
| |
|---|
__len__(self) | |
__getitem__(self, key) | |
__setitem__(self, key, value) | |
__delitem__(self, key) | |
__contains__(self, item) | |
__iter__(self) | |
classMyList:# 实现自定义列表
def__init__(self):
self._items = []
defappend(self, item):
self._items.append(item)
def__len__(self):
return len(self._items)
def__getitem__(self, index):
return self._items[index]
def__setitem__(self, index, value):
self._items[index] = value
def__contains__(self, item):
return item in self._items
def__iter__(self):
return iter(self._items)
my_list = MyList()
my_list.append(1)
my_list.append(2)
my_list.append(3)
print(len(my_list)) # 3(__len__)
print(my_list[1]) # 2(__getitem__)
my_list[1] = 20# __setitem__
print(2in my_list) # False(__contains__)
for item in my_list: # __iter__
print(item) # 1, 20, 3
可调用对象
| | |
|---|
__call__(self, *args, **kwargs) | obj() | |
classCounter:
def__init__(self):
self.count = 0
def__call__(self):
self.count += 1
return self.count
counter = Counter()
print(counter()) # 1(像函数一样调用)
print(counter()) # 2
print(counter()) # 3
上下文管理
| | |
|---|
__enter__(self) | | |
__exit__(self, exc_type, exc_val, exc_tb) | | |
classFile:
def__init__(self, filename, mode):
self.filename = filename
self.mode = mode
def__enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def__exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
print("文件已关闭")
with File("test.txt", "w") as f:
f.write("Hello")
# 退出 with 块时自动关闭文件
属性访问控制
| |
|---|
__getattr__(self, name) | |
__setattr__(self, name, value) | |
__delattr__(self, name) | |
classDynamicAttr:
def__getattr__(self, name):
returnf"属性 {name} 不存在,这是动态返回的"
def__setattr__(self, name, value):
print(f"正在设置属性 {name} = {value}")
super().__setattr__(name, value) # 实际设置属性
obj = DynamicAttr()
print(obj.name) # 属性 name 不存在,这是动态返回的
obj.age = 25# 正在设置属性 age = 25
print(obj.age) # 25
类的设计模式
单例模式 Single Pattern
单例模式 是一种常用的软件设计模式,目的是限制类的实例化次数,确保一个类在整个应用中只有一个实例,并提供一个全局访问点来获取该实例。
适用场景:数据库连接池、配置管理器、日志记录器、线程池等。
实现方式一:重写 __new__
classSingleton:
_instance = None
def__new__(cls, *args, **kwargs):
if cls._instance isNone:
print("创建第一个实例")
cls._instance = super().__new__(cls)
else:
print("返回已有实例")
return cls._instance
def__init__(self, value=None):
# 注意:每次调用都会执行 __init__!
# 需要避免重复初始化
ifnot hasattr(self, 'initialized'):
self.value = value
self.initialized = True
# 测试
s1 = Singleton("第一个")
s2 = Singleton("第二个")
print(s1 is s2) # True
print(s1.value) # 第一个
print(s2.value) # 第一个(没有被覆盖)
实现方式二:使用装饰器
defsingleton(cls):
instances = {}
defget_instance(*args, **kwargs):
if cls notin instances:
instances[cls] = cls(*args, **kwargs)
# 这里的 cls 指的就是 Database 类
# 1. 类对象是可哈希的,所以可以作为字典的键。
# 2. cls 本质上存储的是类对象的内存地址
# 3. 类似C/C++中我们常说的指针概念 (但Python中没有显式的指针语法)
return instances[cls]
return get_instance
@singleton
classDatabase:
def__init__(self, connection_string):
self.connection_string = connection_string
print(f"初始化数据库: {connection_string}")
db1 = Database("mysql://localhost")
db2 = Database("postgresql://localhost")
print(db1 is db2) # True
print(db1.connection_string)
Database 类会被传入 singleton 装饰器。singleton 会返回 get_instance 函数,而 Database 类本身被替换为 get_instance 函数。Database 的实例化将通过 get_instance 函数进行管理。
实现方式三:使用元类。
元类 Metaclass - type
元类是创建类的类。在 Python 中,type 是所有类的元类。当我们定义一个类的时候,实际上是通过某个元类来创建该类的。Python 中 类本身就是通过 type 来创建的,因此 type 也是所有类的元类。type 是 Python 中最基础的元类,可以把它想象成一个类的工厂,它负责创建类。每个类和实例,实际上都是由 type 创建的。通过 type ,我们可以动态创建类、控制类的行为。
# 创建一个类
MyClass = type('MyClass', (object,), {'x': 42})
# 创建该类的实例
obj = MyClass()
print(obj.x) # 输出 42
使用元类实现单例模式:
classSingletonMeta(type):
_instances = {}
def__call__(cls, *args, **kwargs):
print("__call__", args, kwargs)
if cls notin cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
classConfig(metaclass=SingletonMeta):
def__new__(cls, *args, **kwargs):
print("__new__", args, kwargs)
return super().__new__(cls)
def__init__(self, settings, others="others"):
self.settings = settings
print("__init__")
c1 = Config("settings1", others="others1")
c2 = Config("settings2", others="others2")
print(c1 is c2)
print(c1.settings, c2.settings)
输出:
__call__ ('settings1',) {'others': 'others1'}
__new__ ('settings1',) {'others': 'others1'}
__init__
__call__ ('settings2',) {'others': 'others2'}
True
settings1 settings1
SingletonMeta 要继承 type 元类,从而定义一个元类来控制类的创建。Config 类通过 metaclass=SingletonMeta 语法来为其指定一个元类。当 Config 类被调用的时候,也就是以 Config() 的形式调用时,首先会调用其元类的 __call__() 方法,type 默认是所有类的元类,而上面实现单例模式的方法是通过继承 type 的方式,添加 __call__ 方法的额外行为,并在 Config 类的定义中指定其元类为自定义元类来实现的。- 以上代码中
SingletonMeta.__call__(cls, *args, **kwargs) 中的 cls 参数指的是 Config 类而非 SingletonMeta 类。SingletonMeta 是创建 Config 类的类,所以 Config 类是 SingletonMeta 类的实例,所以这里的 cls 参数传入的是 Config 类,_instances 是 SingletonMeta 类的类属性,所以 _instances 属性既可以通过类名 SingletonMeta._instances 来访问,也可以通过 SingletonMeta 类的实例 cls._instances 来访问。
在类的实例化过程中,通常的调用顺序是:
- 类被调用:首先会触发
type.__call__(),元类(默认为 type)的 __call__ 方法被调用。 - 创建实例:
type.__call__ 会调用类的 __new__() 方法来创建一个新的实例。这里的 __new__ 负责创建对象。 - 初始化实例:创建完实例之后,
__new__() 返回的对象会触发 __init__() 来初始化对象。
线程安全的单例模式示例
计算机中有些资源是独占的,同一时间只能被一个线程使用,如打印机、同一个文件的写入等,这类资源叫临界资源,访问它的代码段叫临界区。为了更加安全地使用临界资源,我们每次使用都要对临界资源进行上锁,确保一个线程使用完成释放资源后,其他线程才可以使用。
import threading
classThreadSafeSingleton:
_instance = None
_lock = threading.Lock()
def__new__(cls, *args, **kwargs):
if cls._instance isNone:
with cls._lock:
if cls._instance isNone:
cls._instance = super().__new__(cls)
return cls._instance
如果不加锁会怎样?
# 两个线程同时调用
if cls._instance isNone: # 线程A和线程B同时检查,都通过
cls._instance = ... # 线程A创建了一个
cls._instance = ... # 线程B又创建了一个(覆盖了A的)
这种情况叫竞态条件,结果取决于谁先执行,不可预测。
解决:加锁排队
with cls._lock: # 只有拿到锁的线程能进入,其他线程等待
if cls._instance isNone:
cls._instance = ...
# 出了 with 块,自动释放锁,下一个线程才能进
优化:双重检查
每次都加锁太慢了,加锁本身有开销,所以:
if cls._instance isNone: # 先不加锁,快速判断
with cls._lock: # 确实没有才加锁
if cls._instance isNone: # 加锁后再确认一次
cls._instance = ...
- 第二次检查:防止多个线程同时通过第一次检查后重复创建(安全)
# 线程锁的上下文管理器的实现。上下文管理器见后文。
import threading
# threading.Lock 内部大致实现(简化版)
classLock:
def__enter__(self):
# 获取锁,如果已被占用则阻塞等待
self.acquire()
return self
def__exit__(self, exc_type, exc_val, exc_tb):
# 释放锁
self.release()
# 不抑制异常,返回 False(默认)
returnFalse
工厂模式 Factory Pattern
工厂模式 是一种常用的 创建型设计模式,其核心思想是通过一个专门的工厂方法来创建对象,而不是直接使用类的构造方法来实例化对象。通过使用工厂模式,代码的创建逻辑与对象的使用逻辑分离,减少了直接实例化的复杂性,也使得代码更加灵活,易于扩展和维护。
如果没有工厂模式,所有创建对象的代码都是直接创建的,那么当创建对象的逻辑发生改变了,代码中所有创建对象的地方都要改变,这让代码的修改维护变得非常繁琐。工厂相当于创建对象的一个中间层,由工厂专门来创建对象,所有创建对象的地方都由工厂来创建。当创建对象的逻辑发生改变时,我们只需要修改工厂里的代码,而不是找到所有创建对象的代码,逐一修改。
简单工厂
最直接的实现方式,用一个函数或类封装创建逻辑:
classUser:
def__init__(self, name, role="guest"):
self.name = name
self.role = role
def__repr__(self):
returnf"User({self.name}, {self.role})"
# 简单工厂函数
defcreate_user(user_type, name):
if user_type == "admin":
return User(name, role="admin")
elif user_type == "guest":
return User(name, role="guest")
else:
raise ValueError(f"Unknown user type: {user_type}")
# 使用
admin = create_user("admin", "Alice")
guest = create_user("guest", "Bob")
创建逻辑被封装在 create_user 函数中。如果以后 admin 的创建需要额外参数或初始化逻辑,只需修改工厂函数,调用方代码无需改动。
工厂方法模式
当产品类型较多、扩展频繁时,可以将工厂本身抽象化,让每个产品有对应的工厂:
from abc import ABC, abstractmethod
# 产品接口
classUser(ABC):
@abstractmethod
defget_permissions(self):
pass
classAdminUser(User):
defget_permissions(self):
return ["read", "write", "delete"]
classGuestUser(User):
defget_permissions(self):
return ["read"]
# 工厂接口
classUserFactory(ABC):
@abstractmethod
defcreate(self, name):
pass
classAdminFactory(UserFactory):
defcreate(self, name):
return AdminUser(name)
classGuestFactory(UserFactory):
defcreate(self, name):
return GuestUser(name)
# 使用
defregister_user(factory: UserFactory, name):
user = factory.create(name)
print(f"Created user with permissions: {user.get_permissions()}")
return user
register_user(AdminFactory(), "Alice")
register_user(GuestFactory(), "Bob")
工厂方法模式符合开闭原则——新增用户类型时,只需添加新的产品类和工厂类,无需修改已有代码。
Python 中的实践
Python 作为动态语言,工厂模式的实现可以更简洁:
1.用字典替代 if-else 分支
classAdminUser:
def__init__(self, name):
self.name = name
self.role = "admin"
classGuestUser:
def__init__(self, name):
self.name = name
self.role = "guest"
USER_CLASSES = {
"admin": AdminUser, # 键: 字符串, 值: 类对象
"guest": GuestUser,
}
defcreate_user(user_type, name):
cls = USER_CLASSES.get(user_type)
if cls isNone:
raise ValueError(f"Unknown type: {user_type}")
return cls(name)
# 创建用户
admin = create_user("admin", "张三") # 等价于 AdminUser("张三")
guest = create_user("guest", "李四") # 等价于 GuestUser("李四")
2.用类作为工厂本身
Python 的类是可调用对象,天然具备工厂特性:
classUserFactory:
_registry = {}
@classmethod
defregister(cls, user_type, user_class):
cls._registry[user_type] = user_class
@classmethod
defcreate(cls, user_type, name):
return cls._registry[user_type](name)
# 注册产品
UserFactory.register("admin", AdminUser)
UserFactory.register("guest", GuestUser)
# 使用
user = UserFactory.create("admin", "Alice")
3.必要时再抽象
如果创建逻辑简单、调用点少,一个函数就足够:
defcreate_admin(name):
return User(name, role="admin", level=3, created_at=datetime.now())
不必为了模式而模式。
何时使用工厂模式
工厂模式的本质是封装变化。将对象创建的复杂性隐藏在工厂背后,让业务代码专注于"使用对象"而非"如何创建对象"。在 Python 中,得益于动态特性,工厂模式往往可以用更轻量的方式实现——一个函数、一个字典、或者类本身,不必拘泥于传统面向对象语言的繁琐形式。
代理模式 Proxy Pattern
代理模式是一种结构型设计模式,核心思想是用一个代理对象控制对另一个对象(真实对象)的访问,可以在不修改原对象的情况下增加额外的功能(如延迟加载、访问控制、日志、缓存等)。
classRealSubject:
"""真实对象"""
defrequest(self) -> str:
return"RealSubject: 处理请求"
classProxy:
"""代理对象"""
def__init__(self, real_subject: RealSubject):
self._real_subject = real_subject
defrequest(self) -> str:
# 前置处理
print("Proxy: 检查权限或记录日志...")
# 调用真实对象
result = self._real_subject.request()
# 后置处理
print("Proxy: 请求结束后的清理工作...")
returnf"Proxy 转发结果: {result}"
# 使用
real = RealSubject()
proxy = Proxy(real)
print(proxy.request())
保护代理 Protection Proxy
classSensitiveData:
defaccess_data(self):
return"SensitiveData"
# 代理类
classAccessControlProxy:
def__init__(self, user_role):
self.user_role = user_role
self.real_subject = SensitiveData()
defaccess_data(self):
if self.user_role == "admin":
return self.real_subject.access_data()
else:
return"Access Denied"
# 客户端代码
proxy = AccessControlProxy(user_role="user")
print(proxy.access_data())
proxy_admin = AccessControlProxy(user_role="admin")
print(proxy_admin.access_data())
远程代理 Remote Proxy
common.py :
import pickle # 用于实现对象的序列化
# 真实对象
classRealSubject:
defprocess_data(self, data):
returnf"Processing {data}"
server.py :
import pickle
import socket
from common import RealSubject
# 服务器端
defserver():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('localhost', 8080))
s.listen()
print("Server listening on port 8080...")
conn, addr = s.accept()
with conn:
data = conn.recv(1024)
if data:
data = pickle.loads(data)
result = RealSubject().process_data(data)
conn.sendall(pickle.dumps(result))
if __name__ == "__main__":
server()
remote_proxy.py :
import pickle # 用于实现对象的序列化
import socket
from common import RealSubject
# 远程代理类
classRemoteProxy:
def__init__(self, host, port):
self.host = host
self.port = port
defprocessing_data(self, data):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((self.host, self.port))
s.sendall(pickle.dumps(data))
response = s.recv(1024)
return pickle.loads(response)
client.py :
from remote_proxy import RemoteProxy
# 客户端代码
proxy = RemoteProxy('localhost', 8080)
print(proxy.processing_data("Important data"))
在这个例子中,客户端通过代理与服务器通信,代理负责将数据序列化并发送到远程服务器,服务器接收到数据后进行处理并返回结果。
智能代理 Smart Proxy
# 真实对象
classExpensiveComputation:
defcompute(self, x):
print(f"Computing {x}...")
return x * 2
# 智能代理类
classSmartProxy:
def__init__(self):
self._cache = {}
self._real_subject = ExpensiveComputation()
defcompute(self, x):
if x notin self._cache:
result = self._real_subject.compute(x)
self._cache[x] = result
else:
print(f"Cache hit for {x}")
return self._cache[x]
# 客户端代码
proxy = SmartProxy()
print(proxy.compute(10)) # 计算并缓存
print(proxy.compute(10)) # 从缓存中读取
print(proxy.compute(20)) # 计算并缓存
日志记录代理 Logging Proxy
# 真实对象
classRealSubject:
defperform_action(self):
print("Performing action...")
# 日志代理
classLoggingProxy:
def__init__(self, real_subject):
self.real_subject = real_subject
defperform_action(self):
print("Logging: Action started")
self.real_subject.perform_action()
print("Logging: Action completed")
# 客户端代码
real_subject = RealSubject()
proxy = LoggingProxy(real_subject)
proxy.perform_action()
适配器模式 Adapter Pattern
适配器模式 是一种结构型设计模式,它的作用是使得原本由于接口不兼容而不能一起工作的类能够协同工作。适配器模式通过定义一个中介类(适配器),它把原本不兼容的接口转换为客户端所期望的接口。我们可以把它想象成一个电源插头转换器:插板上只有三孔插座,但是我们的电器是两脚插头,适配器就是连接三孔插座和两脚插头的中间件。
| | |
|---|
| Target(目标接口) | | |
| Adaptee(被适配者) | | |
| Adapter(适配器) | 将 Adaptee 接口转换为 Target 接口 | |
| Client(客户端) | | |
Python 是动态语言类型(鸭子类型), 适配器模式实现起来要比 Java/C++ 更加灵活,甚至不需要显示继承接口。
类适配器(通过继承)
# 目标接口(客户端期望的)
classEuropeanSocket:
"""欧洲标准插座 - 220V"""
defvoltage(self):
return220
defplug_type(self):
return"Type C (两脚圆孔)"
# 被适配者 (现有的、接口不兼容的)
classAmericanAppliance:
"""美国电器 - 需要110V"""
defpower_on(self, voltage):
if voltage == 110:
return"电器正常运行"
else:
returnf"电压 {voltage}V 过高!电器烧坏!"
# 适配器 - 让美国电器能在欧洲插座上使用
classApplianceAdapter:
def__init__(self, appliance: AmericanAppliance, socket: EuropeanSocket):
self.appliance = appliance
self._socket = socket
defuse(self):
# 适配核心: 220V 转换 110V
original_voltage = self._socket.voltage()
adapted_voltage = self._step_down(original_voltage)
return self._appliance.power_on(adapted_voltage)
def_step_down(self, voltage):
return voltage // 2
# 客户端代码
euro_socket = EuropeanSocket()
us_fridge = AmericanAppliance()
adapter = ApplianceAdapter(us_fridge, euro_socket)
print(adapter.use())
对象适配器(通过组合,更常用)
classOldPaymentSystem:
"""旧的支付系统 - 接口不标准"""
defmake_payment(self, amount_cents):
print(f"旧系统:处理 {amount_cents} 分")
classNewPaymentGateway:
"""新支付网关 - 标准接口"""
defpay(self, amount_dollars):
print(f"新网关:支付 ${amount_dollars}")
# 适配器 - 让旧系统适配新接口
classPaymentAdapter:
def__init__(self, old_system: OldPaymentSystem):
self._old_system = old_system
defpay(self, amount_dollars):
# 适配:美元 -> 美分
cents = int(amount_dollars * 100)
self._old_system.make_payment(cents)
# 客户端只认识 pay(amount_dollars) 接口
defcheckout(payment_method):
payment_method.pay(19.99)
# 使用
old = OldPaymentSystem()
adapter = PaymentAdapter(old)
checkout(adapter) # 输出: 旧系统:处理 1999 分
利用 Python 动态特性的通用适配器
classDog:
defbark(self):
return"汪汪"
defrun(self):
return"四条腿跑"
classCat:
defmeow(self):
return"喵喵"
defjump(self):
return"跳得很高"
# 通用适配器:将一个对象的任意方法映射到另一个对象的任意方法
classGenericAdapter:
def__init__(self, obj, **method_mapping):
self._obj = obj
self._mapping = method_mapping
def__getattr__(self, name):
# 如果请求的方法在映射中,则调用原对象的对应方法
if name in self._mapping:
target_method = self._mapping[name]
return getattr(self._obj, target_method)
raise AttributeError(f"{name} 未定义")
# 让猫拥有狗的接口
dog = Dog()
cat = Cat()
# 适配器:让猫可以 bark(实际调用 meow),可以 run(实际调用 jump)
cat_adapter = GenericAdapter(cat, bark="meow", run="jump")
print(cat_adapter.bark()) # 输出: 喵喵
print(cat_adapter.run()) # 输出: 跳得很高
动态类与动态方法
动态添加属性
classPerson:
def__init__(self, name):
self.name = name
# 创建实例
p = Person("张三")
# 动态添加实例属性
p.age = 25
p.email = "JackeySong@jackey.com"
# 动态添加属性
Person.country = "中国"
print(p.age) # 25
print(p.country) # 中国
print(Person.country) # 中国
# 动态添加方法
defgreet(self):
returnf"Hello, my name is {self.name}."
# 方法1: 直接赋值(实例方法)
p.greet = greet.__get__(p) # 手动绑定
print(p.greet())
# 方法2: 使用 types.MethodType
import types
p.say_hello = types.MethodType(lambda self: f"Hello, {self.name}", p)
print(p.say_hello())
# 方法3: 添加到类(所有实例共享)
Person.walk = lambda self: f"{self.name} is walking."
print(p.walk())
使用 __slots__ 限制动态属性
classRestrictedPerson:
__slots__ = ['name', 'age']
def__init__(self, name, age):
self.name = name
self.age = age
p = RestrictedPerson("李四", 30)
try:
p.email = "lisi@example.com"# ❌ 不允许
except AttributeError as e:
print(f"错误: {e}") # 'RestrictedPerson' object has no attribute 'email'
动态创建类
# 方法1: 使用 type() 动态创建类
definit(self, name):
self.name = name
defspeak(self):
returnf"{self.name} is speaking."
# type(类名, 父类元组, 属性字典)
DynamicClass = type('DynamicClass', (object, ), {
'__init__': init,
'speak': speak,
'species': 'Human'
})
obj = DynamicClass("王五")
print(obj.speak())
print(obj.species)
# 方法2: 工厂函数动态生成类
defcreate_model(table_name, fields):
"""动态创建数据库模型类"""
attrs = {
'__tablename__': table_name,
'__init__': lambda self, **kwargs: self.__dict__.update(kwargs)
}
# 或者
# def init(self, **kwargs):
# self.__dict__.update(kwargs)
# attrs = {
# '__tablename__': table_name,
# '__init__': init
# }
# 添加字段
for field_name, field_type in fields.items():
attrs[field_name] = None
# 添加查询方法
defquery(self):
returnf"SELECT * FROM {table_name}"
attrs['query'] = query
return type(f"{table_name.title()}Model", (object, ), attrs)
# 使用
UserModel = create_model('users', {'id': int, 'name': str, 'email': str})
user = UserModel(id=1, name="赵六", email="zhao@example.com")
print(user.query())
print(user.name)
使用元类动态修改类
classAddMethodMeta(type):
"""元类: 自动为类添加方法"""
def__new__(cls, name, bases, attrs):
# 自动添加一个方法
defauto_method(self):
returnf"这是 {name} 类的自动方法"
attrs['auto_method'] = auto_method
# 统计属性数量
attrs['attribute_count'] = len([k for k in attrs ifnot k.startswith('_')])
return super().__new__(cls, name, bases, attrs)
classMyClass(metaclass=AddMethodMeta):
def__init__(self, value):
self.value = value
obj = MyClass(10)
print(obj.auto_method())
print(obj.attribute_count)
理解元类方法调用时机
classMeta(type):
"""元类"""
def__new__(mcls, name, bases, namespace):
"""创建类对象时调用"""
print(f"[元类] __new__: 正在创建类 {name}")
print(f" 参数: mcls={mcls.__name__}, name={name}")
return super().__new__(mcls, name, bases, namespace)
def__init__(cls, name, bases, namespace):
"""类对象初始化时调用"""
print(f"[元类] __init__: 正在初始化类 {name}")
super().__init__(name, bases, namespace)
def__call__(cls, *args, **kwargs):
"""实例化类对象时调用(创建实例)"""
print(f"[元类] __call__: 开始实例化类 {cls.__name__}")
print(f" 传入参数: args={args}, kwargs={kwargs}")
# 1. 调用类的 __new__
print(f" -> 准备调用 {cls.__name__}.__new__")
instance = cls.__new__(cls, *args, **kwargs)
# 2. 如果实例是当前类的实例,调用 __init__
if isinstance(instance, cls):
print(f" -> 准备调用 {cls.__name__}.__init__")
instance.__init__(*args, **kwargs)
print(f"[元类] __call__: 实例创建完成")
return instance
classMyClass(metaclass=Meta):
"""普通类"""
def__new__(cls, *args, **kwargs):
print(f" [类] __new__: 创建实例,cls={cls.__name__}")
print(f" 参数: args={args}, kwargs={kwargs}")
instance = super().__new__(cls)
print(f" 实例创建成功: {instance}")
return instance
def__init__(self, value):
print(f" [类] __init__: 初始化实例,self={self}")
print(f" 参数: value={value}")
self.value = value
# 执行实例化
print("=" * 50)
print("开始执行: obj = MyClass(42)")
print("=" * 50)
obj = MyClass(42)
print(f"\n最终结果: obj.value = {obj.value}")
输出:
[元类] __new__: 正在创建类 MyClass
参数: mcls=Meta, name=MyClass
[元类] __init__: 正在初始化类 MyClass
==================================================
开始执行: obj = MyClass(42)
==================================================
[元类] __call__: 开始实例化类 MyClass
传入参数: args=(42,), kwargs={}
-> 准备调用 MyClass.__new__
[类] __new__: 创建实例,cls=MyClass
参数: args=(42,), kwargs={}
实例创建成功: <__main__.MyClass object at 0x00000194A1749190>
-> 准备调用 MyClass.__init__
[类] __init__: 初始化实例,self=<__main__.MyClass object at 0x00000194A1749190>
参数: value=42
[元类] __call__: 实例创建完成
最终结果: obj.value = 42
元类__new__参数表 :
| | | |
|---|
| mcls | type | | Meta |
| name | str | | "MyClass" |
| bases | tuple | | (object,) |
| namespace | dict | | {'__init__': <function>, 'x': 10} |
classMeta(type):
def__new__(mcls, name, bases, namespace):
print("=" * 50)
print(f"mcls (元类本身): {mcls}")
print(f"mcls.__name__: {mcls.__name__}")
print(f"mcls 类型: {type(mcls)}")
print("-" * 50)
print(f"name (类名): {name}")
print(f"name 类型: {type(name)}")
print("-" * 50)
print(f"bases (父类): {bases}")
print(f"bases 类型: {type(bases)}")
print("-" * 50)
print(f"namespace (属性字典): {namespace}")
print(f"namespace 中的键: {list(namespace.keys())}")
print("=" * 50)
return super().__new__(mcls, name, bases, namespace)
# 定义一个基类
classAnimal:
pass
# 创建使用元数的类
classDog(Animal, metaclass=Meta):
species = "canine"
defbark(self):
return"Woof!"
# 输出会显示所有参数的内容
输出:
==================================================
mcls (元类本身): <class '__main__.Meta'>
mcls.__name__: Meta
mcls 类型: <class 'type'>
--------------------------------------------------
name (类名): Dog
name 类型: <class 'str'>
--------------------------------------------------
bases (父类): (<class '__main__.Animal'>,)
bases 类型: <class 'tuple'>
--------------------------------------------------
namespace (属性字典): {'__module__': '__main__', '__qualname__': 'Dog', 'species': 'canine', 'bark': <function Dog.bark at 0x000002CEED929940>}
namespace 中的键: ['__module__', '__qualname__', 'species', 'bark']
==================================================
内存管理与优化
实例的内存分配机制
# 三层分配器结构
# 1. 对象分配器(PyObjectMalloc)
# 2. Python 对象堆 (Arena)
# 3. 系统堆 (malloc)
import sys
classPerson:
def__init__(self, name, age):
self.name = name
self.age = age
p = Person("Jackey", 24)
print(sys.getsizeof(p)) # 对象头部大小
print(sys.getsizeof(p.__dict__)) # 实例字典大小
输出:
56
296
内存分配流程:
- 小对象 (< 512 bytes) 由 Python 内存池管理
垃圾回收与内存泄漏
常见内存泄漏场景:
# 场景1: 循环引用
classNode:
def__init__(self):
self.next = None
a = Node()
b = Node()
a.next = b
b.next = a # 循环引用,引用计数不为0
# 场景2: 全局容器未清理
cache = []
defadd_to_cache(data):
cache.append(data) # 无限增长
# 场景3: 闭包中的变量
defouter():
large_data = [0] * 10_000_000
definner():
return large_data[0] # 保持引用
return inner
# 场景4: 未关闭的资源
f = open('file.txt')
data = f.read() # 忘记 f.close()
引用计数与垃圾回收机制
import sys
classMyClass:
pass
obj = MyClass()
print(sys.getrefcount(obj)) # 2 (包括临时引用)
# sys.getrefcount() 查看对象的引用计数
# 引用计数变化
obj2 = obj # 引用计数 +1
del obj2 # 引用计数 -1
分代回收机制:
import gc
# 查看分代阈值
print(gc.get_threshold()) # (700, 10, 10)
# 第0代:700个对象触发
# 第1代:10次第0代回收触发
# 第2代:10次第1代回收触发
# 手动触发回收
gc.collect() # 全代回收
gc.collect(1) # 只回收第1代
gc 模块使用:
import gc
import weakref
# 禁用/启用 GC
gc.disable()
gc.enable()
print(gc.isenabled())
# 查看不可达对象
gc.set_debug(gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_LEAK)
classCycle:
def__init__(self):
self.ref = None
# 检测循环引用
a = Cycle()
b = Cycle()
a.ref = b
b.ref = a
# 获取垃圾回收统计
print(gc.get_stats())
# [{'collections': 10, 'collected': 150, 'uncollectable': 0}, ...]
# 使用弱引用避免循环引用
import weakref
classMyClass:
pass
obj = MyClass()
weak_obj = weakref.ref(obj)
print(weak_obj()) # 获取原对象
# 删除原对象后
del obj
print(weak_obj()) # None
# 使用 WeakKeyDictionary 或 WeakValueDictionary
weak_dict = weakref.WeakValueDictionary()
key = 'test'
value = MyClass()
weak_dict[key] = value
print(weak_dict[key]) # 存在
del value
print(weak_dict.get(key)) # None
slots 减少内存开销
import sys
# 传统方式:每个实例都有 __dict__
classTraditional:
def__init__(self, x, y):
self.x = x
self.y = y
# 使用 __slots__
classSlotted:
__slots__ = ('x', 'y')
def__init__(self, x, y):
self.x = x
self.y = y
# 内存对比
t = Traditional(1, 2)
s = Slotted(1, 2)
print(f"Traditional: {sys.getsizeof(t)} + dict: {sys.getsizeof(t.__dict__)}")
# Traditional: 56 + dict: 64
print(f"Slotted: {sys.getsizeof(s)}")
# Slotted: 56 (没有 __dict__)
# 大量对象时差异明显
traditional_list = [Traditional(i, i) for i in range(100000)]
slotted_list = [Slotted(i, i) for i in range(100000)]
# 传统方式约 12MB,__slots__ 约 5.6MB
输出:
Traditional: 56 + dict: 296
Slotted: 48
classSlotted:
__slots__ = ('x', 'y')
def__init__(self, x, y):
self.x = x
self.y = y
s = Slotted(1, 2)
# s.z = 3 # AttributeError: 'Slotted' object has no attribute 'z'
# 继承时的注意事项
classChild(Slotted):
__slots__ = ('z',) # 需要重新定义 __slots__
def__init__(self, x, y, z):
super().__init__(x, y)
self.z = z
# 允许添加 __dict__
classFlexibleSlotted:
__slots__ = ('x', '__dict__') # 包含 __dict__ 允许动态属性
def__init__(self, x):
self.x = x
self.y = 2# 可以动态添加
内存优化最佳实践
# 1. 使用生成器代替列表
defread_large_file(filepath):
with open(filepath) as f:
for line in f: # 逐行读取,不占用大量内存
yield line
# 2. 使用 array 或 numpy 存储数值
from array import array
int_array = array('i', range(1000000)) # 比 list 节省内存
# 3. 使用 __slots__ 优化类实例
classOptimizedPoint:
__slots__ = ('x', 'y', 'z')
def__init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
# 4. 使用 sys.intern() 复用字符串
import sys
str1 = sys.intern('hello')
str2 = sys.intern('hello')
print(str1 is str2) # True
# 5. 手动释放大对象
import gc
large_obj = [0] * 10000000
del large_obj
gc.collect() # 立即释放
# 6. 使用 tracemalloc 监控内存
import tracemalloc
tracemalloc.start()
# 执行代码...
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
for stat in top_stats[:10]:
print(stat)
高级面向对象编程技巧
自定义迭代器
自定义迭代器可以让我们控制如何在自定义对象中进行迭代。
一个迭代器类需要实现 __iter__() 和 __next__() 方法。__iter__() 返回迭代器对象(通常是自己),__next__() 返回当前迭代的值。
classReverse:
def__init__(self, data):
self.data = data
self.index = len(data)
def__iter__(self):
return self
def__next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
# 使用自定义迭代器
rev = Reverse('giraffe')
for char in rev:
print(char, end='')
__iter__() 、__next__() 这两个方法是迭代器协议的核心部分。
__iter__():让对象支持迭代,返回迭代器本身。__next__():返回下一个元素。如果没有更多元素,抛出 StopIteration 异常。
描述符 Descriptors
描述符是 Python 中一种用于定义如何管理类属性的机制。它是通过实现 __get__ 、__set__ 和 __delete__ 等方法来提供属性管理的。
描述符协议:
描述符协议定义了以下方法:
__get__(self, instance, owner):获取属性时被调用。__set__(self, instance, value):设置属性时被调用。__delete__(self, instance):删除属性时被调用。
classDescriptor:
def__get__(self, instance, owner):
return"This is a descriptor value."
def__set__(self, instance, value):
print(f"Setting value: {value}")
def__delete__(self, instance):
print("Deleting attribute")
classMyClass:
attr = Descriptor() # 将描述符赋值给类属性,描述符只能用在类属性上,不能用在实例属性上
obj = MyClass()
print(obj.attr) # __get__ 被调用
obj.attr = 10# __set__ 被调用
del obj.attr # __delete__ 被调用
classPositiveNumber:
def__set_name__(self, owner, name):
self.name = name
def__get__(self, instance, owner):
return instance.__dict__.get(self.name)
def__set__(self, instance, value):
if value <= 0:
raise ValueError(f"{self.name} must be positive.")
instance.__dict__[self.name] = value
classOrder:
quantity = PositiveNumber()
price = PositiveNumber()
def__init__(self, quantity, price):
self.quantity = quantity
self.price = price
order = Order(10, 99.9) # 正常
# order = Order(-5, 99.9) # ValueError: quantity must be positive
描述符优先级
classDescriptor:
def__get__(self, instance, owner):
return"descriptor value"
classTest:
attr = Descriptor()
def__init__(self):
self.attr = "instance attr"# 这个会被忽略!
t = Test()
print(t.attr) # 输出: descriptor value (不是 "instance attr")
访问顺序:
- 数据描述符 (有
__set__ 或 __delete__)
classDataDesc:
def__get__(self, instance, owner):
return"【数据描述符】"
def__set__(self, instance, value):
pass# 即使是空的 __set__ 也让它是数据描述符
classNonDataDesc:
def__get__(self, instance, owner):
return"【非数据描述符】"
classDemo:
data = DataDesc()
non_data = NonDataDesc()
normal = "【普通类属性】"
d = Demo()
# 设置实例属性
d.data = "实例属性"
d.non_data = "实例属性"
d.normal = "实例属性"
print("=" * 50)
print("优先级测试结果:")
print(f"data: {d.data}") # 【数据描述符】 (实例属性被忽略)
print(f"non_data:{d.non_data}") # 实例属性 (覆盖非数据描述符)
print(f"normal: {d.normal}") # 实例属性 (覆盖普通类属性)
print("=" * 50)
描述符的核心价值:
- ORM 框架基础:SQLAlchemy、Django ORM 都大量使用描述符
上下文管理器
上下文管理器是用于处理资源管理的一种方式,最常见的用法是通过 with 语句来管理打开文件、数据库连接、线程锁等。
__enter__ 和 __exit__ 方法:
上下文管理器需要实现 __enter__ 和 __exit__ 方法。
__enter__(self):在进入 with 块时被调用,通常用于资源分配或初始化。__exit__(self, exc_type, exc_val, exc_tb):在退出 with 块时被调用,用于清理资源,处理异常等。
classMyContextManager:
def__enter__(self):
print("Entering the context")
return self # 返回值可以是与资源相关的对象
def__exit__(self, exc_type, exc_val, exc_tb):
print("Exiting the context")
if exc_type:
print(f"An exception occurred: {exc_val}")
returnTrue# 如果返回 True,则抑制异常
# 使用上下文管理器
with MyContextManager() as manager:
print("Inside the context")
# raise ValueError("Something went wrong")
内省 Introspection
内省是指程序在运行时能够检查对象的类型或其属性。Python 提供了一些内省相关的函数来动态操作类对象。
getattr(object, name):获取对象的属性。setattr(object, name, value):设置对象的属性。hasattr(object, name):检查对象是否有某个属性。
classMyClass:
def__init__(self):
self.name = "Python"
self.age = 30
# 使用内省操作
obj = MyClass()
print(getattr(obj, 'name')) # 获取属性
setattr(obj, 'name', 'Python 3') # 设置属性
print(obj.name)
print(hasattr(obj, 'name')) # 检查属性是否存在
END.