本章将深入探讨Python面向对象编程的高级特性,包括魔术方法、属性描述符、元类等,帮助你编写更Pythonic、更强大的代码。
魔术方法(也称为特殊方法或dunder方法)以双下划线开头和结尾,在特定情况下自动调用。
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)"
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
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
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)
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...
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
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) # 只读属性
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
描述符是实现了特定协议的类,用于管理属性访问。
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
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
元类是创建类的“类”,类是元类的实例。
# 使用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
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
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)
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
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
混入是一种设计模式,通过多重继承为类添加功能。
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
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
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())
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
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特性:
这些高级特性让你能够编写更灵活、更强大的面向对象代码。下一章将学习迭代器和生成器。