类、继承、多态:Python的OOP与Java有哪些不同
摘要:Python和Java都是面向对象语言,但设计理念不同。Java强调类型安全和单继承,Python更灵活,支持多继承。
写在前面
作为一个Java工程师,你一定对OOP(面向对象编程)了如指掌。但用Java的思维去写Python时,会遇到很多"奇怪"的问题:
- 为什么Python的类变量会被实例修改?
- 为什么子类的方法会覆盖父类的方法?
- Python居然支持多继承?
这篇文章帮你建立Python的OOP思维,避免常见的陷阱。
一、类定义对比
1.1 基本语法
// Java - 一切皆需声明
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void sayHello() {
System.out.println("Hello, I'm " + name);
}
}
# Python - 简洁太多
class Person:
def __init__(self, name: str, age: int):
self.name = name # self相当于Java的this
self.age = age
def say_hello(self):
print(f"Hello, I'm {self.name}")
# 创建实例
p = Person("Alice", 30)
p.say_hello()
1.2 访问控制对比
| Java | Python | 说明 |
|---|---|---|
public | 无修饰符 | 公开访问 |
private | __双下划线 | 名称改写(name mangling) |
protected | _单下划线 | 约定不可外部访问 |
final | 无直接对应 | 常量用全大写约定 |
# Python 访问控制 - 纯约定,没有强制
class Person:
def __init__(self, name, age):
self.name = name # 公开属性
self._age = age # 约定:受保护
self.__secret = "data" # 名称改写为 _Person__secret
p = Person("Alice", 30)
print(p.name) # Alice - 可访问
print(p._age) # 30 - 不推荐但可访问
# print(p.__secret) # AttributeError - 实际被改名为 _Person__secret
print(p._Person__secret) # data - 硬编码可访问,但不推荐
// Java - 真正的访问控制
public class Person {
private String secret = "data"; // 真正无法外部访问
}
1.3 属性与方法对比
| 特性 | Java | Python |
|---|---|---|
| 实例变量 | 在构造器/类中声明 | 在__init__中用self.变量名 |
| 类变量 | static变量 | 类体内直接定义 |
| 实例方法 | 普通方法 | 普通方法,第一个参数是self |
| 静态方法 | @Staticmethod | @staticmethod |
| 类方法 | 无原生支持(用static) | @classmethod,第一个参数是cls |
class Circle:
pi = 3.14159 # 类变量 - 所有实例共享
def __init__(self, radius):
self.radius = radius # 实例变量
def area(self): # 实例方法
return Circle.pi * self.radius ** 2
@staticmethod
def unit_circle():
return Circle(1)
@classmethod
def from_diameter(cls, diameter):
return cls(diameter / 2)
二、继承对比
2.1 单继承
// Java - extends关键字
public class Animal {
public void eat() {
System.out.println("Eating");
}
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("Dog is eating");
}
public void bark() {
System.out.println("Woof!");
}
}
# Python - 括号里写父类
class Animal:
def eat(self):
print("Eating")
class Dog(Animal):
def eat(self):
print("Dog is eating")
def bark(self):
print("Woof!")
d = Dog()
d.eat() # Dog is eating
d.bark() # Woof!
2.2 多继承(Python支持!Java不支持!)
这是两者最大的区别之一。
// Java - 不支持多继承
// 只能用接口
public interface Runable {
void run();
}
public interface Swimmable {
void swim();
}
// Java 8+ 可以多接口
public class Duck implements Runable, Swimmable {
@Override
public void run() { }
@Override
public void swim() { }
}
# Python - 支持真正的多继承
class Runable:
def run(self):
print("Running")
class Swimmable:
def swim(self):
print("Swimming")
class Duck(Runable, Swimmable):
pass
duck = Duck()
duck.run() # Running
duck.swim() # Swimming
2.3 MRO(方法解析顺序)
Python多继承时,如果父类有同名方法,需要了解MRO。
class A:
def greet(self):
print("A greet")
class B(A):
def greet(self):
print("B greet")
class C(A):
def greet(self):
print("C greet")
class D(B, C): # D继承了B和C
pass
d = D()
d.greet() # 打印什么?B greet
# 查看MRO
print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>,
# <class '__main__.A'>, <class 'object'>)
2.4 super()对比
// Java - super调用父类方法
public class Dog extends Animal {
public Dog(String name) {
super(name); // 调用父类构造器
}
@Override
public void eat() {
super.eat(); // 调用父类方法
System.out.println("Dog finished eating");
}
}
# Python - super()更灵活
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调用父类构造器
self.breed = breed
def eat(self):
super().eat() # 调用父类方法
print("Dog finished eating")
Python 3+ 的super():可以不用传self,Python会自动处理。
class Dog(Animal):
def eat(self):
super().eat() # 不传self也可以!
print("Dog finished eating")
三、多态对比
3.1 运行时多态
// Java - 编译时确定方法签名,运行时多态
public void makeSound(Animal animal) {
animal.eat(); // 运行时确定调用哪个子类的方法
}
Dog dog = new Dog();
Cat cat = new Cat();
makeSound(dog); // Dog eat
makeSound(cat); // Cat eat
# Python - 天然多态,无需声明
def make_sound(animal):
animal.eat() # 运行时确定
dog = Dog()
cat = Cat()
make_sound(dog) # Dog is eating
make_sound(cat) # Cat is eating
3.2 抽象类/接口
// Java - 抽象类
public abstract class Shape {
public abstract double area();
public void print_area() {
System.out.println("Area: " + area());
}
}
// Java - 接口
public interface Drawable {
void draw();
}
# Python - 抽象基类
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
def print_area(self):
print(f"Area: {self.area()}")
# Python 没有interface关键字,抽象基类既当接口又当抽象类
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
四、数据类对比
4.1 Java的Record(Java 16+)
// Java - Record 不可变数据类
public record Person(String name, int age) {
// 自动生成:构造器、getter、equals、hashCode、toString
}
// 使用
Person p = new Person("Alice", 30);
System.out.println(p.name()); // 注意:不是getName()
System.out.println(p); // Person[name=Alice, age=30]
4.2 Python的dataclass
# Python 3.7+ dataclass
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
# 自动生成:__init__、__repr__、__eq__、__hashCode__
p = Person("Alice", 30)
print(p) # Person(name='Alice', age=30)
print(p.name) # Alice
print(p == Person("Alice", 30)) # True
4.3 dataclass高级特性
from dataclasses import dataclass, field
from typing import List
@dataclass
class Student:
name: str
grades: List[int] = field(default_factory=list) # 可变默认值
avg_grade: float = field(init=False) # 不在构造器中
def __post_init__(self):
self.avg_grade = sum(self.grades) / len(self.grades) if self.grades else 0
s = Student("Alice", [90, 85, 88])
print(s) # Student(name='Alice', grades=[90, 85, 88], avg_grade=87.666...)
五、特殊方法(魔术方法)
5.1 Java的Object方法
public class Person {
private String name;
@Override
public String toString() {
return "Person{name='" + name + "'}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
5.2 Python的特殊方法
class Person:
def __init__(self, name):
self.name = name
def __str__(self): # str()调用
return f"Person(name='{self.name}')"
def __repr__(self): # 开发调试用
return f"Person(name='{self.name}')"
def __eq__(self, other): # ==
if isinstance(other, Person):
return self.name == other.name
return False
def __hash__(self): # hash()
return hash(self.name)
def __len__(self): # len()
return len(self.name)
def __call__(self): # 像函数一样调用
return f"Hello, I'm {self.name}"
5.3 常用特殊方法一览
| Java方法 | Python方法 | 调用场景 |
|---|---|---|
toString() | __str__ / __repr__ | str(obj) |
equals() | __eq__ | == |
hashCode() | __hash__ | hash(obj) |
compareTo() | __lt__ / __le__ / ... | <, <= |
clone() | __copy__ / __deepcopy__ | copy.copy() |
Comparable | __lt__ | sorted() |
六、类变量 vs 实例变量
6.1 常见坑:类变量被意外修改
# ❌ 错误示例
class Gadget:
brand = "Generic" # 类变量
g1 = Gadget()
g2 = Gadget()
print(g1.brand, g2.brand) # Generic Generic
g1.brand = "Apple" # 这里创建了一个新的实例变量!
print(g1.brand, g2.brand) # Apple Generic - 不会影响g2
# ✅ 正确做法 - 如果想修改类变量
Gadget.brand = "Apple" # 修改类变量会影响所有实例
// Java - 没有这个问题
class Gadget {
static String brand = "Generic";
}
Gadget g1 = new Gadget();
Gadget g2 = new Gadget();
g1.brand = "Apple"; // 编译错误!static变量要用类名访问
Gadget.brand = "Apple"; // 正确
七、接口与抽象类对比
7.1 Java的接口
public interface Drawable {
void draw(); # 隐式public abstract
default void print() { # Java 8+ default方法
System.out.println("Printing");
}
static void staticMethod() { # Java 8+ 静态方法
System.out.println("Static");
}
}
7.2 Python的协议(Protocol)
# Python 3.8+ 协议 - 类似Go的接口
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None:
...
# 隐式实现 - 不需要显式继承
class Circle:
def draw(self):
print("Drawing circle")
def render(d: Drawable):
d.draw()
render(Circle()) # 可以!自动检查是否有draw方法
八、实战:策略模式
8.1 Java实现
public interface PaymentStrategy {
void pay(double amount);
}
public class CreditCardPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " via Credit Card");
}
}
public class PayPalPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " via PayPal");
}
}
public class ShoppingCart {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void checkout(double amount) {
strategy.pay(amount);
}
}
8.2 Python实现
# Python实现策略模式更简洁
class PaymentStrategy:
def pay(self, amount):
raise NotImplementedError
class CreditCardPayment(PaymentStrategy):
def pay(self, amount):
print(f"Paid {amount} via Credit Card")
class PayPalPayment(PaymentStrategy):
def pay(self, amount):
print(f"Paid {amount} via PayPal")
class ShoppingCart:
def __init__(self, strategy=None):
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def checkout(self, amount):
if self.strategy:
self.strategy.pay(amount)
# 使用
cart = ShoppingCart(CreditCardPayment())
cart.checkout(100)
cart.set_strategy(PayPalPayment())
cart.checkout(200)
九、总结
| 特性 | Java | Python |
|---|---|---|
| 多继承 | 不支持(只有接口) | 支持 |
| 访问控制 | 强制private/public | 约定_和__ |
| 抽象类 | abstract class | @abstractmethod |
| 接口 | interface | 抽象基类或Protocol |
| 数据类 | record(Java 16+) | @dataclass |
| 构造器 | 与类名同名的方法 | __init__ |
| 调用父类 | super() | super() |
| 特殊方法 | Object方法 | __xxx__ |
Python的OOP设计理念是"约定优于强制"。没有强制的访问控制,没有编译时类型检查,但有强大的元编程能力。多继承和dataclass是Python最实用的特性,值得熟练掌握。