第4章:面向对象
一、导读
1.1 本章学习目标
第4章讲解 Python 的面向对象编程:描述符、属性、魔术方法、抽象基类。
本章核心学习目标:
- 掌握描述符协议:
__get__、__set__、__delete__ - 理解 @property 的实现原理
- 掌握抽象基类(ABC):强制子类实现特定方法
- 理解 Python 的方法解析顺序(MRO)
1.2 为什么本章如此重要
Python 的 OOP 比大多数语言更灵活。描述符和 ABC 是构建框架和库的核心工具。
1.3 核心问题引导
@property 底层是如何实现的?
描述符和 @property 有什么区别?
ABC 如何强制子类实现接口?
二、核心概念详解
2.1 描述符
python
class Validated:
"""描述符: 验证属性值"""
def __init__(self, validator=None):
self.validator = validator
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__.get(self.name)
def __set__(self, instance, value):
if self.validator:
value = self.validator(value)
instance.__dict__[self.name] = value
def not_empty(value):
if not value:
raise ValueError("Value cannot be empty")
return value
def positive(value):
if value <= 0:
raise ValueError("Value must be positive")
return value
class Product:
name = Validated(not_empty)
price = Validated(positive)
quantity = Validated(positive)
def __init__(self, name, price, quantity):
self.name = name
self.price = price
self.quantity = quantity
p = Product("Widget", 9.99, 100)
# p.price = -1 # ValueError: Value must be positive2.2 @property
python
class Temperature:
def __init__(self, celsius=0):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("Too cold!")
self._celsius = value
@property
def fahrenheit(self):
return self._celsius * 9/5 + 32
@fahrenheit.setter
def fahrenheit(self, value):
self.celsius = (value - 32) * 5/9
t = Temperature(100)
print(t.fahrenheit) # 212.0
t.fahrenheit = 32
print(t.celsius) # 0.02.3 抽象基类
python
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self) -> float:
"""计算面积"""
pass
@abstractmethod
def perimeter(self) -> float:
"""计算周长"""
pass
@classmethod
@abstractmethod
def name(cls) -> str:
"""返回形状名称"""
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
import math
return math.pi * self.radius ** 2
def perimeter(self):
import math
return 2 * math.pi * self.radius
@classmethod
def name(cls):
return "Circle"
# c = Shape() # TypeError: Can't instantiate abstract class
c = Circle(5)
print(c.area()) # 78.539...三、实战案例
3.1 案例一:类型验证描述符
python
class Typed:
"""类型验证描述符"""
def __init__(self, expected_type):
self.expected_type = expected_type
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__.get(self.name)
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
raise TypeError(f"{self.name} must be {self.expected_type.__name__}")
instance.__dict__[self.name] = value
class Employee:
name = Typed(str)
age = Typed(int)
salary = Typed(float)
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
e = Employee("Alice", 30, 50000.0)
# e.age = "thirty" # TypeError: age must be int四、本章小结
描述符是 @property 的底层实现,提供更灵活的属性管理
@property 将方法调用伪装成属性访问
ABC 强制子类实现特定接口
五、思考与练习
描述符的 __get__ 在类访问和实例访问时有什么区别?
如何用 ABC 定义一个可迭代的接口?