第10章:Python 面向对象编程
10.1 面向对象编程的概念
面向对象编程(Object-Oriented Programming,OOP)是一种编程范式,它将数据和操作数据的方法封装在一起,形成对象。
基本概念
- 类(Class):是对象的蓝图或模板,定义了对象的属性和方法
- 对象(Object):是类的实例,具有类定义的属性和方法
- 属性(Attribute):对象的特征或数据
- 方法(Method):对象的行为或操作
- 封装(Encapsulation):将数据和方法封装在类中,隐藏实现细节
- 继承(Inheritance):子类继承父类的属性和方法
- 多态(Polymorphism):不同类的对象可以使用相同的方法名,但行为不同
面向对象编程的优势
- 代码重用:通过继承实现
- 模块化:将复杂问题分解为对象
- 可维护性:封装使得代码更易于维护
- 可扩展性:通过继承和多态实现
10.2 类的定义和使用
定义类
class Person:
"""人员类"""
# 类变量
species = "Homo sapiens"
# 初始化方法
def __init__(self, name, age):
"""初始化人员对象"""
# 实例变量
self.name = name
self.age = age
# 实例方法
def introduce(self):
"""自我介绍"""
return f"My name is {self.name}, I am {self.age} years old."
# 类方法
@classmethod
def get_species(cls):
"""获取物种"""
return cls.species
# 静态方法
@staticmethod
def is_adult(age):
"""判断是否成年"""
return age >= 18
创建对象
# 创建对象
person1 = Person("John", 30)
person2 = Person("Alice", 25)
# 访问属性
print(person1.name) # John
print(person1.age) # 30
# 调用方法
print(person1.introduce()) # My name is John, I am 30 years old.
# 访问类变量
print(Person.species) # Homo sapiens
print(person1.species) # Homo sapiens
# 调用类方法
print(Person.get_species()) # Homo sapiens
# 调用静态方法
print(Person.is_adult(18)) # True
print(Person.is_adult(16)) # False
实例变量和类变量
class Circle:
"""圆形类"""
# 类变量
pi = 3.14159
def __init__(self, radius):
"""初始化圆形对象"""
# 实例变量
self.radius = radius
def area(self):
"""计算面积"""
return self.pi * self.radius ** 2
def circumference(self):
"""计算周长"""
return 2 * self.pi * self.radius
# 创建对象
circle1 = Circle(5)
circle2 = Circle(10)
# 访问实例变量
print(circle1.radius) # 5
print(circle2.radius) # 10
# 访问类变量
print(Circle.pi) # 3.14159
print(circle1.pi) # 3.14159
# 修改类变量
Circle.pi = 3.14
print(Circle.pi) # 3.14
print(circle1.pi) # 3.14
print(circle2.pi) # 3.14
# 修改实例变量
circle1.radius = 7
print(circle1.radius) # 7
print(circle2.radius) # 10
10.3 方法类型
实例方法
实例方法是最常用的方法类型,它接收 self 参数,用于访问实例变量。
class Person:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"
person = Person("John")
print(person.greet()) # Hello, my name is John
类方法
类方法使用 @classmethod 装饰器,接收 cls 参数,用于访问类变量。
class Person:
count = 0
def __init__(self, name):
self.name = name
Person.count += 1
@classmethod
def get_count(cls):
return cls.count
person1 = Person("John")
person2 = Person("Alice")
print(Person.get_count()) # 2
静态方法
静态方法使用 @staticmethod 装饰器,不接收 self 或 cls 参数,通常用于与类相关但不依赖于类或实例状态的功能。
class Math:
@staticmethod
def add(a, b):
return a + b
@staticmethod
def multiply(a, b):
return a * b
print(Math.add(5, 3)) # 8
print(Math.multiply(5, 3)) # 15
10.4 继承
继承是面向对象编程的重要特性,它允许子类继承父类的属性和方法。
基本继承
# 父类
class Animal:
"""动物类"""
def __init__(self, name):
self.name = name
def speak(self):
"""发出声音"""
return "Some generic sound"
# 子类
class Dog(Animal):
"""狗类"""
def speak(self):
"""发出声音"""
return "Woof!"
class Cat(Animal):
"""猫类"""
def speak(self):
"""发出声音"""
return "Meow!"
# 创建对象
dog = Dog("Buddy")
cat = Cat("Whiskers")
# 调用方法
print(dog.name) # Buddy
print(dog.speak()) # Woof!
print(cat.name) # Whiskers
print(cat.speak()) # Meow!
调用父类方法
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
return f"My name is {self.name}, I am {self.age} years old."
class Student(Person):
def __init__(self, name, age, student_id):
# 调用父类的 __init__ 方法
super().__init__(name, age)
self.student_id = student_id
def introduce(self):
# 调用父类的 introduce 方法
base_intro = super().introduce()
return f"{base_intro} My student ID is {self.student_id}."
student = Student("John", 20, "12345")
print(student.introduce()) # My name is John, I am 20 years old. My student ID is 12345.
多重继承
Python 支持多重继承,一个子类可以继承多个父类。
class A:
def method_a(self):
return "Method A"
class B:
def method_b(self):
return "Method B"
class C(A, B):
def method_c(self):
return "Method C"
c = C()
print(c.method_a()) # Method A
print(c.method_b()) # Method B
print(c.method_c()) # Method C
10.5 多态
多态是指不同类的对象可以使用相同的方法名,但行为不同。
多态示例
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
class Cow(Animal):
def speak(self):
return "Moo!"
# 多态函数
def make_animal_speak(animal):
print(animal.speak())
# 创建对象
dog = Dog()
cat = Cat()
cow = Cow()
# 调用函数
make_animal_speak(dog) # Woof!
make_animal_speak(cat) # Meow!
make_animal_speak(cow) # Moo!
鸭子类型
Python 采用鸭子类型,即"如果它走起来像鸭子,叫起来像鸭子,那么它就是鸭子"。这意味着只要对象有相同的方法名,就可以被当作相同类型使用。
class Duck:
def quack(self):
return "Quack!"
class Person:
def quack(self):
return "I'm quacking like a duck!"
# 函数接收任何有 quack 方法的对象
def make_quack(obj):
print(obj.quack())
# 创建对象
duck = Duck()
person = Person()
# 调用函数
make_quack(duck) # Quack!
make_quack(person) # I'm quacking like a duck!
10.6 封装
封装是将数据和方法封装在类中,隐藏实现细节,只提供公共接口。
私有属性和方法
在 Python 中,使用双下划线前缀 __ 来定义私有属性和方法,它们在类外部不能直接访问。
class Person:
def __init__(self, name, age):
self.name = name # 公共属性
self.__age = age # 私有属性
def get_age(self):
"""获取年龄"""
return self.__age
def set_age(self, age):
"""设置年龄"""
if age > 0:
self.__age = age
def __private_method(self):
"""私有方法"""
return "This is a private method"
person = Person("John", 30)
# 访问公共属性
print(person.name) # John
# 访问私有属性(会报错)
# print(person.__age) # AttributeError
# 通过公共方法访问私有属性
print(person.get_age()) # 30
person.set_age(35)
print(person.get_age()) # 35
# 访问私有方法(会报错)
# print(person.__private_method()) # AttributeError
# 实际上,Python 只是将私有属性名修改为 _ClassName__attribute
print(person._Person__age) # 35
print(person._Person__private_method()) # This is a private method
保护属性
使用单下划线前缀 _ 来定义保护属性,它们在类外部可以访问,但按照约定不应该直接修改。
class Person:
def __init__(self, name, age):
self.name = name
self._age = age # 保护属性
person = Person("John", 30)
print(person._age) # 30(可以访问,但不推荐)
person._age = 35 # 可以修改,但不推荐
print(person._age) # 35
10.7 特殊方法
Python 提供了许多特殊方法,它们以双下划线开头和结尾,用于实现对象的特殊行为。
常用特殊方法
| 方法 | 描述 |
|---|---|
__init__ | 初始化对象 |
__str__ | 字符串表示 |
__repr__ | 官方字符串表示 |
__eq__ | 等于比较 |
__lt__ | 小于比较 |
__gt__ | 大于比较 |
__add__ | 加法运算 |
__sub__ | 减法运算 |
__mul__ | 乘法运算 |
__div__ | 除法运算 |
__len__ | 长度 |
__getitem__ | 索引访问 |
__setitem__ | 索引赋值 |
__contains__ | 成员检测 |
示例:实现特殊方法
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 __mul__(self, scalar):
"""乘法运算"""
return Vector(self.x * scalar, self.y * scalar)
def __len__(self):
"""长度"""
return int((self.x ** 2 + self.y ** 2) ** 0.5)
def __eq__(self, other):
"""等于比较"""
return self.x == other.x and self.y == other.y
# 创建对象
v1 = Vector(3, 4)
v2 = Vector(1, 2)
# 测试特殊方法
print(v1) # Vector(3, 4)
print(repr(v1)) # Vector(3, 4)
print(v1 + v2) # Vector(4, 6)
print(v1 - v2) # Vector(2, 2)
print(v1 * 2) # Vector(6, 8)
print(len(v1)) # 5
print(v1 == v2) # False
print(v1 == Vector(3, 4)) # True
10.8 抽象类和接口
抽象类是不能实例化的类,它定义了子类必须实现的方法。
使用 abc 模块
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):
"""计算面积"""
return 3.14159 * self.radius ** 2
def perimeter(self):
"""计算周长"""
return 2 * 3.14159 * self.radius
class Rectangle(Shape):
"""矩形类"""
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
"""计算面积"""
return self.width * self.height
def perimeter(self):
"""计算周长"""
return 2 * (self.width + self.height)
# 创建对象
circle = Circle(5)
rectangle = Rectangle(4, 6)
# 调用方法
print(circle.area()) # 78.53975
print(circle.perimeter()) # 31.4159
print(rectangle.area()) # 24
print(rectangle.perimeter()) # 20
# 尝试实例化抽象类(会报错)
# shape = Shape() # TypeError
抽象类的高阶用法
抽象属性
from abc import ABC, abstractmethod, abstractproperty
class Shape(ABC):
"""形状抽象类"""
@abstractproperty
def name(self):
"""形状名称"""
pass
class Circle(Shape):
"""圆形类"""
def __init__(self, radius):
self.radius = radius
self._name = "Circle"
@property
def name(self):
return self._name
circle = Circle(5)
print(circle.name) # Circle
抽象类中的具体方法
from abc import ABC, abstractmethod
class Shape(ABC):
"""形状抽象类"""
@abstractmethod
def area(self):
"""计算面积"""
pass
def description(self):
"""抽象类中的具体方法"""
return f"This is a {self.__class__.__name__}"
class Circle(Shape):
"""圆形类"""
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
circle = Circle(5)
print(circle.description()) # This is a Circle
print(circle.area()) # 78.53975
接口
接口是一种特殊的抽象类,它只包含抽象方法,没有具体实现。在 Python 中,没有像 Java 那样的 interface 关键字,但可以使用抽象类来实现接口的功能。
接口的概念
接口定义了对象应该做什么,而不关心对象如何实现。接口是一种协议或契约,确保类实现了特定的方法。
Python 中的接口实现
from abc import ABC, abstractmethod
class Drawable(ABC):
"""可绘制接口"""
@abstractmethod
def draw(self):
"""绘制方法"""
pass
class Colorable(ABC):
"""可着色接口"""
@abstractmethod
def set_color(self, color):
"""设置颜色"""
pass
class Circle(Drawable, Colorable):
"""圆形类,同时实现两个接口"""
def __init__(self, radius):
self.radius = radius
self.color = "black"
def draw(self):
return f"Drawing circle with radius {self.radius}"
def set_color(self, color):
self.color = color
return f"Set color to {color}"
class Square(Drawable):
"""方形类,只实现可绘制接口"""
def __init__(self, side):
self.side = side
def draw(self):
return f"Drawing square with side {self.side}"
# 使用接口
shapes = [Circle(5), Square(4)]
for shape in shapes:
if isinstance(shape, Drawable):
print(shape.draw())
# 检查接口实现
circle = Circle(5)
if isinstance(circle, Colorable):
circle.set_color("red")
print(circle.color) # red
多接口组合
from abc import ABC, abstractmethod
class Printable(ABC):
"""打印接口"""
@abstractmethod
def print_data(self):
pass
class Saveable(ABC):
"""保存接口"""
@abstractmethod
def save(self):
pass
class Exportable(ABC):
"""导出接口"""
@abstractmethod
def export(self, format_type):
pass
class Document(Printable, Saveable, Exportable):
"""文档类,实现多个接口"""
def __init__(self, content):
self.content = content
def print_data(self):
return f"Printing: {self.content}"
def save(self):
return f"Saving: {self.content}"
def export(self, format_type):
return f"Exporting as {format_type}: {self.content}"
doc = Document("Hello, World!")
print(doc.print_data()) # Printing: Hello, World!
print(doc.save()) # Saving: Hello, World!
print(doc.export("PDF")) # Exporting as PDF: Hello, World!
接口与抽象类的区别
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 方法实现 | 可以有具体方法 | 只能有抽象方法(Python 中) |
| 属性 | 可以有实例属性 | 不能有实例属性 |
| 多继承 | 受限 | 支持多接口 |
| 继承关系 | "是-一个"关系 | "能做-什么"关系 |
| 使用场景 | 共享代码和属性 | 定义行为契约 |
接口设计原则
- 接口隔离原则:使用多个专门的接口,而不是一个通用的接口
- 依赖倒置原则:依赖于抽象而不是具体实现
- 开闭原则:对扩展开放,对修改关闭
from abc import ABC, abstractmethod
# 好的接口设计
class Flyable(ABC):
"""飞行接口"""
@abstractmethod
def fly(self):
pass
class Swimmable(ABC):
"""游泳接口"""
@abstractmethod
def swim(self):
pass
class Duck(Swimmable, Flyable):
"""鸭子同时实现飞行和游泳接口"""
def fly(self):
return "Duck is flying"
def swim(self):
return "Duck is swimming"
# 不好的设计(一个大接口)
class Animal(ABC):
@abstractmethod
def fly(self):
pass
@abstractmethod
def swim(self):
pass
@abstractmethod
def run(self):
pass
# 好的设计将行为分离成多个专门的接口
10.9 综合示例
示例1:银行账户
class BankAccount:
"""银行账户类"""
def __init__(self, account_number, balance=0):
"""初始化账户"""
self.account_number = account_number
self._balance = balance
def deposit(self, amount):
"""存款"""
if amount > 0:
self._balance += amount
return f"Deposited ${amount}. New balance: ${self._balance}"
else:
return "Deposit amount must be positive"
def withdraw(self, amount):
"""取款"""
if amount > 0:
if amount <= self._balance:
self._balance -= amount
return f"Withdrew ${amount}. New balance: ${self._balance}"
else:
return "Insufficient funds"
else:
return "Withdrawal amount must be positive"
def get_balance(self):
"""获取余额"""
return self._balance
def __str__(self):
"""字符串表示"""
return f"Account {self.account_number}: ${self._balance}"
# 创建账户
account = BankAccount("12345", 1000)
print(account) # Account 12345: $1000
# 存款
print(account.deposit(500)) # Deposited $500. New balance: $1500
# 取款
print(account.withdraw(200)) # Withdrew $200. New balance: $1300
print(account.withdraw(2000)) # Insufficient funds
# 获取余额
print(account.get_balance()) # 1300
示例2:学生管理系统
class Student:
"""学生类"""
def __init__(self, student_id, name, age, grade):
"""初始化学生"""
self.student_id = student_id
self.name = name
self.age = age
self.grade = grade
def update_grade(self, new_grade):
"""更新成绩"""
self.grade = new_grade
def __str__(self):
"""字符串表示"""
return f"Student {self.student_id}: {self.name}, {self.age} years old, Grade: {self.grade}"
class StudentManager:
"""学生管理类"""
def __init__(self):
"""初始化学生管理"""
self.students = {}
def add_student(self, student):
"""添加学生"""
self.students[student.student_id] = student
return f"Added student: {student.name}"
def remove_student(self, student_id):
"""删除学生"""
if student_id in self.students:
removed_student = self.students.pop(student_id)
return f"Removed student: {removed_student.name}"
else:
return "Student not found"
def get_student(self, student_id):
"""获取学生"""
return self.students.get(student_id, None)
def list_students(self):
"""列出所有学生"""
if not self.students:
return "No students found"
else:
return "\n".join(str(student) for student in self.students.values())
# 创建学生管理系统
manager = StudentManager()
# 添加学生
student1 = Student("101", "John", 15, "A")
student2 = Student("102", "Alice", 16, "B")
student3 = Student("103", "Bob", 14, "C")
print(manager.add_student(student1))
print(manager.add_student(student2))
print(manager.add_student(student3))
# 列出学生
print("\nAll students:")
print(manager.list_students())
# 更新学生成绩
student1.update_grade("A+")
print(f"\nUpdated {student1.name}'s grade to {student1.grade}")
# 获取学生
print("\nGetting student 101:")
student = manager.get_student("101")
print(student)
# 删除学生
print("\nRemoving student 102:")
print(manager.remove_student("102"))
# 列出学生
print("\nAll students after removal:")
print(manager.list_students())
示例3:图形继承体系
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):
"""计算面积"""
return 3.14159 * self.radius ** 2
def perimeter(self):
"""计算周长"""
return 2 * 3.14159 * self.radius
class Rectangle(Shape):
"""矩形类"""
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
"""计算面积"""
return self.width * self.height
def perimeter(self):
"""计算周长"""
return 2 * (self.width + self.height)
class Square(Rectangle):
"""正方形类"""
def __init__(self, side):
super().__init__(side, side)
self.side = side
# 创建对象
circle = Circle(5)
rectangle = Rectangle(4, 6)
square = Square(4)
# 调用方法
print(f"Circle - Area: {circle.area():.2f}, Perimeter: {circle.perimeter():.2f}")
print(f"Rectangle - Area: {rectangle.area():.2f}, Perimeter: {rectangle.perimeter():.2f}")
print(f"Square - Area: {square.area():.2f}, Perimeter: {square.perimeter():.2f}")
10.10 常见问题和解决方案
问题1:忘记使用 self 参数
# 错误示例
class Person:
def __init__(name, age):
self.name = name # 会报错,因为方法没有 self 参数
self.age = age
# 解决方案
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
问题2:混淆类变量和实例变量
# 错误示例
class Counter:
count = 0
def __init__(self):
count = 0 # 这是局部变量,不是类变量
# 解决方案
class Counter:
count = 0
def __init__(self):
Counter.count = 0 # 访问类变量
# 或者
self.__class__.count = 0 # 访问类变量
问题3:继承时忘记调用父类的 init 方法
# 错误示例
class Parent:
def __init__(self, name):
self.name = name
class Child(Parent):
def __init__(self, name, age):
self.age = age # 没有调用父类的 __init__ 方法
# 解决方案
class Child(Parent):
def __init__(self, name, age):
super().__init__(name) # 调用父类的 __init__ 方法
self.age = age
问题4:滥用多重继承
# 不推荐的做法
class A:
pass
class B:
pass
class C(A, B):
pass
# 推荐的做法:优先使用组合而非继承
class A:
pass
class B:
pass
class C:
def __init__(self):
self.a = A()
self.b = B()
10.11 练习
- 类的创建练习:创建一个
Car类,包含品牌、型号、年份等属性,以及启动、停止等方法 - 继承练习:创建一个
Vehicle基类,然后创建Car和Truck子类 - 多态练习:创建一个函数,接收不同类型的对象并调用它们的方法
- 特殊方法练习:创建一个
Fraction类,实现__add__、__sub__等特殊方法 - 封装练习:创建一个类,使用私有属性和公共方法
- 综合练习:创建一个完整的面向对象程序,如图书馆管理系统
10.12 小结
本章我们学习了:
- 面向对象编程的基本概念
- 类的定义和使用
- 实例变量和类变量
- 方法类型(实例方法、类方法、静态方法)
- 继承
- 多态
- 封装
- 特殊方法
- 抽象类和接口
- 综合示例
现在你已经掌握了 Python 的面向对象编程,可以开始学习 Python 的网络编程了!