跳到主要内容

第10章:Python 面向对象编程

10.1 面向对象编程的概念

面向对象编程(Object-Oriented Programming,OOP)是一种编程范式,它将数据和操作数据的方法封装在一起,形成对象。

基本概念

  • 类(Class):是对象的蓝图或模板,定义了对象的属性和方法
  • 对象(Object):是类的实例,具有类定义的属性和方法
  • 属性(Attribute):对象的特征或数据
  • 方法(Method):对象的行为或操作
  • 封装(Encapsulation):将数据和方法封装在类中,隐藏实现细节
  • 继承(Inheritance):子类继承父类的属性和方法
  • 多态(Polymorphism):不同类的对象可以使用相同的方法名,但行为不同

面向对象编程的优势

  1. 代码重用:通过继承实现
  2. 模块化:将复杂问题分解为对象
  3. 可维护性:封装使得代码更易于维护
  4. 可扩展性:通过继承和多态实现

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 装饰器,不接收 selfcls 参数,通常用于与类相关但不依赖于类或实例状态的功能。

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 中)
属性可以有实例属性不能有实例属性
多继承受限支持多接口
继承关系"是-一个"关系"能做-什么"关系
使用场景共享代码和属性定义行为契约

接口设计原则

  1. 接口隔离原则:使用多个专门的接口,而不是一个通用的接口
  2. 依赖倒置原则:依赖于抽象而不是具体实现
  3. 开闭原则:对扩展开放,对修改关闭
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 练习

  1. 类的创建练习:创建一个 Car 类,包含品牌、型号、年份等属性,以及启动、停止等方法
  2. 继承练习:创建一个 Vehicle 基类,然后创建 CarTruck 子类
  3. 多态练习:创建一个函数,接收不同类型的对象并调用它们的方法
  4. 特殊方法练习:创建一个 Fraction 类,实现 __add____sub__ 等特殊方法
  5. 封装练习:创建一个类,使用私有属性和公共方法
  6. 综合练习:创建一个完整的面向对象程序,如图书馆管理系统

10.12 小结

本章我们学习了:

  • 面向对象编程的基本概念
  • 类的定义和使用
  • 实例变量和类变量
  • 方法类型(实例方法、类方法、静态方法)
  • 继承
  • 多态
  • 封装
  • 特殊方法
  • 抽象类和接口
  • 综合示例

现在你已经掌握了 Python 的面向对象编程,可以开始学习 Python 的网络编程了!