
文章目录
-
- [📖 目录导航](#📖 目录导航)
- [前言:为何精通面向对象对 Pythonista 至关重要](#前言:为何精通面向对象对 Pythonista 至关重要)
- [Ch10 类与对象:奠定坚实的"壳、魂、根"](#Ch10 类与对象:奠定坚实的"壳、魂、根")
-
- [1. `class` 的本质:模板与对象的二重性](#1.
class
的本质:模板与对象的二重性) - [2. 对象的诞生:`new` 与 `init` 的协奏曲](#2. 对象的诞生:
__new__
与__init__
的协奏曲) - [3. `self` 的奥秘:从函数到绑定方法](#3.
self
的奥秘:从函数到绑定方法) - [4. 属性的三级查找链](#4. 属性的三级查找链)
- [5. 调试友好双剑客:`str` 与 `repr`](#5. 调试友好双剑客:
__str__
与__repr__
) - [6. "私有"属性:君子协定与名字重整](#6. "私有"属性:君子协定与名字重整)
- [1. `class` 的本质:模板与对象的二重性](#1.
- [Ch11 三大特性一步到位](#Ch11 三大特性一步到位)
-
- [1. 继承与 `super()`:驾驭 MRO](#1. 继承与
super()
:驾驭 MRO) - [2. 多态与鸭子类型:行为决定一切](#2. 多态与鸭子类型:行为决定一切)
- [3. 封装与 `@property`:优雅的访问控制](#3. 封装与
@property
:优雅的访问控制)
- [1. 继承与 `super()`:驾驭 MRO](#1. 继承与
- [Ch12 魔法工具箱:让你的类更强大](#Ch12 魔法工具箱:让你的类更强大)
-
- [1. 运算符重载:让对象表现得像内置类型](#1. 运算符重载:让对象表现得像内置类型)
- [2. `@dataclass`:一行顶十行的代码生成器](#2.
@dataclass
:一行顶十行的代码生成器) - [3. 枚举与类型提示:提升代码的健壮性与可读性](#3. 枚举与类型提示:提升代码的健壮性与可读性)
- [4. 极简设计模式](#4. 极简设计模式)
- [总结:Python OOP 速记口诀(精解版)](#总结:Python OOP 速记口诀(精解版))
-
-
[🔍 详细解读](#🔍 详细解读)
-
[📊 学习进度追踪](#📊 学习进度追踪)
╔══════════════════════════════════════════════════════════════╗
║ 🐍 Python OOP 魔法世界 ║
║ ║
║ ┌─────────┐ ┌─────────┐ ┌─────────┐ ║
║ │ 类与 │───▶│ 三大 │───▶│ 魔法 │ ║
║ │ 对象 │ │ 特性 │ │ 工具箱 │ ║
║ └─────────┘ └─────────┘ └─────────┘ ║
║ │ │ │ ║
║ ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ ║
║ │ 壳·魂·根 │ │继承·多态·│ │运算符重载│ ║
║ │ │ │ 封装 │ │@dataclass│ ║
║ └───────────┘ └───────────┘ └───────────┘ ║
╚══════════════════════════════════════════════════════════════╝
-
📖 目录导航
- [前言:为何精通面向对象对 Pythonista 至关重要](#前言:为何精通面向对象对 Pythonista 至关重要)
- [Ch10 类与对象:奠定坚实的"壳、魂、根"](#Ch10 类与对象:奠定坚实的"壳、魂、根")
- [Ch11 三大特性一步到位](#Ch11 三大特性一步到位)
- [Ch12 魔法工具箱:让你的类更强大](#Ch12 魔法工具箱:让你的类更强大)
- [总结:Python OOP 速记口诀](#总结:Python OOP 速记口诀)
前言:为何精通面向对象对 Pythonista 至关重要
面向对象编程(Object-Oriented Programming, OOP)是现代软件开发的基石之一。在 Python 中,OOP 不仅仅是一种编程范式,它是语言设计的核心哲学------"一切皆对象"。
Python 世界 整数 int 字符串 str 函数 function 模块 module 类 class 对象实例
本指南将带你深入探索 Python 的面向对象世界,从"类与对象"的基础概念,到"继承、多态、封装"三大支柱,再到利用"魔术方法"和高级工具施展 Python 独有的编程魔法。
💡 学习目标:无论你是希望巩固基础,还是渴望突破进阶,本文都将为你提供一份清晰、全面且可直接应用的实战地图。
Ch10 类与对象:奠定坚实的"壳、魂、根"
类的本质架构图
┌─────────────────────────────────────────┐
│ type (元类) │
│ ┌─────────────────────────────────────┐│
│ │ class 定义 ││
│ │ ┌─────────────────────────────────┐││
│ │ │ __dict__ 属性字典 │││
│ │ │ ┌─────────────────────────────┐│││
│ │ │ │ 方法 & 类变量 ││││
│ │ │ └─────────────────────────────┘│││
│ │ └─────────────────────────────────┘││
│ └─────────────────────────────────────┘│
└─────────────────────────────────────────┘
1. class
的本质:模板与对象的二重性
在 Python 中,class
关键字定义了一个"模板"或"蓝图",用于创建具有特定属性和方法的对象。但更深层次地,类本身也是一个对象 ,它是 type
这个元类(metaclass)的实例。
python
class Cookie:
# 类体代码在 import 时执行一次
print("正在烘焙 Cookie 模板...")
shape = "圆形" # 这是一个类属性
def bake(self):
# 这是一个方法
print(f"正在烘烤一个{self.shape}的饼干。")
# "Cookie" 不仅是一个类,也是一个可以被引用的对象
print(Cookie)
# <class '__main__.Cookie'>
# 我们可以检查它的类型
print(type(Cookie))
# <class 'type'>
💡 深入理解:类的创建过程
当你定义一个类时,Python 解释器会:
- 执行类体中的代码
- 将所有产生的局部变量存储在字典中
- 这个字典最终成为类的
__dict__
属性 - 类本身成为
type
的实例
这就是为什么我们说"类也是对象"的原因。
2. 对象的诞生:__new__
与 __init__
的协奏曲
Client Python new init Instance Car("Tesla", "Model 3") 调用 new(cls, ...) 创建空实例 返回实例 调用 init(self, ...) 初始化属性 None 返回完整实例 Client Python new init Instance
许多人误以为 __init__
是 Python 的构造函数,但事实并非如此。对象的创建分为两个阶段:
__new__
(分配内存):负责创建并返回一个类的空实例__init__
(初始化实例):负责为实例填充属性
python
class Car:
def __new__(cls, *args, **kwargs):
print(f"1. 调用 __new__: 为 {cls.__name__} 分配内存。")
instance = super().__new__(cls)
return instance
def __init__(self, brand, model):
print(f"2. 调用 __init__: 初始化 {brand} {model}。")
self.brand = brand
self.model = model
# 注意:__init__ 不应有 return 语句
my_car = Car("Tesla", "Model 3")
# 输出:
# 1. 调用 __new__: 为 Car 分配内存。
# 2. 调用 __init__: 初始化 Tesla Model 3。
3. self
的奥秘:从函数到绑定方法
方法调用转换过程
┌─────────────────┐ ┌─────────────────┐
│ 类中定义时 │ │ 实例访问时 │
│ │ │ │
│ def method() │───▶│ bound method │
│ (普通函数) │ │ (绑定方法) │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
obj.method = method obj.method()
(未绑定) (self 自动传入)
self
是一个约定俗成的名称,它代表实例对象本身。当你在实例上调用一个方法时,Python 会自动将该实例作为第一个参数传递给方法。
python
class Pizza:
def __init__(self, ingredients):
self.ingredients = ingredients
@staticmethod
def is_vegetarian(ingredients_list):
"""静态方法:不接收 self 或 cls"""
return "火腿" not in ingredients_list
@classmethod
def from_margherita(cls):
"""类方法:接收 cls 作为第一个参数"""
return cls(["番茄", "马苏里拉奶酪"])
# 静态方法可以直接通过类调用
print(Pizza.is_vegetarian(["番茄", "蘑菇"])) # True
# 类方法作为工厂函数
margherita_pizza = Pizza.from_margherita()
print(margherita_pizza.ingredients) # ['番茄', '马苏里拉奶酪']
4. 属性的三级查找链
找到 未找到 找到 未找到 找到 未找到 访问 obj.attr 实例 dict 返回实例属性 类 dict 返回类属性 父类 MRO 返回父类属性 AttributeError
Python 访问属性时遵循一个查找顺序:
- 实例属性 :首先在实例的
__dict__
中查找 - 类属性 :如果在实例中找不到,则向上追溯到类的
__dict__
中查找 - 父类属性:如果当前类也找不到,会继续沿着继承链(MRO)向上查找
⚠️ 重要提醒:属性遮蔽现象
如果在实例上对一个与类属性同名的属性进行赋值,会发生"遮蔽"(shadowing),即创建一个新的实例属性,它会覆盖掉对类属性的访问,但不会修改类属性本身。
python
class Example:
shared = "我是类属性"
obj1 = Example()
obj2 = Example()
print(obj1.shared) # "我是类属性"
obj1.shared = "我是实例属性"
print(obj1.shared) # "我是实例属性" (遮蔽了类属性)
print(obj2.shared) # "我是类属性" (类属性未被修改)
5. 调试友好双剑客:__str__
与 __repr__
字符串表示方法对比
┌─────────────────┬─────────────────┐
│ __str__ │ __repr__ │
├─────────────────┼─────────────────┤
│ 用户友好 │ 开发者友好 │
│ 可读性优先 │ 明确性优先 │
│ print() 调用 │ 交互式显示 │
│ str() 调用 │ repr() 调用 │
└─────────────────┴─────────────────┘
python
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
# 目标:明确,无歧义,最好能重建对象
return f"Point(x={self.x!r}, y={self.y!r})"
def __str__(self):
# 目标:对用户友好
return f"坐标点 ({self.x}, {self.y})"
p = Point(1, 2)
print(p) # 调用 __str__ -> 坐标点 (1, 2)
print(repr(p)) # 调用 __repr__ -> Point(x=1, y=2)
print([p]) # 容器会调用元素的 __repr__ -> [Point(x=1, y=2)]
6. "私有"属性:君子协定与名字重整
Python 访问控制机制
┌─────────────────────────────────────────┐
│ public_attr ← 完全公开 │
│ _protected_attr ← 受保护 (约定) │
│ __private_attr ← 名字重整 │
│ ↓ │
│ _ClassName__private_attr (实际名称) │
└─────────────────────────────────────────┘
Python 没有真正的私有属性。访问控制主要依靠约定:
python
class MyClass:
def __init__(self):
self.public_var = 0 # 公开属性
self._protected_var = 1 # 受保护属性 (约定)
self.__private_var = 2 # 私有属性 (名字重整)
def get_private(self):
return self.__private_var
obj = MyClass()
print(obj.public_var) # 0 (正常访问)
print(obj._protected_var) # 1 (可以访问,但不推荐)
# print(obj.__private_var) # AttributeError
# 但可以通过重整后的名字访问
print(obj._MyClass__private_var) # 2
Ch11 三大特性一步到位
OOP 三大特性关系图
┌─────────────────────────────────────────────────────────┐
│ 面向对象 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 继承 │ │ 多态 │ │ 封装 │ │
│ │ Inheritance │ │ Polymorphism│ │Encapsulation│ │
│ │ │ │ │ │ │ │
│ │ 代码复用 │ │ 接口统一 │ │ 数据保护 │ │
│ │ super() │ │ 鸭子类型 │ │ @property │ │
│ │ MRO │ │ abc.ABC │ │ getter/setter│ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
1. 继承与 super()
:驾驭 MRO
继承允许一个类(子类)获取另一个类(父类)的属性和方法。Python 支持多重继承,使用 C3 线性化算法 来计算方法解析顺序(MRO)。
python
class A:
def __init__(self, **kwargs):
super().__init__(**kwargs)
print("A")
class B(A):
def __init__(self, **kwargs):
super().__init__(**kwargs)
print("B")
class C(A):
def __init__(self, **kwargs):
super().__init__(**kwargs)
print("C")
class D(B, C):
def __init__(self, **kwargs):
super().__init__(**kwargs)
print("D")
# MRO: D -> B -> C -> A -> object
d = D()
# 输出: A, C, B, D (注意顺序与 MRO 中 __init__ 的调用顺序相反)
# 查看 MRO
print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>,
# <class '__main__.A'>, <class 'object'>)
🔍 深入理解:C3 线性化算法
C3 线性化算法确保:
- 单调性:子类永远在父类前面
- 局部优先级保持:如果 A 在类定义中出现在 B 前面,那么在 MRO 中 A 也在 B 前面
- 无环性:避免循环继承
这个算法解决了多重继承中的"菱形问题",确保方法调用的顺序是明确且可预测的。
2. 多态与鸭子类型:行为决定一切
鸭子类型哲学
┌─────────────────────────────────────────┐
│ "如果它走起路来像鸭子,叫起来也像鸭子, │
│ 那么它就是一只鸭子。" │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 鸟 │ │ 飞机 │ │ 超人 │ │
│ │ .fly() │ │ .fly() │ │ .fly() │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │
│ └────────────┼────────────┘ │
│ ▼ │
│ make_it_fly(obj) │
└─────────────────────────────────────────┘
Python 的多态性根植于"鸭子类型"哲学:我们不关心对象的"血统",只关心它是否具备我们需要的"行为"。
python
import abc
class CanFly(abc.ABC):
@abc.abstractmethod
def fly(self):
"""使对象飞行的方法"""
pass
class Bird(CanFly):
def fly(self):
print("🐦 鸟儿在扇动翅膀飞翔。")
class Airplane(CanFly):
def fly(self):
print("✈️ 飞机在引擎轰鸣中起飞。")
# 虚拟子类:即使 Plane 没有继承 CanFly,只要注册了,
# isinstance 和 issubclass 也会认为它们是兼容的。
class Plane:
def fly(self):
print("🛩️ 小型飞机起飞了。")
CanFly.register(Plane)
def make_it_fly(flyer: CanFly):
flyer.fly()
# 多态性展示
flyers = [Bird(), Airplane(), Plane()]
for flyer in flyers:
make_it_fly(flyer)
print(isinstance(Plane(), CanFly)) # True
3. 封装与 @property
:优雅的访问控制
封装是将数据(属性)和操作数据的方法捆绑在一起的机制。@property
装饰器让你在保持简洁的 .
语法的同时,背后可以执行复杂的逻辑。
python
import math
class Circle:
def __init__(self, radius):
self._radius = radius # 使用"受保护"的内部变量
@property
def radius(self):
"""半径属性的 getter"""
return self._radius
@radius.setter
def radius(self, value):
"""半径属性的 setter,带数据验证"""
if value < 0:
raise ValueError("半径不能为负数。")
self._radius = value
@property
def area(self):
"""一个只读的计算属性"""
return math.pi * (self._radius ** 2)
@property
def circumference(self):
"""周长计算属性"""
return 2 * math.pi * self._radius
# 使用示例
c = Circle(10)
print(f"半径: {c.radius}") # 10
print(f"面积: {c.area:.2f}") # 314.16
print(f"周长: {c.circumference:.2f}") # 62.83
c.radius = 12 # 调用 setter
print(f"新面积: {c.area:.2f}") # 452.39
# c.area = 5 # AttributeError: can't set attribute
🚀 高级技巧:cached_property
对于计算昂贵的属性,可以使用 functools.cached_property
:
python
from functools import cached_property
import time
class ExpensiveCalculation:
@cached_property
def expensive_result(self):
print("正在进行昂贵的计算...")
time.sleep(1) # 模拟耗时操作
return 42
obj = ExpensiveCalculation()
print(obj.expensive_result) # 第一次调用,会计算
print(obj.expensive_result) # 第二次调用,直接返回缓存结果
Ch12 魔法工具箱:让你的类更强大
Python 魔术方法全景图
┌─────────────────────────────────────────────────────────┐
│ 🎩 魔术方法 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 运算符重载 │ │ 容器协议 │ │ 上下文管理 │ │
│ │ __add__ │ │ __len__ │ │ __enter__ │ │
│ │ __eq__ │ │ __getitem__ │ │ __exit__ │ │
│ │ __lt__ │ │ __setitem__ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 字符串表示 │ │ 函数调用 │ │ 属性访问 │ │
│ │ __str__ │ │ __call__ │ │ __getattr__ │ │
│ │ __repr__ │ │ │ │ __setattr__ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
1. 运算符重载:让对象表现得像内置类型
通过实现特定的"魔术方法"(dunder methods),你可以让自定义对象支持 Python 的内置运算符。
运算符 | 方法 | 反向 | 就地 | 描述 |
---|---|---|---|---|
+ |
__add__ |
__radd__ |
__iadd__ |
a + b 。如果 a 不支持,尝试 b.__radd__(a) |
== |
__eq__ |
--- | --- | a == b 。建议同时实现 __hash__ |
< |
__lt__ |
__gt__ |
--- | a < b 。配合 @functools.total_ordering |
len() |
__len__ |
--- | --- | 必须返回整数 |
obj[k] |
__getitem__ |
--- | --- | 支持下标和切片 |
with obj |
__enter__ , __exit__ |
--- | --- | 上下文管理器协议 |
obj() |
__call__ |
--- | --- | 让实例可以像函数一样被调用 |
python
from functools import total_ordering
@total_ordering
class Student:
def __init__(self, name, score):
self.name = name
self.score = 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 __add__(self, other):
if isinstance(other, Student):
return Student(f"{self.name}+{other.name}",
(self.score + other.score) / 2)
return NotImplemented
def __repr__(self):
return f"Student('{self.name}', {self.score})"
# 使用示例
alice = Student("Alice", 85)
bob = Student("Bob", 92)
charlie = Student("Charlie", 78)
print(alice < bob) # True
print(alice == charlie) # False
print(alice + bob) # Student('Alice+Bob', 88.5)
students = [alice, bob, charlie]
students.sort() # 自动使用 __lt__ 进行排序
print(students) # [Student('Charlie', 78), Student('Alice', 85), Student('Bob', 92)]
2. @dataclass
:一行顶十行的代码生成器
@dataclass 功能对比
┌─────────────────┬─────────────────┬─────────────────┐
│ 传统类定义 │ @dataclass │ 增强选项 │
├─────────────────┼─────────────────┼─────────────────┤
│ 手写 __init__ │ 自动生成 │ init=True │
│ 手写 __repr__ │ 自动生成 │ repr=True │
│ 手写 __eq__ │ 自动生成 │ eq=True │
│ 手写 __hash__ │ 可选生成 │ frozen=True │
│ 普通属性存储 │ 可优化内存 │ slots=True │
└─────────────────┴─────────────────┴─────────────────┘
dataclasses
模块自 Python 3.7 引入,是一个代码生成器,可以为你自动添加样板代码。
python
from dataclasses import dataclass, field
from typing import List
@dataclass(frozen=True, slots=True)
class Point3D:
x: float
y: float
z: float = field(default=0.0, repr=False) # z 默认为0,且不出现在 repr 中
def distance_from_origin(self) -> float:
return (self.x**2 + self.y**2 + self.z**2)**0.5
@dataclass
class Student:
name: str
age: int
grades: List[float] = field(default_factory=list)
@property
def average_grade(self) -> float:
return sum(self.grades) / len(self.grades) if self.grades else 0.0
# 使用示例
p1 = Point3D(1, 2)
p2 = Point3D(1, 2)
print(p1) # Point3D(x=1.0, y=2.0)
print(p1 == p2) # True (自动实现了 __eq__)
print(p1.distance_from_origin()) # 2.23606797749979
student = Student("Alice", 20)
student.grades.extend([85, 92, 78])
print(f"平均分: {student.average_grade:.1f}") # 平均分: 85.0
⚡ 性能优化:slots=True 的威力
使用 slots=True
可以:
- 减少内存使用(约 40-50%)
- 提高属性访问速度
- 防止动态添加属性
python
import sys
@dataclass
class RegularPoint:
x: float
y: float
@dataclass(slots=True)
class SlottedPoint:
x: float
y: float
regular = RegularPoint(1.0, 2.0)
slotted = SlottedPoint(1.0, 2.0)
print(f"Regular: {sys.getsizeof(regular)} bytes")
print(f"Slotted: {sys.getsizeof(slotted)} bytes")
# Slotted 通常会更小
3. 枚举与类型提示:提升代码的健壮性与可读性
enum.Enum TrafficLight RED YELLOW GREEN typing List Dict Union Optional
python
from enum import Enum, auto
from typing import List, Dict, Union, Optional
class TrafficLight(Enum):
RED = auto()
YELLOW = auto()
GREEN = auto()
def can_go(self) -> bool:
return self == TrafficLight.GREEN
def next_light(self) -> 'TrafficLight':
transitions = {
TrafficLight.RED: TrafficLight.GREEN,
TrafficLight.GREEN: TrafficLight.YELLOW,
TrafficLight.YELLOW: TrafficLight.RED
}
return transitions[self]
class TrafficController:
def __init__(self):
self.current_light = TrafficLight.RED
self.log: List[str] = []
def change_light(self) -> None:
old_light = self.current_light
self.current_light = self.current_light.next_light()
self.log.append(f"{old_light.name} -> {self.current_light.name}")
def get_status(self) -> Dict[str, Union[str, bool]]:
return {
"current": self.current_light.name,
"can_go": self.current_light.can_go(),
"changes": len(self.log)
}
# 使用示例
controller = TrafficController()
print(f"初始状态: {controller.current_light.name}") # RED
controller.change_light()
print(f"变更后: {controller.current_light.name}") # GREEN
print(f"可以通行: {controller.current_light.can_go()}") # True
print(controller.get_status())
# {'current': 'GREEN', 'can_go': True, 'changes': 1}
4. 极简设计模式
单例模式 (Singleton)
单例模式实现对比
┌─────────────────┬─────────────────┬─────────────────┐
│ 传统实现 │ 装饰器实现 │ 元类实现 │
├─────────────────┼─────────────────┼─────────────────┤
│ 复杂的逻辑 │ 简洁优雅 │ 高级但复杂 │
│ 线程安全问题 │ functools │ metaclass │
│ 代码重复 │ @cache │ type.__call__ │
└─────────────────┴─────────────────┴─────────────────┘
python
# 线程安全的 Pythonic 实现
from functools import cache
import threading
import time
class _Meta(type):
@cache
def __call__(cls, *args, **kwargs):
return super().__call__(*args, **kwargs)
class DatabaseConnection(metaclass=_Meta):
def __init__(self):
print(f"🔗 正在创建新的数据库连接... (线程: {threading.current_thread().name})")
time.sleep(0.1) # 模拟连接耗时
self.connection_id = id(self)
def query(self, sql: str) -> str:
return f"执行查询: {sql} (连接ID: {self.connection_id})"
# 测试单例模式
def test_singleton():
conn = DatabaseConnection()
print(f"连接ID: {conn.connection_id}")
# 多线程测试
threads = []
for i in range(3):
thread = threading.Thread(target=test_singleton, name=f"Thread-{i}")
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
# 验证是否为同一实例
a = DatabaseConnection()
b = DatabaseConnection()
print(f"是否为同一实例: {a is b}") # True
工厂函数 (Factory)
python
from abc import ABC, abstractmethod
from typing import Dict, Type
class Shape(ABC):
@abstractmethod
def draw(self) -> str:
pass
@abstractmethod
def area(self) -> float:
pass
class Circle(Shape):
def __init__(self, radius: float):
self.radius = radius
def draw(self) -> str:
return f"⭕ 画一个半径为 {self.radius} 的圆形"
def area(self) -> float:
return 3.14159 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width: float, height: float):
self.width = width
self.height = height
def draw(self) -> str:
return f"⬜ 画一个 {self.width}x{self.height} 的矩形"
def area(self) -> float:
return self.width * self.height
class Triangle(Shape):
def __init__(self, base: float, height: float):
self.base = base
self.height = height
def draw(self) -> str:
return f"🔺 画一个底边 {self.base},高 {self.height} 的三角形"
def area(self) -> float:
return 0.5 * self.base * self.height
class ShapeFactory:
_shapes: Dict[str, Type[Shape]] = {
"circle": Circle,
"rectangle": Rectangle,
"triangle": Triangle
}
@classmethod
def create_shape(cls, shape_type: str, **kwargs) -> Shape:
if shape_type not in cls._shapes:
raise ValueError(f"不支持的形状类型: {shape_type}")
shape_class = cls._shapes[shape_type]
return shape_class(**kwargs)
@classmethod
def register_shape(cls, name: str, shape_class: Type[Shape]) -> None:
"""注册新的形状类型"""
cls._shapes[name] = shape_class
@classmethod
def available_shapes(cls) -> List[str]:
return list(cls._shapes.keys())
# 使用示例
shapes = [
ShapeFactory.create_shape("circle", radius=5),
ShapeFactory.create_shape("rectangle", width=4, height=6),
ShapeFactory.create_shape("triangle", base=3, height=4)
]
for shape in shapes:
print(shape.draw())
print(f"面积: {shape.area():.2f}")
print("-" * 30)
print(f"可用形状: {ShapeFactory.available_shapes()}")
总结:Python OOP 速记口诀(精解版)
🎯 Python OOP 核心要点
┌─────────────────────────────────────────────────────────┐
│ ┌─────────────────────────────────────────────────────┐│
│ │ 类是模板也是对象,init 只负责填肉 ││
│ │ 变量找 dict 先实例后类,double 下划线被改名 ││
│ │ str 用户 repr 调试,property 把方法变属性 ││
│ │ super 按 MRO 走,多态看行为不看族 ││
│ │ 重载返回 NotImplemented,dataclass 写 slots 省内存 ││
│ │ enum 做常量集,工厂返回类,Python 把面向对象玩成脚本││
│ └─────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────┘
🔍 详细解读
-
类是模板也是对象,
init
只负责填肉 :类是
type
的实例,__new__
创建骨架,__init__
负责初始化。 -
变量找
dict
先实例后类,double
下划线被改名 :属性查找遵循 MRO,
__var
会被重整为_Class__var
。 -
str
用户repr
调试,property
把方法变属性 :明确
__str__
和__repr__
的用途,用@property
优雅封装。 -
super
按 MRO 走,多态看行为不看族 :
super()
遵循 C3 线性化顺序,鸭子类型是 Python 多态的核心。 -
重载返回
NotImplemented
,dataclass
写slots
省内存 :善用魔术方法和
dataclasses
模块提升开发效率。 -
enum
做常量集,工厂返回类,Python 把面向对象玩成脚本 :Python 的 OOP 灵活而强大,鼓励简洁、实用的设计。
📊 学习进度追踪
- 掌握类与对象的基本概念
- 理解
__new__
和__init__
的区别 - 熟练使用
@staticmethod
和@classmethod
- 掌握属性查找链和 MRO
- 实现
__str__
和__repr__
方法 - 理解继承和
super()
的使用 - 掌握多态和鸭子类型
- 熟练使用
@property
进行封装 - 掌握常用魔术方法
- 使用
@dataclass
简化代码 - 理解并应用设计模式
🎉 恭喜你完成了 Python 面向对象编程的深度学习!
现在你已经掌握了 Python OOP 的精髓,可以开始构建更加优雅和强大的程序了!