上篇回顾 :3.2-01 把 Python 四大容器、推导式、函数一等公民、EAFP、鸭子类型讲透。本篇进入 Python 面向对象------与 Java OOP 表面相似但底层机制截然不同。理解魔法方法和元类,才能看懂 Django/SQLAlchemy 这类框架的「声明式魔法」。
一、开篇:Django Model 的魔法
python
# Django Model
class User(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
# 使用
user = User(name="Tom", age=18)
user.save()
User.objects.filter(age__gte=18)
后端工程师看这段代码会困惑:
name和age是类属性还是实例属性?User.objects哪来的?- 为什么定义类属性就能对应数据库列?
答案是元类(metaclass)------Django 的 ModelBase 元类在类创建时拦截字段定义,把它们转成数据库列映射。这是 Python OOP 最强大的特性之一。
本篇从基础 OOP 讲到魔法方法再到元类,让你理解 Python 式面向对象与 Java 式 OOP 的根本差异。
二、类与实例
2.1 基本定义
python
class Dog:
# 类属性(所有实例共享)
species = "Canis familiaris"
# 初始化方法
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age
# 实例方法
def bark(self):
return f"{self.name} says Woof!"
dog = Dog("Rex", 3)
dog.bark() # "Rex says Woof!"
2.2 与 Java 的区别
| 维度 | Python | Java |
|---|---|---|
| 类属性/实例属性 | 共存于 class body | static/instance 分明 |
| 构造 | new + init | 构造方法 |
| 访问控制 | 无(约定 _private/__private) | private/protected/public |
| 多继承 | 支持 | 不支持(接口多实现) |
三、init 真的是构造函数吗
3.1 new 才是真正的构造
python
class MyClass:
def __new__(cls, *args, **kwargs):
print("__new__:创建实例")
instance = super().__new__(cls)
return instance
def __init__(self, value):
print("__init__:初始化实例")
self.value = value
obj = MyClass(42)
# 输出:
# __new__:创建实例
# __init__:初始化实例
流程:
__new__创建实例(分配内存)__init__初始化实例(设置属性)
__new__ 是类方法(第一个参数是 cls),__init__ 是实例方法(第一个参数是 self)。
3.2 什么时候用 new
| 场景 | 说明 |
|---|---|
| 单例模式 | new 里检查是否已创建 |
| 不可变类型 | int/str/tuple 的子类化 |
| 元类 | 控制类的创建 |
python
# 单例
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
a = Singleton()
b = Singleton()
a is b # True
3.3 Java 构造函数 vs Python new /init
Java 的构造函数同时负责「创建+初始化」。Python 分两步:__new__ 创建、__init__ 初始化。分开的好处是可以在创建阶段做控制(单例、缓存实例等)。
四、继承与 MRO
4.1 单继承
python
class Animal:
def speak(self):
return "..."
class Dog(Animal):
def speak(self):
return "Woof"
Dog().speak() # "Woof"(子类覆盖)
4.2 多继承
python
class Flyable:
def fly(self):
return "flying"
class Swimmable:
def swim(self):
return "swimming"
class Duck(Flyable, Swimmable):
pass
duck = Duck()
duck.fly() # "flying"
duck.swim() # "swimming"
Java 不支持多继承(避免菱形问题),Python 支持但用 MRO 解决冲突。
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().greet() # "B"(MRO: D → B → C → A)
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
Python 用 C3 线性化算法计算 MRO------保证子类在父类前、多个父类按声明顺序查找。
4.4 菱形继承怎么解
A
/ \
B C
\ /
D
B 和 C 都继承 A。D 继承 B 和 C。如果 B 和 C 都重写了 A 的方法,D 调哪个?
Python 的 MRO 保证:D → B → C → A,先找到 B 的就用 B 的。如果 B 想调 A 的原始方法,用 super():
python
class B(A):
def greet(self):
super().greet() # 调 C 的 greet(MRO 下一个)
print("B")
super() 在多继承下不是「调父类」而是「调 MRO 的下一个」。
五、property:受控属性
python
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("低于绝对零度")
self._celsius = value
@property
def fahrenheit(self):
return self._celsius * 9/5 + 32
t = Temperature(25)
t.celsius = 30 # 调 setter
t.fahrenheit # 86.0(只读 property)
t.celsius = -300 # ValueError
property 让属性的访问像直接访问字段,但背后可以有校验/计算逻辑。
六、高频魔法方法
6.1 字符串表示
python
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"User(name={self.name!r}, age={self.age})"
def __str__(self):
return f"{self.name} ({self.age}岁)"
user = User("Tom", 18)
repr(user) # User(name='Tom', age=18)(调试用)
str(user) # Tom (18岁)(print 用)
6.2 可调用对象
python
class Multiplier:
def __init__(self, factor):
self.factor = factor
def __call__(self, x):
return x * self.factor
double = Multiplier(2)
double(5) # 10(像函数一样调用)
6.3 序列协议
python
class Matrix:
def __init__(self, data):
self.data = data
def __getitem__(self, key):
return self.data[key]
def __setitem__(self, key, value):
self.data[key] = value
def __len__(self):
return len(self.data)
m = Matrix([1, 2, 3])
m[0] # 1
m[0] = 10
len(m) # 3
6.4 上下文管理器
python
class FileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
return False
with FileManager("test.txt", "w") as f:
f.write("hello")
# 自动关闭
6.5 魔法方法速查表
| 方法 | 触发 | 用途 |
|---|---|---|
| init | 创建后 | 初始化 |
| new | 创建时 | 控制创建 |
| repr | repr() | 调试表示 |
| str | str()/print | 用户表示 |
| call | obj() | 可调用 |
| getitem | objkey | 索引访问 |
| len | len(obj) | 长度 |
| enter /exit | with | 上下文管理 |
| eq | == | 相等比较 |
| hash | hash() | 哈希 |
| iter /next | for | 迭代 |
七、dataclass:现代化写法
python
from dataclasses import dataclass, field
@dataclass
class User:
name: str
age: int = 0
tags: list = field(default_factory=list)
def __post_init__(self):
if self.age < 0:
raise ValueError("年龄不能为负")
user = User("Tom", 18)
# 自动生成 __init__/__repr__/__eq__
dataclass 省去了样板代码,类似 Java 的 record(JDK 14+)。
八、元类:控制类的创建
8.1 type 是所有类的元类
python
# type 创建类
Dog = type("Dog", (object,), {"bark": lambda self: "Woof"})
dog = Dog()
dog.bark() # "Woof"
type 既是函数又是类------它是所有类的元类。
8.2 自定义元类
python
class MyMeta(type):
def __new__(mcs, name, bases, namespace):
# 类创建时拦截
cls = super().__new__(mcs, name, bases, namespace)
# 可以修改类
return cls
class MyClass(metaclass=MyMeta):
pass
8.3 Django Model 的元类魔法
python
# 简化版 Django Model 元类
class ModelMeta(type):
def __new__(mcs, name, bases, namespace):
fields = {}
for key, value in list(namespace.items()):
if isinstance(value, Field):
fields[key] = value
value.name = key # 字段知道自己叫什么
namespace['_fields'] = fields
namespace['objects'] = QuerySet()
return super().__new__(mcs, name, bases, namespace)
class Field:
def __init__(self, **kwargs):
self.name = None
self.options = kwargs
class CharField(Field): pass
class IntegerField(Field): pass
class Model(metaclass=ModelMeta):
pass
# 使用
class User(Model):
name = CharField(max_length=100)
age = IntegerField()
User._fields # {'name': CharField(...), 'age': IntegerField(...)}
User.objects # QuerySet 实例
魔法揭秘:
- 定义 User 类时,ModelMeta.new 被调用
- 元类扫描命名空间,找出所有 Field 实例
- 把字段名写入 Field.name(让字段知道自己叫什么)
- 自动添加 objects 属性(QuerySet)
这就是 ORM 的核心------用元类把「声明式字段定义」转成「数据库列映射」。
九、小结表
| 概念 | Python | Java |
|---|---|---|
| 构造 | new + init | 构造方法 |
| 多继承 | 支持(MRO) | 不支持 |
| 属性控制 | @property | getter/setter |
| 魔法方法 | repr /call 等 | toString() 等 |
| 元类 | type/metaclass | 无 |
| 数据类 | @dataclass | record |
