Python面向对象编程 - 从Java到Python的OOP转变
1. Python面向对象编程概述
作为Java开发者,您已经熟悉面向对象编程(OOP)的核心概念。Python也是一种支持面向对象编程的语言,但其实现方式与Java有一些显著差异。
2. 类的定义与实例化
基本类定义
Python使用class关键字定义类,与Java类似:
# Python类定义
class Person:
# 类变量(相当于Java的静态字段)
species = "Homo Sapiens"
# 构造方法
def __init__(self, name, age):
# 实例变量(相当于Java的实例字段)
self.name = name
self.age = age
# 实例方法
def introduce(self):
return f"我叫{self.name},今年{self.age}岁"
# 类方法(相当于Java的静态方法,但可以访问类变量)
@classmethod
def get_species(cls):
return cls.species
# 静态方法(与Java的静态方法类似)
@staticmethod
def is_adult(age):
return age >= 18
对比Java:
// Java类定义
public class Person {
// 静态字段
private static final String SPECIES = "Homo Sapiens";
// 实例字段
private String name;
private int age;
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 实例方法
public String introduce() {
return "我叫" + this.name + ",今年" + this.age + "岁";
}
// 静态方法
public static String getSpecies() {
return SPECIES;
}
// 静态方法
public static boolean isAdult(int age) {
return age >= 18;
}
}
实例化与使用
# 创建实例
person = Person("张三", 30)
# 访问实例变量
print(person.name) # 输出: 张三
print(person.age) # 输出: 30
# 调用实例方法
print(person.introduce()) # 输出: 我叫张三,今年30岁
# 访问类变量
print(Person.species) # 输出: Homo Sapiens
print(person.species) # 也可以通过实例访问类变量
# 调用类方法
print(Person.get_species()) # 输出: Homo Sapiens
# 调用静态方法
print(Person.is_adult(20)) # 输出: True
print(person.is_adult(15)) # 也可以通过实例调用静态方法,输出: False
3. Python与Java的OOP差异
self参数
Python的实例方法必须显式声明第一个参数(通常命名为self),表示实例本身:
class MyClass:
def instance_method(self, arg1, arg2):
# self相当于Java中的this
pass
访问修饰符
Python没有Java那样的public、private、protected访问修饰符:
- Python使用命名约定来表示访问控制:
- 单下划线前缀(
_name)表示"受保护"(相当于Java的protected) - 双下划线前缀(
__name)表示"私有"(相当于Java的private) - 无前缀表示"公开"(相当于Java的
public)
- 单下划线前缀(
class Account:
def __init__(self, owner, balance):
self.owner = owner # 公开
self._account_type = "储蓄" # 受保护
self.__balance = balance # 私有
def deposit(self, amount):
if amount > 0:
self.__balance += amount
return True
return False
def get_balance(self):
return self.__balance
注意:Python的"私有"属性实际上是通过名称改写实现的(名称修饰),而不是真正的访问控制。
属性访问
Python提供了@property装饰器,可以将方法转换为属性访问语法:
class Person:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
@property
def full_name(self):
return f"{self.first_name} {self.last_name}"
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value < 0:
raise ValueError("年龄不能为负数")
self._age = value
# 使用
person = Person("张", "三")
print(person.full_name) # 输出: 张 三
person.age = 30 # 调用setter
print(person.age) # 调用getter,输出: 30
try:
person.age = -5 # 引发ValueError
except ValueError as e:
print(e) # 输出: 年龄不能为负数
这类似于Java的getter和setter,但语法更简洁。
4. 继承与多态
类继承关系图
classDiagram
Animal <|-- Dog
Animal <|-- Cat
Animal <|-- Duck
Flyable <|-- Duck
Swimmable <|-- Duck
class Animal {
+name: str
+__init__(name)
+speak()
}
class Dog {
+speak()
}
class Cat {
+speak()
}
class Flyable {
+fly()
}
class Swimmable {
+swim()
}
class Duck {
+speak()
+fly()
+swim()
}
基本继承
Python支持类继承,语法比Java更简洁:
# 基类
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("子类必须实现这个方法")
# 派生类
class Dog(Animal):
def speak(self):
return f"{self.name}说:汪汪!"
class Cat(Animal):
def speak(self):
return f"{self.name}说:喵喵!"
# 使用
dog = Dog("旺财")
cat = Cat("咪咪")
print(dog.speak()) # 输出: 旺财说:汪汪!
print(cat.speak()) # 输出: 咪咪说:喵喵!
对比Java:
// 基类
public abstract class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public abstract String speak();
}
// 派生类
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public String speak() {
return name + "说:汪汪!";
}
}
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public String speak() {
return name + "说:喵喵!";
}
}
多重继承
与Java不同,Python支持多重继承:
class Flyable:
def fly(self):
return "我能飞!"
class Swimmable:
def swim(self):
return "我能游泳!"
class Duck(Animal, Flyable, Swimmable):
def speak(self):
return f"{self.name}说:嘎嘎!"
# 使用
duck = Duck("唐老鸭")
print(duck.speak()) # 输出: 唐老鸭说:嘎嘎!
print(duck.fly()) # 输出: 我能飞!
print(duck.swim()) # 输出: 我能游泳!
Java只支持单继承,但可以实现多个接口:
public interface Flyable {
String fly();
}
public interface Swimmable {
String swim();
}
public class Duck extends Animal implements Flyable, Swimmable {
public Duck(String name) {
super(name);
}
@Override
public String speak() {
return name + "说:嘎嘎!";
}
@Override
public String fly() {
return "我能飞!";
}
@Override
public String swim() {
return "我能游泳!";
}
}
方法解析顺序(MRO)
Python使用C3线性化算法来确定多重继承中的方法解析顺序:
class A:
def who_am_i(self):
return "I am A"
class B(A):
def who_am_i(self):
return "I am B"
class C(A):
def who_am_i(self):
return "I am C"
class D(B, C):
pass
# 查看MRO
print(D.__mro__) # 输出: (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
d = D()
print(d.who_am_i()) # 输出: I am B
super()函数
Python的super()函数用于调用父类方法:
class Animal:
def __init__(self, name):
self.name = name
class Pet:
def __init__(self, owner):
self.owner = owner
class Dog(Animal, Pet):
def __init__(self, name, owner, breed):
Animal.__init__(self, name) # 显式调用父类构造方法
Pet.__init__(self, owner) # 显式调用父类构造方法
self.breed = breed
class Cat(Animal, Pet):
def __init__(self, name, owner, color):
super().__init__(name) # 使用super()调用第一个父类的构造方法
Pet.__init__(self, owner) # 显式调用第二个父类构造方法
self.color = color
5. 特殊方法(魔术方法)
Python的特殊方法(以双下划线开头和结尾)允许自定义类的行为:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
# 字符串表示
def __str__(self):
return f"Vector({self.x}, {self.y})"
# 调试表示
def __repr__(self):
return f"Vector({self.x}, {self.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 __eq__(self, other):
return self.x == other.x and self.y == other.y
# 长度(向量的模)
def __len__(self):
import math
return int(math.sqrt(self.x ** 2 + self.y ** 2))
# 使对象可调用
def __call__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
# 使用
v1 = Vector(3, 4)
v2 = Vector(1, 2)
print(v1) # 输出: Vector(3, 4)
print(v1 + v2) # 输出: Vector(4, 6)
print(v1 - v2) # 输出: Vector(2, 2)
print(v1 == v2) # 输出: False
print(len(v1)) # 输出: 5
print(v1(2)) # 输出: Vector(6, 8)
这些特殊方法在Java中通常通过重写toString()、equals()等方法或实现特定接口来实现。
6. 抽象基类
Python 3.4+提供了abc模块来定义抽象基类,类似于Java的抽象类:
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
import math
return math.pi * self.radius ** 2
def perimeter(self):
import math
return 2 * math.pi * self.radius
# 尝试实例化抽象类会引发错误
# shape = Shape() # TypeError
# 实例化具体类
circle = Circle(5)
print(f"面积: {circle.area():.2f}")
print(f"周长: {circle.perimeter():.2f}")
7. 数据类
Python 3.7+引入了数据类,简化了创建主要用于存储数据的类:
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
def distance_from_origin(self):
import math
return math.sqrt(self.x ** 2 + self.y ** 2)
@dataclass
class Rectangle:
width: float
height: float
position: Point = None # 默认值
def area(self):
return self.width * self.height
# 使用
p = Point(3, 4)
print(p) # 输出: Point(x=3, y=4)
print(p.distance_from_origin()) # 输出: 5.0
rect = Rectangle(10, 5, p)
print(rect) # 输出: Rectangle(width=10, height=5, position=Point(x=3, y=4))
print(rect.area()) # 输出: 50
这类似于Java的记录类(Java 14+)或Lombok的@Data注解。
8. 上下文管理器
Python的上下文管理器(with语句)用于资源管理,类似于Java的try-with-resources:
class FileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
# 使用
with FileManager('test.txt', 'w') as f:
f.write('Hello, World!')
# 文件自动关闭
9. 练习:Java到Python的OOP转换
将以下Java代码转换为Python:
public abstract class Employee {
private String name;
private int id;
public Employee(String name, int id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public int getId() {
return id;
}
public abstract double calculateSalary();
@Override
public String toString() {
return "Employee[name=" + name + ", id=" + id + ", salary=" + calculateSalary() + "]";
}
}
public class FullTimeEmployee extends Employee {
private double monthlySalary;
public FullTimeEmployee(String name, int id, double monthlySalary) {
super(name, id);
this.monthlySalary = monthlySalary;
}
@Override
public double calculateSalary() {
return monthlySalary;
}
}
public class PartTimeEmployee extends Employee {
private double hourlyRate;
private int hoursWorked;
public PartTimeEmployee(String name, int id, double hourlyRate, int hoursWorked) {
super(name, id);
this.hourlyRate = hourlyRate;
this.hoursWorked = hoursWorked;
}
@Override
public double calculateSalary() {
return hourlyRate * hoursWorked;
}
}
public class Main {
public static void main(String[] args) {
Employee[] employees = new Employee[2];
employees[0] = new FullTimeEmployee("张三", 1001, 10000);
employees[1] = new PartTimeEmployee("李四", 1002, 150, 80);
for (Employee emp : employees) {
System.out.println(emp);
}
}
}
Python解决方案:
from abc import ABC, abstractmethod
class Employee(ABC):
def __init__(self, name, id):
self.name = name
self.id = id
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@property
def id(self):
return self._id
@id.setter
def id(self, value):
self._id = value
@abstractmethod
def calculate_salary(self):
pass
def __str__(self):
return f"Employee[name={self.name}, id={self.id}, salary={self.calculate_salary()}]"
class FullTimeEmployee(Employee):
def __init__(self, name, id, monthly_salary):
super().__init__(name, id)
self.monthly_salary = monthly_salary
def calculate_salary(self):
return self.monthly_salary
class PartTimeEmployee(Employee):
def __init__(self, name, id, hourly_rate, hours_worked):
super().__init__(name, id)
self.hourly_rate = hourly_rate
self.hours_worked = hours_worked
def calculate_salary(self):
return self.hourly_rate * self.hours_worked
def main():
employees = [
FullTimeEmployee("张三", 1001, 10000),
PartTimeEmployee("李四", 1002, 150, 80)
]
for emp in employees:
print(emp)
if __name__ == "__main__":
main()
10. 今日总结
- Python的面向对象编程与Java有许多相似之处,但也有一些重要差异
- Python使用
class关键字定义类,__init__方法作为构造函数 - Python没有访问修饰符,而是使用命名约定(如
_和__前缀) - Python支持多重继承,而Java只支持单继承和多接口实现
- Python的特殊方法(魔术方法)允许自定义类的行为,如运算符重载
- Python 3.7+提供了数据类,简化了创建主要用于存储数据的类
11. 明日预告
明天我们将学习Python的模块和包,包括如何组织代码、导入模块、创建包以及使用第三方库,这对于构建大型Python项目至关重要。