====== 第六章 高级OOP特性 ======
===== 本章概要 =====
本章将深入探讨Python面向对象编程的高级特性,包括魔术方法、属性描述符、元类等,帮助你编写更Pythonic、更强大的代码。
===== 6.1 魔术方法(Magic Methods) =====
魔术方法(也称为特殊方法或dunder方法)以双下划线开头和结尾,在特定情况下自动调用。
==== 6.1.1 对象表示 ====
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
"""给用户的友好表示"""
return f"Person(name='{self.name}', age={self.age})"
def __repr__(self):
"""给开发者的详细表示,eval(repr(obj))应能重建对象"""
return f"Person('{self.name}', {self.age})"
def __bytes__(self):
"""返回bytes表示"""
return str(self).encode('utf-8')
p = Person("Alice", 25)
print(str(p)) # Person(name='Alice', age=25)
print(repr(p)) # Person('Alice', 25)
print(bytes(p)) # b"Person(name='Alice', age=25)"
==== 6.1.2 比较操作 ====
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def __eq__(self, other):
"""等于 =="""
if not isinstance(other, Rectangle):
return NotImplemented
return self.area() == other.area()
def __lt__(self, other):
"""小于 <"""
if not isinstance(other, Rectangle):
return NotImplemented
return self.area() < other.area()
def __le__(self, other):
"""小于等于 <="""
return self < other or self == other
# 使用functools.total_ordering可以自动生成其他比较方法
def __hash__(self):
"""支持作为字典键或集合元素"""
return hash((self.width, self.height))
r1 = Rectangle(3, 4) # area = 12
r2 = Rectangle(2, 6) # area = 12
r3 = Rectangle(4, 4) # area = 16
print(r1 == r2) # True(面积相等)
print(r1 < r3) # True
==== 6.1.3 算术运算 ====
class Vector:
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 __sub__(self, other):
"""- 运算符"""
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar):
"""* 运算符(向量数乘)"""
return Vector(self.x * scalar, self.y * scalar)
def __rmul__(self, scalar):
"""右乘(scalar * vector)"""
return self * scalar
def __truediv__(self, scalar):
"""/ 运算符"""
return Vector(self.x / scalar, self.y / scalar)
def __neg__(self):
"""一元负号 -vector"""
return Vector(-self.x, -self.y)
def __abs__(self):
"""abs()函数"""
return (self.x ** 2 + self.y ** 2) ** 0.5
def __str__(self):
return f"Vector({self.x}, {self.y})"
def __repr__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(3, 4)
v2 = Vector(1, 2)
print(v1 + v2) # Vector(4, 6)
print(v1 * 2) # Vector(6, 8)
print(3 * v1) # Vector(9, 12)
print(abs(v1)) # 5.0
==== 6.1.4 容器类型方法 ====
class Deck:
def __init__(self):
self._cards = []
def __len__(self):
"""len()函数"""
return len(self._cards)
def __getitem__(self, index):
"""索引访问 deck[index]"""
return self._cards[index]
def __setitem__(self, index, value):
"""索引赋值 deck[index] = value"""
self._cards[index] = value
def __delitem__(self, index):
"""del deck[index]"""
del self._cards[index]
def __iter__(self):
"""迭代"""
return iter(self._cards)
def __contains__(self, item):
"""in 运算符"""
return item in self._cards
def add_card(self, card):
self._cards.append(card)
# 使用
deck = Deck()
for card in ["A♠", "K♥", "Q♦"]:
deck.add_card(card)
print(len(deck)) # 3
print(deck[0]) # A♠
print("K♥" in deck) # True
for card in deck:
print(card)
==== 6.1.5 上下文管理器 ====
class DatabaseConnection:
def __init__(self, db_name):
self.db_name = db_name
self.connection = None
def __enter__(self):
"""进入with语句时调用"""
print(f"Connecting to {self.db_name}...")
self.connection = f"Connection({self.db_name})"
return self.connection
def __exit__(self, exc_type, exc_val, exc_tb):
"""退出with语句时调用"""
print(f"Closing connection to {self.db_name}...")
self.connection = None
# 返回True表示异常已处理,不再传播
return False
# 使用
with DatabaseConnection("mydb") as conn:
print(f"Using {conn}")
# 输出:
# Connecting to mydb...
# Using Connection(mydb)
# Closing connection to mydb...
==== 6.1.6 可调用对象 ====
class Counter:
def __init__(self, start=0):
self.value = start
def __call__(self, step=1):
"""使实例可以像函数一样调用"""
self.value += step
return self.value
def __int__(self):
"""int()转换"""
return self.value
def __index__(self):
"""用于索引"""
return self.value
counter = Counter(10)
print(counter()) # 11
print(counter(5)) # 16
print(int(counter)) # 16
===== 6.2 属性(Property)深入 =====
==== 6.2.1 属性装饰器详解 ====
class Temperature:
def __init__(self, celsius=0):
self._celsius = celsius
@property
def celsius(self):
"""Getter"""
print("Getting celsius...")
return self._celsius
@celsius.setter
def celsius(self, value):
"""Setter"""
print("Setting celsius...")
if value < -273.15:
raise ValueError("Temperature below absolute zero!")
self._celsius = value
@celsius.deleter
def celsius(self):
"""Deleter"""
print("Deleting celsius...")
del self._celsius
@property
def fahrenheit(self):
"""只读属性"""
return self._celsius * 9/5 + 32
# 使用
temp = Temperature(25)
print(temp.celsius) # 调用getter
temp.celsius = 30 # 调用setter
print(temp.fahrenheit) # 只读属性
==== 6.2.2 property() 函数方式 ====
class Circle:
def __init__(self, radius):
self._radius = radius
def get_radius(self):
return self._radius
def set_radius(self, value):
if value < 0:
raise ValueError("Radius cannot be negative")
self._radius = value
def del_radius(self):
del self._radius
# 使用property()函数
radius = property(
fget=get_radius,
fset=set_radius,
fdel=del_radius,
doc="The radius property"
)
@property
def area(self):
import math
return math.pi * self._radius ** 2
===== 6.3 描述符(Descriptor) =====
描述符是实现了特定协议的类,用于管理属性访问。
==== 6.3.1 描述符协议 ====
class Validator:
"""基础验证描述符"""
def __init__(self, min_value=None, max_value=None):
self.min_value = min_value
self.max_value = max_value
self.name = None
def __set_name__(self, owner, name):
"""Python 3.6+ 自动获取属性名"""
self.name = name
self.private_name = f"_{name}"
def __get__(self, obj, objtype=None):
if obj is None:
return self
return getattr(obj, self.private_name, None)
def __set__(self, obj, value):
if self.min_value is not None and value < self.min_value:
raise ValueError(f"{self.name} must be >= {self.min_value}")
if self.max_value is not None and value > self.max_value:
raise ValueError(f"{self.name} must be <= {self.max_value}")
setattr(obj, self.private_name, value)
def __delete__(self, obj):
raise AttributeError(f"Cannot delete {self.name}")
class Person:
age = Validator(min_value=0, max_value=150)
salary = Validator(min_value=0)
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
# 使用
p = Person("Alice", 25, 5000)
print(p.age) # 25
# p.age = 200 # ValueError: age must be <= 150
==== 6.3.2 类型检查描述符 ====
class Typed:
"""类型检查描述符"""
def __init__(self, expected_type):
self.expected_type = expected_type
self.name = None
def __set_name__(self, owner, name):
self.name = name
self.private_name = f"_{name}"
def __get__(self, obj, objtype=None):
if obj is None:
return self
return getattr(obj, self.private_name)
def __set__(self, obj, value):
if not isinstance(value, self.expected_type):
raise TypeError(f"{self.name} must be {self.expected_type.__name__}, "
f"got {type(value).__name__}")
setattr(obj, self.private_name, value)
class Stock:
name = Typed(str)
shares = Typed(int)
price = Typed(float)
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
@property
def cost(self):
return self.shares * self.price
# 使用
s = Stock("GOOG", 100, 123.45)
print(s.cost) # 12345.0
# s.shares = "100" # TypeError
===== 6.4 元类(Metaclass) =====
元类是创建类的"类",类是元类的实例。
==== 6.4.1 type 元类 ====
# 使用type动态创建类
def init(self, name):
self.name = name
def greet(self):
return f"Hello, I'm {self.name}"
# 创建类
Person = type('Person', (), {
'__init__': init,
'greet': greet
})
p = Person("Alice")
print(p.greet()) # Hello, I'm Alice
==== 6.4.2 自定义元类 ====
class SingletonMeta(type):
"""单例元类"""
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=SingletonMeta):
def __init__(self, db_name):
self.db_name = db_name
def query(self, sql):
return f"Querying {self.db_name}: {sql}"
# 使用
db1 = Database("mydb")
db2 = Database("otherdb") # 实际上还是mydb实例
print(db1 is db2) # True
==== 6.4.3 元类控制类创建 ====
class AutoReprMeta(type):
"""自动为类生成__repr__方法的元类"""
def __new__(mcs, name, bases, namespace, **kwargs):
# 创建类之前修改namespace
if '__repr__' not in namespace:
namespace['__repr__'] = mcs._make_repr(name, namespace.get('__init__'))
cls = super().__new__(mcs, name, bases, namespace)
return cls
@staticmethod
def _make_repr(class_name, init_method):
def __repr__(self):
attrs = ', '.join(f"{k}={v!r}" for k, v in self.__dict__.items())
return f"{class_name}({attrs})"
return __repr__
class Point(metaclass=AutoReprMeta):
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(3, 4)
print(p) # Point(x=3, y=4)
===== 6.5 抽象基类(ABC) =====
==== 6.5.1 定义抽象基类 ====
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
"""子类必须实现"""
pass
@abstractmethod
def move(self):
pass
@property
@abstractmethod
def species(self):
"""抽象属性"""
pass
def introduce(self):
"""具体方法"""
return f"I am a {self.species}"
class Dog(Animal):
@property
def species(self):
return "Canis familiaris"
def speak(self):
return "Woof!"
def move(self):
return "Running on 4 legs"
# animal = Animal() # TypeError: Can't instantiate abstract class
dog = Dog()
print(dog.introduce()) # I am a Canis familiaris
==== 6.5.2 注册虚拟子类 ====
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Rectangle:
"""普通类,没有继承Shape"""
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
# 注册为虚拟子类
Shape.register(Rectangle)
print(issubclass(Rectangle, Shape)) # True
print(isinstance(Rectangle(3, 4), Shape)) # True
===== 6.6 混入(Mixin) =====
混入是一种设计模式,通过多重继承为类添加功能。
class JSONSerializableMixin:
"""提供JSON序列化功能的混入"""
import json
def to_json(self):
return self.json.dumps(self.__dict__, indent=2)
@classmethod
def from_json(cls, json_str):
data = cls.json.loads(json_str)
return cls(**data)
class ComparableMixin:
"""提供比较功能的混入"""
def __eq__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
return self.__dict__ == other.__dict__
def __ne__(self, other):
return not self == other
class Person(JSONSerializableMixin, ComparableMixin):
def __init__(self, name, age):
self.name = name
self.age = age
# 使用
p1 = Person("Alice", 25)
p2 = Person("Alice", 25)
print(p1.to_json())
print(p1 == p2) # True
===== 6.7 代码示例 =====
==== 示例1:实现一个完整的数值类型 ====
from functools import total_ordering
@total_ordering
class Rational:
"""有理数类"""
def __init__(self, numerator, denominator=1):
if denominator == 0:
raise ValueError("Denominator cannot be zero")
# 约分
g = self._gcd(abs(numerator), abs(denominator))
self.numerator = numerator // g
self.denominator = denominator // g
# 确保分母为正
if self.denominator < 0:
self.numerator = -self.numerator
self.denominator = -self.denominator
@staticmethod
def _gcd(a, b):
while b:
a, b = b, a % b
return a
def __str__(self):
if self.denominator == 1:
return str(self.numerator)
return f"{self.numerator}/{self.denominator}"
def __repr__(self):
return f"Rational({self.numerator}, {self.denominator})"
def __eq__(self, other):
if isinstance(other, Rational):
return (self.numerator == other.numerator and
self.denominator == other.denominator)
return NotImplemented
def __lt__(self, other):
if isinstance(other, Rational):
return (self.numerator * other.denominator <
other.numerator * self.denominator)
return NotImplemented
def __add__(self, other):
if isinstance(other, Rational):
num = (self.numerator * other.denominator +
other.numerator * self.denominator)
den = self.denominator * other.denominator
return Rational(num, den)
return NotImplemented
def __sub__(self, other):
return self + Rational(-other.numerator, other.denominator)
def __mul__(self, other):
if isinstance(other, Rational):
return Rational(self.numerator * other.numerator,
self.denominator * other.denominator)
return NotImplemented
def __truediv__(self, other):
return self * Rational(other.denominator, other.numerator)
def __float__(self):
return self.numerator / self.denominator
def __int__(self):
return self.numerator // self.denominator
# 使用
r1 = Rational(1, 2)
r2 = Rational(1, 3)
print(r1 + r2) # 5/6
print(r1 * r2) # 1/6
print(r1 > r2) # True
print(float(r1)) # 0.5
==== 示例2:使用描述符实现ORM字段 ====
class Field:
"""数据库字段描述符"""
def __init__(self, name=None, dtype=str, primary_key=False):
self.name = name
self.dtype = dtype
self.primary_key = primary_key
def __set_name__(self, owner, name):
if self.name is None:
self.name = name
self.private_name = f"_{name}"
def __get__(self, obj, objtype=None):
if obj is None:
return self
return getattr(obj, self.private_name, None)
def __set__(self, obj, value):
if not isinstance(value, self.dtype):
try:
value = self.dtype(value)
except (ValueError, TypeError):
raise TypeError(f"Expected {self.dtype.__name__}, got {type(value).__name__}")
setattr(obj, self.private_name, value)
class ModelMeta(type):
"""模型元类"""
def __new__(mcs, name, bases, namespace):
fields = {}
for key, value in namespace.items():
if isinstance(value, Field):
fields[key] = value
namespace['_fields'] = fields
return super().__new__(mcs, name, bases, namespace)
class Model(metaclass=ModelMeta):
"""ORM基础模型"""
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
def __repr__(self):
attrs = ', '.join(f"{k}={getattr(self, k)!r}" for k in self._fields)
return f"{self.__class__.__name__}({attrs})"
def to_dict(self):
return {k: getattr(self, k) for k in self._fields}
class User(Model):
id = Field(dtype=int, primary_key=True)
name = Field(dtype=str)
age = Field(dtype=int)
email = Field(dtype=str)
# 使用
user = User(id=1, name="Alice", age=25, email="alice@example.com")
print(user)
print(user.to_dict())
===== 6.8 练习题 =====
==== 练习1:实现一个可切片、可迭代的序列类 ====
class MySequence:
def __init__(self, data):
self._data = list(data)
def __len__(self):
return len(self._data)
def __getitem__(self, index):
if isinstance(index, slice):
return MySequence(self._data[index])
return self._data[index]
def __setitem__(self, index, value):
self._data[index] = value
def __iter__(self):
return iter(self._data)
def __contains__(self, item):
return item in self._data
def __repr__(self):
return f"MySequence({self._data})"
# 测试
seq = MySequence([1, 2, 3, 4, 5])
print(len(seq)) # 5
print(seq[1:4]) # MySequence([2, 3, 4])
print(3 in seq) # True
==== 练习2:实现属性缓存装饰器 ====
class cached_property:
"""缓存属性装饰器,只计算一次"""
def __init__(self, func):
self.func = func
self.name = func.__name__
self.__doc__ = func.__doc__
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.name not in obj.__dict__:
obj.__dict__[self.name] = self.func(obj)
return obj.__dict__[self.name]
class Circle:
def __init__(self, radius):
self.radius = radius
@cached_property
def area(self):
print("Calculating area...")
import math
return math.pi * self.radius ** 2
# 测试
c = Circle(5)
print(c.area) # 计算并缓存
print(c.area) # 直接使用缓存
===== 本章小结 =====
本章深入学习了Python的高级OOP特性:
* **魔术方法** - 对象表示、比较、算术运算、容器操作、上下文管理
* **属性** - @property装饰器、getter/setter/deleter
* **描述符** - __get__、__set__、__delete__协议
* **元类** - type、自定义元类、控制类创建
* **抽象基类** - ABC、@abstractmethod、虚拟子类
* **混入** - 通过多重继承复用代码
这些高级特性让你能够编写更灵活、更强大的面向对象代码。下一章将学习迭代器和生成器。
===== 进一步阅读 =====
* [[https://docs.python.org/zh-cn/3/reference/datamodel.html|Python数据模型]]
* [[https://docs.python.org/zh-cn/3/howto/descriptor.html|描述符指南]]