Skip to content

Python 面向对象编程(OOP)

面向对象编程是一种以对象为核心的编程范式,通过来定义对象的属性和行为。

1. 类与对象

1.1 基本概念

  • 类(Class):对象的模板/蓝图,定义了属性和方法
  • 对象(Object):类的实例,拥有具体的数据
python
# 定义一个最简单的类
class Dog:
    pass

# 创建对象(实例化)
d1 = Dog()
d2 = Dog()

print(type(d1))      # <class '__main__.Dog'>
print(isinstance(d1, Dog))  # True
print(d1 is d2)      # False  (两个不同的对象)

1.2 __init__ 与实例属性

__init__初始化方法,在创建对象时自动调用。self 代表当前实例本身。

python
class Dog:
    def __init__(self, name, age):
        self.name = name    # 实例属性
        self.age = age

    def bark(self):         # 实例方法
        print(f"{self.name} 说:汪汪!")

    def info(self):
        print(f"名字:{self.name},年龄:{self.age}岁")

# 创建对象
dog1 = Dog("旺财", 3)
dog2 = Dog("小白", 1)

dog1.bark()    # 旺财 说:汪汪!
dog2.bark()    # 小白 说:汪汪!
dog1.info()    # 名字:旺财,年龄:3岁

# 访问和修改属性
print(dog1.name)   # 旺财
dog1.age = 4
dog1.info()        # 名字:旺财,年龄:4岁

# 动态添加属性(仅对当前实例生效)
dog1.color = "黄色"
print(dog1.color)  # 黄色
# print(dog2.color)  # ❌ AttributeError

1.3 类属性 vs 实例属性

python
class Cat:
    species = "猫科动物"   # 类属性,所有实例共享
    count = 0

    def __init__(self, name):
        self.name = name   # 实例属性,每个实例独有
        Cat.count += 1     # 通过类名修改类属性

c1 = Cat("小花")
c2 = Cat("小黑")

# 类属性通过类或实例都能访问
print(Cat.species)    # 猫科动物
print(c1.species)     # 猫科动物
print(c2.species)     # 猫科动物
print(Cat.count)      # 2

# ⚠️ 通过实例赋值会创建同名实例属性,不会修改类属性
c1.species = "猫猫"
print(c1.species)     # 猫猫   (实例属性,遮蔽了类属性)
print(c2.species)     # 猫科动物(类属性不受影响)
print(Cat.species)    # 猫科动物(类属性不受影响)

2. 方法类型

2.1 实例方法、类方法、静态方法

python
class MyClass:
    class_var = "我是类变量"

    def __init__(self, value):
        self.value = value

    # 实例方法:第一个参数是 self(实例)
    def instance_method(self):
        print(f"实例方法,访问实例属性:{self.value}")
        print(f"实例方法,也能访问类属性:{MyClass.class_var}")

    # 类方法:第一个参数是 cls(类本身)
    @classmethod
    def class_method(cls):
        print(f"类方法,访问类属性:{cls.class_var}")
        # print(cls.value)  # ❌ 不能访问实例属性

    # 静态方法:没有 self 也没有 cls,和普通函数一样
    @staticmethod
    def static_method(x, y):
        print(f"静态方法,计算结果:{x + y}")

obj = MyClass(42)

# 实例方法:通过实例调用
obj.instance_method()
# 实例方法,访问实例属性:42
# 实例方法,也能访问类属性:我是类变量

# 类方法:通过类或实例调用
MyClass.class_method()   # 类方法,访问类属性:我是类变量
obj.class_method()       # 类方法,访问类属性:我是类变量

# 静态方法:通过类或实例调用
MyClass.static_method(3, 5)  # 静态方法,计算结果:8
obj.static_method(3, 5)      # 静态方法,计算结果:8

2.2 类方法的常见用途 —— 替代构造函数

python
class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    def __str__(self):
        return f"{self.year}-{self.month:02d}-{self.day:02d}"

    @classmethod
    def from_string(cls, date_str):
        """从字符串创建日期:'2024-01-15'"""
        year, month, day = map(int, date_str.split("-"))
        return cls(year, month, day)

    @classmethod
    def today(cls):
        """创建今天的日期"""
        import datetime
        t = datetime.date.today()
        return cls(t.year, t.month, t.day)

# 不同方式创建对象
d1 = Date(2024, 1, 15)
d2 = Date.from_string("2024-06-20")
d3 = Date.today()

print(d1)  # 2024-01-15
print(d2)  # 2024-06-20
print(d3)  # 2026-03-17(当天日期)

3. 封装

3.1 访问控制约定

Python 没有真正的私有,而是通过命名约定来表示访问级别。

python
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner        # 公开属性
        self._type = "储蓄"       # 受保护属性(约定,不强制)
        self.__balance = balance  # 私有属性(名称改写)

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print(f"存入 {amount},余额:{self.__balance}")

    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            print(f"取出 {amount},余额:{self.__balance}")
        else:
            print("余额不足或金额无效")

    def get_balance(self):
        return self.__balance

acc = BankAccount("Alice", 1000)

# 公开属性:随意访问
print(acc.owner)       # Alice

# 受保护属性:可以访问,但约定不建议外部使用
print(acc._type)       # 储蓄

# 私有属性:不能直接访问
# print(acc.__balance)   # ❌ AttributeError

# 通过公开方法访问
print(acc.get_balance())   # 1000
acc.deposit(500)           # 存入 500,余额:1500
acc.withdraw(200)          # 取出 200,余额:1300

# ⚠️ 其实可以通过名称改写访问(但强烈不推荐)
print(acc._BankAccount__balance)  # 1300

3.2 @property 属性装饰器

将方法伪装成属性,实现受控的读写访问。

python
class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        """获取半径"""
        return self._radius

    @radius.setter
    def radius(self, value):
        """设置半径(带校验)"""
        if value <= 0:
            raise ValueError("半径必须大于 0")
        self._radius = value

    @property
    def area(self):
        """面积(只读属性,没有 setter)"""
        import math
        return math.pi * self._radius ** 2

    @property
    def perimeter(self):
        """周长(只读)"""
        import math
        return 2 * math.pi * self._radius

c = Circle(5)

# 像访问属性一样使用(不需要加括号)
print(c.radius)          # 5
print(f"{c.area:.2f}")       # 78.54
print(f"{c.perimeter:.2f}")  # 31.42

# 设置属性(触发 setter 校验)
c.radius = 10
print(c.radius)          # 10
print(f"{c.area:.2f}")       # 314.16

# 校验生效
try:
    c.radius = -1
except ValueError as e:
    print(e)  # 半径必须大于 0

# 只读属性不能赋值
# c.area = 100  # ❌ AttributeError: property 'area' has no setter

4. 继承

4.1 基本继承

python
# 父类(基类)
class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def speak(self):
        print(f"{self.name} 发出了声音")

    def info(self):
        print(f"[{self.__class__.__name__}] {self.name}{self.age}岁")

# 子类(派生类)
class Dog(Animal):
    def __init__(self, name, age, breed):
        super().__init__(name, age)  # 调用父类的 __init__
        self.breed = breed

    def speak(self):               # 方法重写(Override)
        print(f"{self.name} 说:汪汪!")

    def fetch(self):               # 子类独有的方法
        print(f"{self.name} 在捡球")

class Cat(Animal):
    def speak(self):
        print(f"{self.name} 说:喵喵~")

# 使用
dog = Dog("旺财", 3, "柴犬")
cat = Cat("小花", 2)

dog.speak()    # 旺财 说:汪汪!
cat.speak()    # 小花 说:喵喵~

dog.info()     # [Dog] 旺财,3岁
cat.info()     # [Cat] 小花,2岁

dog.fetch()    # 旺财 在捡球
# cat.fetch()  # ❌ AttributeError(Cat 没有 fetch 方法)

# 继承关系判断
print(isinstance(dog, Dog))      # True
print(isinstance(dog, Animal))   # True  (Dog 是 Animal 的子类)
print(isinstance(cat, Dog))      # False

print(issubclass(Dog, Animal))   # True
print(issubclass(Cat, Animal))   # True
print(issubclass(Dog, Cat))      # False

4.2 多态

同一个方法在不同子类中有不同的行为。

python
class Shape:
    def area(self):
        raise NotImplementedError("子类必须实现 area 方法")

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        import math
        return math.pi * self.radius ** 2

class Triangle(Shape):
    def __init__(self, base, height):
        self.base = base
        self.height = height

    def area(self):
        return 0.5 * self.base * self.height

# 多态:同一个函数处理不同类型的对象
def print_area(shape: Shape):
    print(f"{shape.__class__.__name__} 的面积:{shape.area():.2f}")

shapes = [Rectangle(4, 5), Circle(3), Triangle(6, 8)]
for s in shapes:
    print_area(s)
# Rectangle 的面积:20.00
# Circle 的面积:28.27
# Triangle 的面积:24.00

4.3 多继承与 MRO

python
class A:
    def greet(self):
        print("来自 A")

class B(A):
    def greet(self):
        print("来自 B")

class C(A):
    def greet(self):
        print("来自 C")

class D(B, C):   # 多继承
    pass

d = D()
d.greet()  # 来自 B

# MRO(方法解析顺序):决定多继承时方法的查找顺序
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)

# Python 使用 C3 线性化算法,顺序:D → B → C → A → object

# super() 在多继承中沿 MRO 顺序调用
class A:
    def __init__(self):
        print("A.__init__")

class B(A):
    def __init__(self):
        print("B.__init__")
        super().__init__()  # 沿 MRO 调用下一个

class C(A):
    def __init__(self):
        print("C.__init__")
        super().__init__()

class D(B, C):
    def __init__(self):
        print("D.__init__")
        super().__init__()

D()
# D.__init__
# B.__init__
# C.__init__
# A.__init__

4.4 Mixin 模式

Mixin 是一种通过多继承为类添加功能的设计模式。

python
# Mixin 类:提供可复用的功能
class JsonMixin:
    def to_json(self):
        import json
        return json.dumps(self.__dict__, ensure_ascii=False)

class PrintMixin:
    def print_info(self):
        attrs = ", ".join(f"{k}={v}" for k, v in self.__dict__.items())
        print(f"{self.__class__.__name__}({attrs})")

# 通过多继承组合功能
class User(JsonMixin, PrintMixin):
    def __init__(self, name, age):
        self.name = name
        self.age = age

class Product(JsonMixin, PrintMixin):
    def __init__(self, title, price):
        self.title = title
        self.price = price

user = User("Alice", 25)
user.print_info()           # User(name=Alice, age=25)
print(user.to_json())       # {"name": "Alice", "age": 25}

product = Product("Python书", 59.9)
product.print_info()        # Product(title=Python书, price=59.9)
print(product.to_json())    # {"title": "Python书", "price": 59.9}

5. 魔术方法(双下方法)

魔术方法以双下划线开头和结尾(__xxx__),让自定义类支持 Python 的内置操作。

5.1 字符串表示

python
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        """print() 和 str() 调用,面向用户"""
        return f"({self.x}, {self.y})"

    def __repr__(self):
        """repr() 调用,面向开发者,应能重建对象"""
        return f"Point({self.x}, {self.y})"

p = Point(3, 4)
print(p)        # (3, 4)        ← __str__
print(repr(p))  # Point(3, 4)   ← __repr__
print(f"坐标是 {p}")  # 坐标是 (3, 4)  ← f-string 调用 __str__

# 在列表中展示时用 __repr__
points = [Point(1, 2), Point(3, 4)]
print(points)   # [Point(1, 2), Point(3, 4)]  ← 列表中用 __repr__

5.2 比较运算

python
class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def __repr__(self):
        return f"Student({self.name!r}, {self.score})"

    def __eq__(self, other):
        if not isinstance(other, Student):
            return NotImplemented
        return self.score == other.score

    def __lt__(self, other):
        if not isinstance(other, Student):
            return NotImplemented
        return self.score < other.score

    def __le__(self, other):
        return self == other or self < other

s1 = Student("Alice", 90)
s2 = Student("Bob", 85)
s3 = Student("Charlie", 90)

print(s1 == s3)   # True   (分数相同)
print(s1 == s2)   # False
print(s2 < s1)    # True   (85 < 90)
print(s1 <= s3)   # True

# 支持排序
students = [s1, s2, s3]
students.sort()
print(students)   # [Student('Bob', 85), Student('Alice', 90), Student('Charlie', 90)]

# 💡 用 functools.total_ordering 可以少写比较方法
from functools import total_ordering

@total_ordering
class Score:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        return self.value == other.value

    def __lt__(self, other):
        return self.value < other.value

    # 自动生成 __le__、__gt__、__ge__

5.3 算术运算

python
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

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

    def __add__(self, other):
        """v1 + v2"""
        return Vector(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        """v1 - v2"""
        return Vector(self.x - other.x, self.y - other.y)

    def __mul__(self, scalar):
        """v * 数字"""
        return Vector(self.x * scalar, self.y * scalar)

    def __rmul__(self, scalar):
        """数字 * v(反向乘法)"""
        return self.__mul__(scalar)

    def __abs__(self):
        """abs(v) 返回向量的模"""
        return (self.x ** 2 + self.y ** 2) ** 0.5

    def __bool__(self):
        """bool(v) 判断是否为零向量"""
        return self.x != 0 or self.y != 0

    def __neg__(self):
        """-v"""
        return Vector(-self.x, -self.y)

v1 = Vector(3, 4)
v2 = Vector(1, 2)

print(v1 + v2)      # Vector(4, 6)
print(v1 - v2)      # Vector(2, 2)
print(v1 * 3)       # Vector(9, 12)
print(2 * v1)       # Vector(6, 8)    ← __rmul__
print(abs(v1))       # 5.0
print(-v1)           # Vector(-3, -4)
print(bool(v1))      # True
print(bool(Vector(0, 0)))  # False

5.4 容器协议

让自定义类像列表/字典一样工作。

python
class Playlist:
    def __init__(self, name, songs=None):
        self.name = name
        self._songs = list(songs) if songs else []

    def __len__(self):
        """len(playlist)"""
        return len(self._songs)

    def __getitem__(self, index):
        """playlist[i] 或 playlist[start:end]"""
        return self._songs[index]

    def __setitem__(self, index, value):
        """playlist[i] = value"""
        self._songs[index] = value

    def __delitem__(self, index):
        """del playlist[i]"""
        del self._songs[index]

    def __contains__(self, item):
        """item in playlist"""
        return item in self._songs

    def __iter__(self):
        """for song in playlist"""
        return iter(self._songs)

    def __repr__(self):
        return f"Playlist({self.name!r}, {self._songs})"

pl = Playlist("我的歌单", ["稻香", "晴天", "夜曲"])

# 长度
print(len(pl))       # 3

# 索引访问
print(pl[0])         # 稻香
print(pl[-1])        # 夜曲
print(pl[0:2])       # ['稻香', '晴天']

# 修改
pl[1] = "七里香"
print(pl)  # Playlist('我的歌单', ['稻香', '七里香', '夜曲'])

# 删除
del pl[0]
print(pl)  # Playlist('我的歌单', ['七里香', '夜曲'])

# 成员判断
print("夜曲" in pl)   # True
print("晴天" in pl)   # False

# 遍历
for song in pl:
    print(f"♪ {song}")
# ♪ 七里香
# ♪ 夜曲

5.5 可调用对象

python
class Adder:
    def __init__(self, n):
        self.n = n

    def __call__(self, x):
        """让实例可以像函数一样调用"""
        return self.n + x

add5 = Adder(5)
add10 = Adder(10)

# 像调用函数一样使用
print(add5(3))     # 8
print(add10(3))    # 13
print(callable(add5))  # True

# 实用示例:带状态的计数器
class Counter:
    def __init__(self):
        self.count = 0

    def __call__(self):
        self.count += 1
        return self.count

c = Counter()
print(c())  # 1
print(c())  # 2
print(c())  # 3
print(c.count)  # 3

5.6 上下文管理器

python
class FileManager:
    def __init__(self, filename, mode="r"):
        self.filename = filename
        self.mode = mode
        self.file = None

    def __enter__(self):
        """进入 with 块时调用"""
        print(f"打开文件:{self.filename}")
        self.file = open(self.filename, self.mode)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        """离开 with 块时调用(无论是否异常)"""
        print(f"关闭文件:{self.filename}")
        if self.file:
            self.file.close()
        return False  # False 表示不抑制异常

# 使用
with FileManager("test.txt", "w") as f:
    f.write("Hello, Python!")
# 打开文件:test.txt
# 关闭文件:test.txt

# 实用示例:计时器
import time

class Timer:
    def __enter__(self):
        self.start = time.time()
        return self

    def __exit__(self, *args):
        self.elapsed = time.time() - self.start
        print(f"耗时:{self.elapsed:.4f}秒")

with Timer():
    total = sum(range(1_000_000))
# 耗时:0.0234秒

5.7 常用魔术方法总结

方法触发方式说明
__init__(self, ...)MyClass()初始化
__str__(self)print(obj) / str(obj)用户友好的字符串
__repr__(self)repr(obj)开发者友好的字符串
__len__(self)len(obj)长度
__getitem__(self, key)obj[key]索引访问
__setitem__(self, key, val)obj[key] = val索引赋值
__delitem__(self, key)del obj[key]索引删除
__contains__(self, item)item in obj成员判断
__iter__(self)for x in obj迭代
__next__(self)next(obj)下一个元素
__call__(self, ...)obj()像函数一样调用
__eq__(self, other)obj == other等于
__lt__(self, other)obj < other小于
__add__(self, other)obj + other加法
__mul__(self, other)obj * other乘法
__enter__ / __exit__with obj上下文管理
__hash__(self)hash(obj)哈希值
__bool__(self)bool(obj)布尔转换

6. 抽象基类(ABC)

抽象基类定义接口规范,强制子类实现指定方法。

python
from abc import ABC, abstractmethod

class Animal(ABC):
    def __init__(self, name):
        self.name = name

    @abstractmethod
    def speak(self):
        """子类必须实现此方法"""
        pass

    @abstractmethod
    def move(self):
        pass

    def sleep(self):
        """普通方法,子类可以直接使用"""
        print(f"{self.name} 正在睡觉 💤")

# 抽象类不能实例化
# a = Animal("test")  # ❌ TypeError: Can't instantiate abstract class

class Dog(Animal):
    def speak(self):
        print(f"{self.name}:汪汪!")

    def move(self):
        print(f"{self.name} 在跑")

class Fish(Animal):
    def speak(self):
        print(f"{self.name}:...(鱼不会说话)")

    def move(self):
        print(f"{self.name} 在游泳")

dog = Dog("旺财")
fish = Fish("尼莫")

dog.speak()    # 旺财:汪汪!
dog.move()     # 旺财 在跑
dog.sleep()    # 旺财 正在睡觉 💤

fish.speak()   # 尼莫:...(鱼不会说话)
fish.move()    # 尼莫 在游泳

# 如果子类没有实现抽象方法,实例化时报错
# class Bird(Animal):
#     def speak(self):
#         print("叽叽")
#     # 没有实现 move()
# Bird("小鸟")  # ❌ TypeError: Can't instantiate abstract class Bird

7. 数据类(dataclass)

@dataclass 自动生成 __init____repr____eq__ 等方法,减少样板代码。

python
from dataclasses import dataclass, field

# 基本用法
@dataclass
class Point:
    x: float
    y: float

p1 = Point(3, 4)
p2 = Point(3, 4)
p3 = Point(1, 2)

print(p1)          # Point(x=3, y=4)       ← 自动生成 __repr__
print(p1 == p2)    # True                  ← 自动生成 __eq__
print(p1 == p3)    # False

# 带默认值
@dataclass
class User:
    name: str
    age: int = 0
    email: str = ""
    tags: list = field(default_factory=list)  # 可变默认值用 field

u = User("Alice", 25)
print(u)  # User(name='Alice', age=25, email='', tags=[])

u.tags.append("python")
print(u.tags)  # ['python']

# 冻结的数据类(不可变)
@dataclass(frozen=True)
class Color:
    r: int
    g: int
    b: int

red = Color(255, 0, 0)
print(red)      # Color(r=255, g=0, b=0)
# red.r = 100   # ❌ FrozenInstanceError

# frozen 的数据类可以作为字典的键
colors = {Color(255, 0, 0): "红色", Color(0, 255, 0): "绿色"}
print(colors[Color(255, 0, 0)])  # 红色

# 排序支持
@dataclass(order=True)
class Student:
    score: float
    name: str = ""

students = [Student(85, "Bob"), Student(92, "Alice"), Student(85, "Charlie")]
students.sort()
for s in students:
    print(s)
# Student(score=85, name='Bob')
# Student(score=85, name='Charlie')
# Student(score=92, name='Alice')

# __post_init__ 后处理
@dataclass
class Rectangle:
    width: float
    height: float
    area: float = field(init=False)  # 不在 __init__ 参数中

    def __post_init__(self):
        self.area = self.width * self.height

r = Rectangle(4, 5)
print(r)       # Rectangle(width=4, height=5, area=20)
print(r.area)  # 20

8. 枚举类(Enum)

用于定义一组有名字的常量。

python
from enum import Enum, auto

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

# 访问枚举成员
print(Color.RED)         # Color.RED
print(Color.RED.name)    # RED
print(Color.RED.value)   # 1
print(repr(Color.RED))   # <Color.RED: 1>

# 通过值或名称获取
print(Color(1))           # Color.RED
print(Color["GREEN"])     # Color.GREEN

# 比较
print(Color.RED == Color.RED)    # True
print(Color.RED == Color.BLUE)   # False
print(Color.RED is Color.RED)    # True
# print(Color.RED < Color.BLUE)  # ❌ TypeError(Enum 不支持大小比较)

# 遍历
for color in Color:
    print(f"{color.name} = {color.value}")
# RED = 1
# GREEN = 2
# BLUE = 3

# auto() 自动赋值
class Direction(Enum):
    NORTH = auto()
    SOUTH = auto()
    EAST = auto()
    WEST = auto()

print(list(Direction))
# [<Direction.NORTH: 1>, <Direction.SOUTH: 2>, <Direction.EAST: 3>, <Direction.WEST: 4>]

# 实际应用
class Status(Enum):
    PENDING = "pending"
    RUNNING = "running"
    SUCCESS = "success"
    FAILED = "failed"

def handle_task(status: Status):
    match status:
        case Status.PENDING:
            print("等待中...")
        case Status.RUNNING:
            print("运行中...")
        case Status.SUCCESS:
            print("已完成!")
        case Status.FAILED:
            print("失败!")

handle_task(Status.SUCCESS)  # 已完成!

9. __slots__ 优化

默认情况下,Python 对象使用 __dict__ 存储属性,灵活但占用更多内存。使用 __slots__ 可以限制属性并节省内存。

python
class PointDict:
    """普通类,使用 __dict__"""
    def __init__(self, x, y):
        self.x = x
        self.y = y

class PointSlots:
    """使用 __slots__ 优化"""
    __slots__ = ("x", "y")

    def __init__(self, x, y):
        self.x = x
        self.y = y

p1 = PointDict(1, 2)
p2 = PointSlots(1, 2)

# 功能相同
print(p1.x, p1.y)  # 1 2
print(p2.x, p2.y)  # 1 2

# __dict__ 存在与否
print(p1.__dict__)   # {'x': 1, 'y': 2}
# print(p2.__dict__)  # ❌ AttributeError

# __slots__ 不允许动态添加属性
p1.z = 3      # ✅ 普通类可以
# p2.z = 3    # ❌ AttributeError: 'PointSlots' object has no attribute 'z'

# 内存对比
import sys
print(sys.getsizeof(p1.__dict__))  # 104(__dict__ 本身的开销)
# PointSlots 没有 __dict__,更省内存
# 当创建大量对象时(如 100 万个),差异非常明显

10. 综合示例

示例 1:简易购物车

python
@dataclass
class Product:
    name: str
    price: float

class ShoppingCart:
    def __init__(self):
        self._items: list[tuple[Product, int]] = []

    def add(self, product: Product, qty: int = 1):
        for i, (p, q) in enumerate(self._items):
            if p.name == product.name:
                self._items[i] = (p, q + qty)
                return
        self._items.append((product, qty))

    def total(self) -> float:
        return sum(p.price * q for p, q in self._items)

    def __len__(self):
        return sum(q for _, q in self._items)

    def __str__(self):
        lines = [f"  {p.name} × {q} = ¥{p.price * q:.2f}"
                 for p, q in self._items]
        return "购物车:\n" + "\n".join(lines) + f"\n  合计:¥{self.total():.2f}"

cart = ShoppingCart()
cart.add(Product("Python书", 59.9), 2)
cart.add(Product("键盘", 299.0))
cart.add(Product("鼠标", 79.0))

print(cart)
# 购物车:
#   Python书 × 2 = ¥119.80
#   键盘 × 1 = ¥299.00
#   鼠标 × 1 = ¥79.00
#   合计:¥497.80

print(f"共 {len(cart)} 件商品")  # 共 4 件商品

示例 2:链式调用(Builder 模式)

python
class QueryBuilder:
    def __init__(self, table):
        self._table = table
        self._conditions = []
        self._order = None
        self._limit = None

    def where(self, condition):
        self._conditions.append(condition)
        return self  # 返回 self 实现链式调用

    def order_by(self, field, desc=False):
        direction = "DESC" if desc else "ASC"
        self._order = f"{field} {direction}"
        return self

    def limit(self, n):
        self._limit = n
        return self

    def build(self):
        sql = f"SELECT * FROM {self._table}"
        if self._conditions:
            sql += " WHERE " + " AND ".join(self._conditions)
        if self._order:
            sql += f" ORDER BY {self._order}"
        if self._limit:
            sql += f" LIMIT {self._limit}"
        return sql

# 链式调用
sql = (QueryBuilder("users")
       .where("age > 18")
       .where("status = 'active'")
       .order_by("created_at", desc=True)
       .limit(10)
       .build())

print(sql)
# SELECT * FROM users WHERE age > 18 AND status = 'active' ORDER BY created_at DESC LIMIT 10

示例 3:观察者模式(事件系统)

python
class EventEmitter:
    def __init__(self):
        self._listeners: dict[str, list] = {}

    def on(self, event: str, callback):
        """注册监听器"""
        self._listeners.setdefault(event, []).append(callback)

    def emit(self, event: str, *args, **kwargs):
        """触发事件"""
        for callback in self._listeners.get(event, []):
            callback(*args, **kwargs)

# 使用
emitter = EventEmitter()

# 注册事件监听
emitter.on("login", lambda user: print(f"[日志] {user} 登录了"))
emitter.on("login", lambda user: print(f"[通知] 欢迎回来,{user}!"))
emitter.on("error", lambda msg: print(f"[错误] {msg}"))

# 触发事件
emitter.emit("login", "Alice")
# [日志] Alice 登录了
# [通知] 欢迎回来,Alice!

emitter.emit("error", "数据库连接失败")
# [错误] 数据库连接失败

11. 总结

概念说明关键字/语法
类与对象类是模板,对象是实例classself__init__
实例方法操作实例数据def method(self)
类方法操作类级别数据@classmethodcls
静态方法与类相关的工具函数@staticmethod
封装控制属性访问___@property
继承复用父类代码class Child(Parent)super()
多态同名方法不同行为方法重写
抽象基类定义接口规范ABC@abstractmethod
魔术方法支持内置操作__str____add____len__
数据类减少样板代码@dataclass
枚举类定义命名常量Enumauto()
__slots__限制属性,节省内存__slots__ = (...)

OOP 设计原则

python
# 1. 单一职责:一个类只负责一件事
# 2. 开闭原则:对扩展开放,对修改关闭(用继承/多态扩展,不改已有代码)
# 3. 里氏替换:子类对象能替换父类对象使用
# 4. 组合优于继承:优先用"有一个"(组合)而非"是一个"(继承)

# ✅ 组合
class Engine:
    def start(self):
        print("引擎启动")

class Car:
    def __init__(self):
        self.engine = Engine()  # Car "有一个" Engine

    def start(self):
        self.engine.start()
        print("汽车出发")

car = Car()
car.start()
# 引擎启动
# 汽车出发

# ❌ 不好的继承(Car 不应该 "是一个" Engine)
# class Car(Engine):
#     pass

Released under the MIT License.