第4章: 面向对象 — 相似的表象,不同的灵魂
Java/Kotlin 开发者看到 Python 的 class 语法会觉得"这不就是一样的东西吗?"——然后掉进坑里。Python 的类系统表面上是经典的 OOP,底层却融合了元编程、描述符协议、MRO 线性化等 JVM 世界不存在的机制。类属性共享、self 显式传递、new/init 分离、多继承 MRO,每一个都是踩坑高发区。本章逐个拆解。
4.1 类定义与 self: 显式接收者
Java/Kotlin 对比
public class User {
private String name;
public User(String name) {
this.name = name;
}
public void greet() {
System.out.println("Hi, " + this.name);
System.out.println("Hi, " + name);
}
}
class User(val name: String) {
fun greet() {
println("Hi, $name")
println("Hi, ${this.name}")
}
}
Python 实现
class User:
def __init__(self, name: str):
self.name = name
def greet(self):
print(f"Hi, {self.name}")
u = User("Alice")
u.greet()
User.greet(u)
class Dog:
pass
d = Dog()
d.name = "Rex"
User.greet(d)
class BadStyle:
def __init__(me, name):
me.name = name
def greet(me):
print(f"Hi, {me.name}")
u = User("Alice")
print(u.greet)
print(User.greet)
核心差异
| 维度 | Java/Kotlin | Python |
|---|
| 接收者 | this 隐式,编译器自动传入 | self 显式,必须手动声明 |
self/this 性质 | 语言关键字 | 普通参数名,只是约定 |
| 方法本质 | 属于类的一部分 | 类中定义的函数,调用时绑定实例 |
| 能否用其他名字 | 不行 | 能,但极其不推荐 |
常见陷阱
class Point:
def __init__(self, x, y):
self.x = x
y = y
def get_y(self):
return y
class Calculator:
def __init__(self, value):
self.value = value
def add(self, delta):
self.value += delta
def add_and_double(self, delta):
add(delta)
self.value *= 2
何时使用
- 永远用
self 作为实例方法的第一个参数名
- 永远用
cls 作为类方法的第一个参数名
- 理解
obj.method(args) 等价于 Class.method(obj, args) 是理解 Python OOP 的一切基础
4.2 init vs 构造函数
Java/Kotlin 对比
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public User(String name) {
this(name, 0);
}
}
class User(val name: String, val age: Int = 0) {
init {
require(age >= 0) { "Age must be non-negative" }
}
}
class User2 {
val name: String
val age: Int
constructor(name: String) : this(name, 0)
constructor(name: String, age: Int) {
this.name = name
this.age = age
}
}
Python 实现
class User:
def __new__(cls, name, age=0):
"""真正的构造器:负责创建实例(分配内存)"""
print(f"__new__ 被调用: cls={cls}")
instance = super().__new__(cls)
return instance
def __init__(self, name, age=0):
"""初始化器:负责填充实例属性"""
print(f"__init__ 被调用: self={self}")
self.name = name
self.age = age
u = User("Alice", 30)
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
print("创建新实例")
cls._instance = super().__new__(cls)
else:
print("返回已有实例")
return cls._instance
def __init__(self):
print("__init__ 被调用")
self.data = []
a = Singleton()
b = Singleton()
print(a is b)
class SafeSingleton:
_instance = None
_initialized = False
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
if not SafeSingleton._initialized:
print("初始化")
self.data = []
SafeSingleton._initialized = True
else:
print("跳过重复初始化")
a = SafeSingleton()
b = SafeSingleton()
print(a is b)
class UpperStr(str):
"""所有字符串自动转大写的 str 子类"""
def __new__(cls, value=""):
return super().__new__(cls, value.upper())
s = UpperStr("hello")
print(s)
print(type(s))
print(isinstance(s, str))
核心差异
| 维度 | Java/Kotlin | Python |
|---|
| 构造过程 | 构造函数一步完成 | __new__(创建)+ __init__(初始化)两步 |
__new__ 等价物 | 无(JVM 构造函数 = 两者合一) | object.__new__(cls) 分配内存 |
| 构造函数链 | this() / super() | __new__ 返回实例后自动调 __init__ |
| 单例实现 | 多种设计模式 | __new__ 控制实例创建 |
常见陷阱
class Broken:
def __new__(cls):
super().__new__(cls)
class A:
def __new__(cls):
return 42
def __init__(self):
print("这会被调用吗?")
a = A()
print(type(a))
何时使用
- 99% 的情况:只写
__init__,不用碰 __new__
- 需要
__new__ 的场景:
- 单例模式
- 子类化不可变类型(
str, int, tuple, frozenset)
- 元类配合(高级用法)
- 控制实例创建逻辑(如对象池、缓存)
4.3 实例属性 vs 类属性
Java/Kotlin 对比
public class Counter {
private static int totalCount = 0;
private int count = 0;
public Counter() {
totalCount++;
count++;
}
public static int getTotalCount() { return totalCount; }
public int getCount() { return count; }
}
class Counter {
companion object {
var totalCount: Int = 0
}
var count: Int = 0
constructor() {
totalCount++
count++
}
}
Python 实现
class Dog:
species = "Canis familiaris"
def __init__(self, name):
self.name = name
d1 = Dog("Rex")
d2 = Dog("Buddy")
print(d1.species)
print(d2.species)
print(Dog.species)
d1.name = "Max"
print(d1.name)
print(d2.name)
class TrickyCounter:
count = 0
def __init__(self):
self.count += 1
c1 = TrickyCounter()
c2 = TrickyCounter()
print(c1.count)
print(c2.count)
print(TrickyCounter.count)
print(c1.count is TrickyCounter.count)
class CorrectCounter:
count = 0
def __init__(self):
CorrectCounter.count += 1
@classmethod
def get_count(cls):
return cls.count
c1 = CorrectCounter()
c2 = CorrectCounter()
print(CorrectCounter.count)
class Animal:
category = "animal"
class Pet(Animal):
species = "pet"
class Dog(Pet):
pass
d = Dog()
print(d.category)
print(d.species)
d.category = "my pet"
print(d.category)
print(Dog.category)
del d.category
print(d.category)
class Team:
members = []
def add_member(self, name):
self.members.append(name)
t1 = Team()
t2 = Team()
t1.add_member("Alice")
t2.add_member("Bob")
print(t1.members)
print(t1.members is t2.members)
class FixedTeam:
def __init__(self):
self.members = []
def add_member(self, name):
self.members.append(name)
t1 = FixedTeam()
t2 = FixedTeam()
t1.add_member("Alice")
t2.add_member("Bob")
print(t1.members)
print(t2.members)
核心差异
| 维度 | Java/Kotlin | Python |
|---|
| 类属性声明 | static 关键字显式标记 | 直接在类体中赋值,无关键字 |
| 属性查找 | 编译时确定(静态绑定) | 运行时沿继承链查找(动态) |
| 实例遮蔽类属性 | 不可能(static 和实例字段是不同的命名空间) | 实例属性同名时遮蔽类属性 |
| 可变共享 | static 字段天然共享 | 类属性中的可变对象天然共享,但容易忘记 |
常见陷阱
def append_to(element, target=[]):
target.append(element)
return target
print(append_to(1))
print(append_to(2))
def append_to(element, target=None):
if target is None:
target = []
target.append(element)
return target
何时使用
- 类属性:常量、所有实例共享的不可变数据、计数器
- 实例属性(在
__init__ 中创建):每个实例独立的数据、可变对象
- 铁律:可变对象(list, dict, set)永远在
__init__ 中初始化,不要作为类属性
4.4 _ 和 __ 访问控制
Java/Kotlin 对比
public class User {
public String name;
protected int age;
private String secret;
private int _score;
public int getScore() { return _score; }
public void setScore(int score) {
if (score >= 0) this._score = score;
}
}
class User(val name: String) {
protected var age: Int = 0
private var secret: String = ""
var score: Int = 0
set(value) {
if (value >= 0) field = value
}
}
Python 实现
class User:
def __init__(self, name, age, secret):
self.name = name
self._age = age
self.__secret = secret
u = User("Alice", 30, "password123")
print(u.name)
print(u._age)
print(u._User__secret)
class Parent:
def __init__(self):
self.__value = "parent"
def __method(self):
print("parent method")
def public_method(self):
self.__method()
class Child(Parent):
def __init__(self):
super().__init__()
self.__value = "child"
def __method(self):
print("child method")
c = Child()
c.public_method()
print(c._Parent__value)
print(c._Child__value)
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def celsius(self):
"""getter — 像属性一样访问"""
return self._celsius
@celsius.setter
def celsius(self, value):
"""setter — 像属性一样赋值,但可以加验证"""
if value < -273.15:
raise ValueError("温度不能低于绝对零度")
self._celsius = value
@property
def fahrenheit(self):
"""只读计算属性 — 没有 setter"""
return self._celsius * 9 / 5 + 32
@property
def kelvin(self):
"""只读计算属性"""
return self._celsius + 273.15
t = Temperature(100)
print(t.celsius)
print(t.fahrenheit)
t.celsius = 0
print(t.fahrenheit)
class Circle:
def __init__(self, radius):
self.radius = radius
self._area = None
@property
def area(self):
if self._area is None:
print("计算面积...")
self._area = 3.14159 * self.radius ** 2
return self._area
c = Circle(5)
print(c.area)
print(c.area)
c.radius = 10
核心差异
| 维度 | Java/Kotlin | Python |
|---|
| 访问控制 | 语言强制(private, protected, public) | 无强制,只有约定(_, __) |
_ 单下划线 | 无等价物 | 约定私有,工具可警告但不阻止 |
__ 双下划线 | 无等价物 | Name mangling,防止子类意外覆盖 |
| Getter/Setter | 必须写方法 | @property 让属性访问和方法的验证逻辑共存 |
| 设计哲学 | "我们都是成年人" → 编译器强制 | "我们都是成年人" → 信任开发者 |
常见陷阱
class BankAccount:
def __init__(self, balance):
self.__balance = balance
a = BankAccount(1000)
a.__balance = 0
print(a.__balance)
print(a._BankAccount__balance)
a._BankAccount__balance = 0
print(a._BankAccount__balance)
何时使用
- 无前缀:公开 API,外部应该使用的属性
_ 单下划线:内部实现细节,外部不应该使用(但可以)
__ 双下划线:仅当需要防止子类命名冲突时使用(不常见)
@property:需要验证逻辑、计算属性、或未来可能需要加验证时
4.5 多继承与 MRO (C3 线性化)
Java/Kotlin 对比
public interface Flyable {
void fly();
}
public interface Swimmable {
void swim();
}
public class Duck implements Flyable, Swimmable {
@Override
public void fly() { System.out.println("Flying"); }
@Override
public void swim() { System.out.println("Swimming"); }
}
interface Flyable { fun fly() }
interface Swimmable { fun swim() }
class Duck : Flyable, Swimmable {
override fun fly() = println("Flying")
override fun swim() = println("Swimming")
}
Python 实现
class Flyable:
def fly(self):
print(f"{self.__class__.__name__} is flying")
class Swimmable:
def swim(self):
print(f"{self.__class__.__name__} is swimming")
class Duck(Flyable, Swimmable):
pass
d = Duck()
d.fly()
d.swim()
class A:
def greet(self):
print("A")
class B(A):
def greet(self):
print("B")
super().greet()
class C(A):
def greet(self):
print("C")
super().greet()
class D(B, C):
def greet(self):
print("D")
super().greet()
d = D()
d.greet()
print(D.__mro__)
class X: pass
class Y: pass
class A(X, Y): pass
class B(Y, X): pass
class Base:
def __init__(self):
print("Base.__init__")
super().__init__()
class Left(Base):
def __init__(self):
print("Left.__init__")
super().__init__()
class Right(Base):
def __init__(self):
print("Right.__init__")
super().__init__()
class Child(Left, Right):
def __init__(self):
print("Child.__init__")
super().__init__()
print(Child.__mro__)
c = Child()
class LoggerMixin:
"""混入类:给任何类添加日志能力"""
def __init__(self, *args, **kwargs):
print(f"[LOG] 初始化 {self.__class__.__name__}")
super().__init__(*args, **kwargs)
class ValidatorMixin:
"""混入类:给任何类添加验证能力"""
def validate(self, data):
print(f"[VALIDATE] 验证 {data}")
return True
class BaseModel(LoggerMixin):
def __init__(self, **kwargs):
super().__init__()
for k, v in kwargs.items():
setattr(self, k, v)
class UserModel(BaseModel, ValidatorMixin):
pass
u = UserModel(name="Alice", age=30)
u.validate({"name": "Bob"})
核心差异
| 维度 | Java/Kotlin | Python |
|---|
| 多继承 | 禁止(只能多接口) | 完全支持 |
| 菱形继承 | 不存在(单继承) | MRO (C3 线性化) 解决 |
super | 指向直接父类 | 沿 MRO 链调用下一个 |
| 接口 | interface 关键字 | 用无实现的类或 ABC 模拟 |
| Mixin | 无原生支持 | 天然支持,通过多继承实现 |
常见陷阱
class Base:
def __init__(self):
self.value = 42
class Child(Base):
def __init__(self):
pass
c = Child()
class A:
def __init__(self, a):
self.a = a
class B:
def __init__(self, b):
self.b = b
class C(A, B):
def __init__(self, a, b):
super().__init__(a, b)
何时使用
- 单继承:大多数情况,和 Java 一样
- Mixin:添加可复用的功能(日志、序列化、验证等)
- 避免:复杂的多继承层次(超过 2 层)
super():在多继承中必须用无参 super(),不要写 super(ParentClass, self)
4.6 魔术方法 (Dunder Methods)
Java/Kotlin 对比
public class User {
private String name;
private int age;
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return age == user.age && name.equals(user.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
data class User(val name: String, val age: Int)
Python 实现
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
"""面向用户的字符串表示 → Java toString()"""
return f"User(name={self.name!r}, age={self.age})"
def __repr__(self):
"""面向开发者的字符串表示 → 用于调试"""
return f"User({self.name!r}, {self.age})"
u = User("Alice", 30)
print(str(u))
print(repr(u))
print(u)
print([u])
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
"""等价于 Java equals()"""
if not isinstance(other, Point):
return NotImplemented
return self.x == other.x and self.y == other.y
def __hash__(self):
"""等价于 Java hashCode()"""
return hash((self.x, self.y))
p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(3, 4)
print(p1 == p2)
print(p1 == p3)
print(p1 != p3)
points = {p1, p2, p3}
print(len(points))
point_map = {p1: "origin"}
print(point_map[p2])
class DefaultUser:
def __init__(self, name):
self.name = name
u1 = DefaultUser("Alice")
u2 = DefaultUser("Alice")
print(u1 == u2)
print(u1)
class Score:
def __init__(self, value):
self.value = value
def __bool__(self):
"""真值判断 → Java 没有,Kotlin 的 operator fun Boolean"""
return self.value > 60
def __len__(self):
"""len() 函数 → Java size()"""
return abs(self.value)
s = Score(80)
if s:
print("及格")
print(len(s))
s = Score(40)
if not s:
print("不及格")
class Countdown:
"""Java: 实现 Iterator<E> 接口
Python: 实现 __iter__ 和 __next__"""
def __init__(self, start):
self.current = start
def __iter__(self):
"""返回迭代器对象(通常是 self)"""
return self
def __next__(self):
"""返回下一个值,没有更多元素时抛 StopIteration"""
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
for i in Countdown(5):
print(i, end=" ")
c = Countdown(3)
it = iter(c)
print(next(it))
print(next(it))
print(next(it))
def countdown(start):
while start > 0:
yield start
start -= 1
for i in countdown(5):
print(i, end=" ")
class Multiplier:
"""让实例像函数一样调用 — Java/Kotlin 没有等价物"""
def __init__(self, factor):
self.factor = factor
def __call__(self, x):
return x * self.factor
double = Multiplier(2)
triple = Multiplier(3)
print(double(5))
print(triple(5))
print(callable(double))
class Timer:
"""用 __call__ 实现装饰器"""
import time
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
import time
start = time.perf_counter()
result = self.func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{self.func.__name__} 耗时 {elapsed:.4f}s")
return result
@Timer
def slow_function():
import time
time.sleep(0.1)
return "done"
print(slow_function())
核心差异
| 维度 | Java/Kotlin | Python |
|---|
| 字符串表示 | toString() | __str__(用户)+ __repr__(开发者) |
| 相等比较 | equals() + hashCode() | __eq__ + __hash__ |
| 真值判断 | 无(必须显式判断) | __bool__ / __len__ |
| 迭代 | Iterator<E> 接口 | __iter__ + __next__ |
| 可调用对象 | 不支持(Lambda 是函数不是对象) | __call__ 让实例可调用 |
| 默认行为 | equals 比较引用 | __eq__ 默认比较 id |
常见陷阱
class Broken:
def __init__(self, value):
self.value = value
def __eq__(self, other):
return isinstance(other, Broken) and self.value == other.value
class BadEq:
def __eq__(self, other):
if not isinstance(other, BadEq):
return False
何时使用
__repr__:永远实现,用于调试
__str__:需要面向用户的字符串表示时
__eq__ + __hash__:对象需要比较或放入 set/dict 时
__bool__:对象需要在 if 语句中有语义时
__iter__ + __next__:需要自定义迭代行为时(但优先考虑生成器)
__call__:需要"有状态的函数"时(装饰器、策略模式、回调)
4.7 运算符重载
Java/Kotlin 对比
String s = "a" + "b";
data class Vec2(val x: Double, val y: Double) {
operator fun plus(other: Vec2) = Vec2(x + other.x, y + other.y)
operator fun minus(other: Vec2) = Vec2(x - other.x, y - other.y)
operator fun times(scalar: Double) = Vec2(x * scalar, y * scalar)
operator fun unaryMinus() = Vec2(-x, -y)
}
Python 实现
class Vec2:
"""二维向量 — 和 Kotlin 的 Vec2 对比"""
def __init__(self, x: float, y: float):
self.x = x
self.y = y
def __add__(self, other):
"""+ 运算符"""
if isinstance(other, Vec2):
return Vec2(self.x + other.x, self.y + other.y)
return NotImplemented
def __sub__(self, other):
"""- 运算符"""
if isinstance(other, Vec2):
return Vec2(self.x - other.x, self.y - other.y)
return NotImplemented
def __mul__(self, scalar):
"""* 运算符(向量 * 标量)"""
if isinstance(scalar, (int, float)):
return Vec2(self.x * scalar, self.y * scalar)
return NotImplemented
def __rmul__(self, scalar):
"""反射乘法(标量 * 向量)"""
return self.__mul__(scalar)
def __neg__(self):
"""一元 - 运算符"""
return Vec2(-self.x, -self.y)
def __abs__(self):
"""abs() 内置函数"""
return (self.x ** 2 + self.y ** 2) ** 0.5
def __eq__(self, other):
if isinstance(other, Vec2):
return self.x == other.x and self.y == other.y
return NotImplemented
def __repr__(self):
return f"Vec2({self.x}, {self.y})"
v1 = Vec2(1, 2)
v2 = Vec2(3, 4)
print(v1 + v2)
print(v1 - v2)
print(v1 * 3)
print(3 * v1)
print(-v1)
print(abs(v1))
class Matrix:
"""简化版矩阵 — 支持 m[i][j] 语法"""
def __init__(self, rows, cols, default=0):
self._data = [[default] * cols for _ in range(rows)]
self.rows = rows
self.cols = cols
def __getitem__(self, key):
"""m[i] 返回第 i 行"""
return self._data[key]
def __setitem__(self, key, value):
"""m[i] = [...] 设置第 i 行"""
self._data[key] = value
def __len__(self):
"""len(m) 返回行数"""
return self.rows
def __repr__(self):
return f"Matrix({self._data})"
m = Matrix(2, 3)
m[0] = [1, 2, 3]
m[1] = [4, 5, 6]
print(m[0])
print(m[1][2])
print(len(m))
class IntSet:
def __init__(self):
self._items = set()
def add(self, value):
self._items.add(value)
def __contains__(self, value):
"""'value in s' 语法"""
return value in self._items
s = IntSet()
s.add(1)
s.add(2)
print(1 in s)
print(3 in s)
class Timer:
"""计时上下文管理器"""
import time
def __enter__(self):
import time
self._start = time.perf_counter()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
import time
elapsed = time.perf_counter() - self._start
print(f"耗时 {elapsed:.4f}s")
return False
with Timer():
import time
time.sleep(0.1)
class DBConnection:
def __init__(self, url):
self.url = url
self.connected = False
def connect(self):
print(f"连接到 {self.url}")
self.connected = True
def disconnect(self):
print("断开连接")
self.connected = False
def query(self, sql):
if not self.connected:
raise RuntimeError("未连接")
print(f"执行: {sql}")
def __enter__(self):
self.connect()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.disconnect()
if exc_type is not None:
print(f"发生异常: {exc_val}")
return False
with DBConnection("postgresql://localhost/mydb") as conn:
conn.query("SELECT * FROM users")
from contextlib import contextmanager
@contextmanager
def db_connection(url):
conn = DBConnection(url)
conn.connect()
try:
yield conn
finally:
conn.disconnect()
with db_connection("postgresql://localhost/mydb") as conn:
conn.query("SELECT 1")
核心差异
| 维度 | Java/Kotlin | Python |
|---|
| 运算符重载 | Java 不支持;Kotlin 用 operator | Python 用 dunder 方法 |
| 反射运算符 | 不需要 | __radd__, __rmul__ 等处理左操作数不支持的情况 |
| 索引访问 | 不支持(用 get()/set()) | __getitem__/__setitem__ |
| 上下文管理 | AutoCloseable + try-with-resources | __enter__/__exit__ + with |
| 资源清理 | finally 块 | with 语句(更优雅) |
常见陷阱
class MyDict:
def __getitem__(self, key):
return self._data[key]
class Dangerous:
def __enter__(self): return self
def __exit__(self, *args): return True
with Dangerous():
1 / 0
print("这行会执行")
何时使用
- 算术运算符:数学类型(向量、矩阵、复数、分数)
__getitem__/__setitem__:自定义容器类型
__contains__:自定义集合类型
__enter__/__exit__:任何需要资源管理的场景(文件、连接、锁)
contextmanager:简单的上下文逻辑,比写类更简洁
4.8 @property: Pythonic 的 getter/setter
Java/Kotlin 对比
public class Rectangle {
private double width;
private double height;
public double getWidth() { return width; }
public void setWidth(double width) {
if (width <= 0) throw new IllegalArgumentException("宽度必须为正");
this.width = width;
}
public double getHeight() { return height; }
public void setHeight(double height) {
if (height <= 0) throw new IllegalArgumentException("高度必须为正");
this.height = height;
}
public double getArea() { return width * height; }
}
class Rectangle(var width: Double, var height: Double) {
var area: Double
get() = width * height
init {
require(width > 0) { "宽度必须为正" }
require(height > 0) { "高度必须为正" }
}
}
Python 实现
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def width(self):
"""getter"""
return self._width
@width.setter
def width(self, value):
"""setter — 带验证"""
if value <= 0:
raise ValueError("宽度必须为正")
self._width = value
@property
def height(self):
return self._height
@height.setter
def height(self, value):
if value <= 0:
raise ValueError("高度必须为正")
self._height = value
@property
def area(self):
"""只读计算属性 — 没有 setter"""
return self._width * self._height
@property
def perimeter(self):
"""只读计算属性"""
return 2 * (self._width + self._height)
r = Rectangle(3, 4)
print(r.width)
print(r.area)
print(r.perimeter)
r.width = 5
print(r.area)
class UserV1:
def __init__(self, name):
self.name = name
u1 = UserV1("Alice")
print(u1.name)
class UserV2:
def __init__(self, name):
self.name = name
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not value or not value.strip():
raise ValueError("名字不能为空")
self._name = value.strip()
u2 = UserV2("Alice")
print(u2.name)
class Config:
def __init__(self, db_url):
self._db_url = db_url
@property
def db_url(self):
"""只读 — 外部无法修改"""
return self._db_url
config = Config("postgresql://localhost/mydb")
print(config.db_url)
class CachedData:
def __init__(self):
self._cache = {}
@property
def cache(self):
return self._cache
@cache.deleter
def cache(self):
"""del obj.cache 时调用"""
print("清除缓存")
self._cache.clear()
data = CachedData()
data.cache["key"] = "value"
print(data.cache)
del data.cache
print(data.cache)
class HeavyResource:
def __init__(self):
self._data = None
@property
def data(self):
if self._data is None:
print("加载重量级数据...")
self._data = [i ** 2 for i in range(100000)]
return self._data
resource = HeavyResource()
print("资源已创建,数据尚未加载")
print(len(resource.data))
print(len(resource.data))
核心差异
| 维度 | Java | Kotlin | Python |
|---|
| Getter/Setter | 必须写方法 | property 语法糖 | @property 装饰器 |
| 访问方式 | getWidth() | .width | .width |
| 只读属性 | 只写 getter | val / 无 setter | 只写 @property 不写 setter |
| 计算属性 | getArea() 方法 | val area get() = ... | @property 无 setter |
| 从公开属性迁移 | 必须改调用方 | 不需要(本来就是属性) | 不需要(@property 兼容属性访问) |
常见陷阱
class User:
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not value:
raise ValueError("名字不能为空")
self._name = value
def __init__(self, name):
self.name = name
class Broken:
@property
def x(self):
return self._x
def __init__(self):
self.x = 10
何时使用
- 需要验证:setter 中加校验逻辑
- 计算属性:依赖其他属性动态计算
- 只读属性:只提供 getter 不提供 setter
- 懒加载:首次访问时计算并缓存
- 向后兼容:从公开属性迁移到受控访问,不破坏调用方
4.9 slots: 内存优化与属性锁定
Java/Kotlin 对比
public class Point {
private int x;
private int y;
}
data class Point(val x: Int, val y: Int)
Python 实现
class NormalPoint:
def __init__(self, x, y):
self.x = x
self.y = y
p = NormalPoint(1, 2)
print(p.__dict__)
p.z = 3
print(p.__dict__)
print(hasattr(p, '__dict__'))
class SlottedPoint:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
p = SlottedPoint(1, 2)
print(p.x, p.y)
print(hasattr(p, '__dict__'))
import sys
class Normal:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
class Slotted:
__slots__ = ('x', 'y', 'z')
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
n = Normal(1, 2, 3)
s = Slotted(1, 2, 3)
print(f"Normal 对象大小: {sys.getsizeof(n)} bytes")
print(f"Slotted 对象大小: {sys.getsizeof(s)} bytes")
normal_objects = [Normal(i, i+1, i+2) for i in range(100000)]
slotted_objects = [Slotted(i, i+1, i+2) for i in range(100000)]
import tracemalloc
tracemalloc.start()
normal_objects = [Normal(i, i+1, i+2) for i in range(100000)]
_, normal_size = tracemalloc.get_traced_memory()
tracemalloc.stop()
tracemalloc.start()
slotted_objects = [Slotted(i, i+1, i+2) for i in range(100000)]
_, slotted_size = tracemalloc.get_traced_memory()
tracemalloc.stop()
print(f"Normal 100K 对象: {normal_size / 1024 / 1024:.1f} MB")
print(f"Slotted 100K 对象: {slotted_size / 1024 / 1024:.1f} MB")
print(f"节省: {(1 - slotted_size / normal_size) * 100:.1f}%")
class Base:
__slots__ = ('x',)
class Child(Base):
__slots__ = ('y',)
c = Child()
c.x = 1
c.y = 2
class LooseChild(Base):
pass
lc = LooseChild()
lc.x = 1
lc.z = 99
核心差异
| 维度 | Java/Kotlin | Python (默认) | Python (slots) |
|---|
| 动态属性 | 不支持 | 支持(__dict__) | 不支持 |
| 内存开销 | 固定 | 较大(__dict__ 哈希表) | 较小(描述符数组) |
| 属性访问速度 | 快 | 中等(哈希查找) | 快(描述符查找) |
| 序列化 | 框架处理 | __dict__ 直接序列化 | 需要显式处理 |
常见陷阱
class Broken:
__slots__ = ('x', 'y')
z = 0
import weakref
class Slotted:
__slots__ = ('x',)
s = Slotted()
class WeakSlotted:
__slots__ = ('x', '__weakref__')
s = WeakSlotted()
ref = weakref.ref(s)
何时使用
- 大量对象(数万以上):显著减少内存
- 属性固定:不需要动态添加属性的场景
- 性能敏感:属性访问更快
- 不适用:需要动态属性、或与依赖
__dict__ 的库(如某些 ORM)配合时
4.10 抽象基类 ABC vs 接口/抽象类
Java/Kotlin 对比
public interface Drawable {
void draw();
default void resize(double f) {
System.out.println("默认缩放");
}
}
public abstract class Shape implements Drawable {
protected String color;
public Shape(String color) {
this.color = color;
}
public abstract double area();
public String getColor() { return color; }
}
public class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public void draw() { System.out.println("画圆"); }
@Override
public double area() { return Math.PI * radius * radius; }
}
interface Drawable {
fun draw()
fun resize(f: Double) { println("默认缩放") }
}
abstract class Shape(val color: String) : Drawable {
abstract fun area(): Double
}
class Circle(color: String, val radius: Double) : Shape(color) {
override fun draw() = println("画圆")
override fun area() = Math.PI * radius * radius
}
Python 实现
from abc import ABC, abstractmethod
class Drawable(ABC):
"""类似 Java interface"""
@abstractmethod
def draw(self):
"""抽象方法:子类必须实现"""
pass
def resize(self, factor):
"""具体方法:子类可以直接使用(类似 Java default method)"""
print(f"默认缩放 {factor}x")
class Shape(Drawable, ABC):
"""抽象类:有构造函数和状态"""
def __init__(self, color):
self.color = color
@abstractmethod
def area(self):
"""抽象方法"""
pass
class Circle(Shape):
"""具体类"""
def __init__(self, color, radius):
super().__init__(color)
self.radius = radius
def draw(self):
print(f"画一个 {self.color} 的圆")
def area(self):
import math
return math.pi * self.radius ** 2
c = Circle("red", 5)
c.draw()
print(c.area())
c.resize(2)
class Base(ABC):
@property
@abstractmethod
def name(self):
"""抽象只读属性"""
pass
@name.setter
@abstractmethod
def name(self, value):
"""抽象可写属性"""
pass
@abstractmethod
def greet(self):
pass
class Impl(Base):
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
def greet(self):
print(f"Hello, {self._name}")
class Plugin(ABC):
@classmethod
@abstractmethod
def from_config(cls, config):
"""抽象类方法:子类必须实现"""
pass
class MyPlugin(Plugin):
@classmethod
def from_config(cls, config):
return cls(config.get("name"))
class Animal(ABC):
@abstractmethod
def speak(self):
pass
@classmethod
def register(cls, subclass):
"""手动注册虚拟子类"""
print(f"注册: {subclass.__name__}")
class Robot:
def speak(self):
print("Beep boop")
Animal.register(Robot)
print(isinstance(Robot(), Animal))
核心差异
| 维度 | Java | Kotlin | Python |
|---|
| 接口 | interface | interface | ABC + @abstractmethod |
| 抽象类 | abstract class | abstract class | ABC 子类 |
| 默认实现 | default 方法 | 直接实现 | 普通方法 |
| 抽象属性 | 无 | 无 | @property + @abstractmethod |
| 多继承 | 多接口 | 多接口 | 多 ABC |
| 实例化检查 | 编译时 | 编译时 | 运行时(TypeError) |
| 虚拟子类 | 无 | 无 | register() |
常见陷阱
class Base(ABC):
@property
@abstractmethod
def x(self):
pass
何时使用
ABC + @abstractmethod:需要强制子类实现某些方法时
- 抽象属性:需要强制子类提供某些属性时
register():需要让第三方类被视为子类时(鸭子类型 + 类型检查)
- vs
Protocol:需要运行时实例化检查用 ABC;只需要静态类型检查用 Protocol
4.11 Protocol (3.8+): 结构化子类型
Java/Kotlin 对比
public interface Drawable {
void draw();
}
public class Circle {
public void draw() { System.out.println("画圆"); }
}
interface Drawable {
fun draw()
}
class Circle {
fun draw() = println("画圆")
}
Python 实现
from typing import Protocol, runtime_checkable
class Drawable(Protocol):
"""结构化子类型:只要类有 draw() 方法,就是 Drawable
不需要显式继承或声明!"""
def draw(self) -> None:
...
class Circle:
def draw(self):
print("画圆")
class Square:
def draw(self):
print("画正方形")
class Text:
def render(self):
print("渲染文本")
def render(obj: Drawable):
"""接受任何有 draw() 方法的对象"""
obj.draw()
render(Circle())
render(Square())
@runtime_checkable
class Closeable(Protocol):
def close(self) -> None:
...
class File:
def close(self):
print("关闭文件")
class Socket:
def close(self):
print("关闭连接")
class NotCloseable:
pass
print(isinstance(File(), Closeable))
print(isinstance(Socket(), Closeable))
print(isinstance(NotCloseable(), Closeable))
class BadClose:
def close(self, x, y):
pass
print(isinstance(BadClose(), Closeable))
class Named(Protocol):
name: str
class User:
def __init__(self, name):
self.name = name
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def get_name(obj: Named) -> str:
return obj.name
print(get_name(User("Alice")))
print(get_name(Product("Book", 10)))
from abc import ABC, abstractmethod
class DrawableABC(ABC):
@abstractmethod
def draw(self):
pass
class DrawableProtocol(Protocol):
def draw(self):
...
class MyCircle:
def draw(self):
print("画圆")
isinstance(MyCircle(), DrawableProtocol)
class Readable(Protocol):
def read(self, size: int = -1) -> bytes:
...
class Writable(Protocol):
def write(self, data: bytes) -> int:
...
class ReadWrite(Protocol):
"""组合 Protocol"""
def read(self, size: int = -1) -> bytes:
...
def write(self, data: bytes) -> int:
...
class Buffer:
def read(self, size=-1):
return b"data"
def write(self, data):
return len(data)
def process(rw: ReadWrite):
rw.write(rw.read())
process(Buffer())
核心差异
| 维度 | Java/Kotlin | Python ABC | Python Protocol |
|---|
| 子类型关系 | 名义子类型(显式声明) | 名义子类型 | 结构化子类型(隐式) |
| 检查时机 | 编译时 | 运行时(实例化) | 静态类型检查 + 可选运行时 |
| 第三方类 | 必须修改源码或继承 | 用 register() | 自动匹配 |
| 设计哲学 | "声明你是什么" | "声明你是什么" | "你有什么能力" |
常见陷阱
class Bad(Protocol):
def method(self):
print("这行不会被执行")
@runtime_checkable
class HasName(Protocol):
name: str
class NoName:
def __init__(self):
self.name = "test"
何时使用
Protocol:定义接口但不要求显式继承,特别是处理第三方库的类时
@runtime_checkable:需要在运行时做 isinstance 检查时
ABC:需要强制子类实现方法,且需要运行时实例化保护时
- 经验法则:优先用
Protocol(更灵活),需要运行时保护时用 ABC
4.12 init_subclass 与类钩子
Java/Kotlin 对比
public interface Plugin {
String name();
void execute();
}
Python 实现
class PluginBase:
"""插件基类:自动注册所有子类"""
_registry: dict[str, type] = {}
def __init_subclass__(cls, **kwargs):
"""每当有类继承 PluginBase 时,这个方法会被自动调用"""
super().__init_subclass__(**kwargs)
PluginBase._registry[cls.__name__] = cls
print(f"[注册] 插件 {cls.__name__}")
@classmethod
def get_plugin(cls, name: str):
"""按名称获取插件类"""
plugin_cls = cls._registry.get(name)
if plugin_cls is None:
raise KeyError(f"未知插件: {name}")
return plugin_cls
@classmethod
def list_plugins(cls):
"""列出所有已注册插件"""
return list(cls._registry.keys())
class EmailPlugin(PluginBase):
def send(self, to, message):
print(f"发送邮件给 {to}: {message}")
class SMSPlugin(PluginBase):
def send(self, to, message):
print(f"发送短信给 {to}: {message}")
class PushPlugin(PluginBase):
def send(self, to, message):
print(f"发送推送给 {to}: {message}")
print(PluginBase.list_plugins())
plugin_cls = PluginBase.get_plugin("SMSPlugin")
plugin = plugin_cls()
plugin.send("13800138000", "验证码: 1234")
class Validator:
"""验证器基类:子类声明自己验证的字段"""
def __init_subclass__(cls, field: str = None, **kwargs):
super().__init_subclass__(**kwargs)
if field:
cls._validated_field = field
print(f"验证器 {cls.__name__} 监控字段: {field}")
def validate(self, data):
field = getattr(self, '_validated_field', None)
if field and field not in data:
raise ValueError(f"缺少必填字段: {field}")
return True
class UserValidator(Validator, field="username"):
pass
class EmailValidator(Validator, field="email"):
pass
uv = UserValidator()
uv.validate({"username": "alice"})
class EventEmitter:
"""事件发射器:子类自动注册事件处理器"""
_handlers: dict[str, dict[str, callable]] = {}
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
for attr_name in dir(cls):
if attr_name.startswith("on_"):
event_name = attr_name[3:]
handler = getattr(cls, attr_name)
if callable(handler):
if event_name not in EventEmitter._handlers:
EventEmitter._handlers[event_name] = {}
EventEmitter._handlers[event_name][cls.__name__] = handler
print(f"[事件] {cls.__name__}.on_{event_name} 已注册")
@classmethod
def emit(cls, event_name: str, *args, **kwargs):
"""触发事件"""
handlers = cls._handlers.get(event_name, {})
for name, handler in handlers.items():
handler(None, *args, **kwargs)
class UserEvents(EventEmitter):
def on_login(self, user_id):
print(f"[{self.__class__.__name__}] 用户 {user_id} 登录")
def on_logout(self, user_id):
print(f"[{self.__class__.__name__}] 用户 {user_id} 登出")
class AdminEvents(EventEmitter):
def on_login(self, user_id):
print(f"[{self.__class__.__name__}] 管理员 {user_id} 登录(需审计)")
EventEmitter.emit("login", 1001)
EventEmitter.emit("logout", 1001)
核心差异
| 维度 | Java/Kotlin | Python |
|---|
| 类定义钩子 | 无(注解处理器是编译时) | __init_subclass__(运行时) |
| 插件注册 | SPI / 反射扫描 / 手动注册 | 自动注册,子类定义即注册 |
| 元编程 | 注解处理器 / KSP | __init_subclass__ / 元类 / 描述符 |
| 复杂度 | 高(需要额外工具链) | 低(纯 Python,无需额外工具) |
常见陷阱
class Base:
def __init_subclass__(cls):
print(f"定义子类: {cls.__name__}")
class Middle(Base):
def __init_subclass__(cls, **kwargs):
pass
何时使用
- 插件系统:自动发现和注册插件
- 框架开发:Django REST Framework 的序列化器、Pydantic 的模型验证
- 事件系统:自动注册事件处理器
- 约束检查:确保子类满足某些条件
- 经验法则:能用
__init_subclass__ 就不用元类
本章总结
| 知识点 | Java/Kotlin 等价物 | Python 关键区别 |
|---|
self | this(隐式) | 显式参数,不是关键字 |
__init__ | 构造函数 | 只是初始化器,__new__ 才是构造器 |
| 类属性 | static 字段 | 可被实例属性遮蔽,可变对象共享陷阱 |
_ / __ | private / protected | 只是约定,__ 触发 name mangling |
| 多继承 | 禁止 | 支持,MRO (C3) 解决菱形继承 |
super() | 指向父类 | 沿 MRO 链调用下一个 |
| 魔术方法 | toString()/equals() 等 | 更丰富,__call__/__iter__ 等 |
| 运算符重载 | Java 不支持/Kotlin operator | dunder 方法,更灵活 |
@property | getter/setter | 属性访问语法 + 验证逻辑 |
__slots__ | 无等价物 | 内存优化 + 属性锁定 |
| ABC | interface/abstract class | @abstractmethod,运行时检查 |
| Protocol | 无等价物 | 结构化子类型,鸭子类型的静态版本 |
__init_subclass__ | 无等价物 | 类定义钩子,插件系统的优雅实现 |
一句话总结:Python 的类系统表面看起来和 Java/Kotlin 类似,但底层机制完全不同。理解 self 的本质、__new__/__init__ 的分离、MRO 线性化、以及描述符协议,是避免踩坑的关键。