====== 第五章 面向对象编程 ======
===== 本章概要 =====
面向对象编程(OOP)是一种程序设计范式,通过"对象"来组织代码。本章将学习Python中类和对象的基本概念,包括封装、继承和多态等核心特性。
===== 5.1 面向对象基础 =====
==== 5.1.1 什么是面向对象 ====
面向对象编程的核心概念:
* **类(Class)** - 对象的蓝图,定义属性和方法
* **对象(Object)** - 类的实例
* **封装(Encapsulation)** - 隐藏内部实现,暴露接口
* **继承(Inheritance)** - 子类继承父类的特性
* **多态(Polymorphism)** - 同一接口,不同实现
==== 5.1.2 定义类 ====
使用 ``class`` 关键字定义类。
class Dog:
"""狗的类"""
# 类属性(所有实例共享)
species = "Canis familiaris"
# 构造方法
def __init__(self, name, age):
"""初始化方法"""
self.name = name # 实例属性
self.age = age
# 实例方法
def bark(self):
"""狗叫"""
return f"{self.name} says: Woof!"
def get_info(self):
"""获取狗的信息"""
return f"{self.name} is {self.age} years old"
# 创建对象
buddy = Dog("Buddy", 3)
miles = Dog("Miles", 5)
# 访问属性和方法
print(buddy.name) # Buddy
print(buddy.bark()) # Buddy says: Woof!
print(buddy.get_info()) # Buddy is 3 years old
==== 5.1.3 __init__ 构造方法 ====
class Person:
def __init__(self, name, age=0):
"""
构造方法
self代表类的实例,必须作为第一个参数
"""
self.name = name
self.age = age
self._id = None # 内部属性
def introduce(self):
return f"Hi, I'm {self.name}, {self.age} years old."
# 创建对象
person1 = Person("Alice", 25)
person2 = Person("Bob") # 使用默认年龄
==== 5.1.4 self 详解 ====
class Example:
def __init__(self, value):
self.value = value # 给实例添加属性
def show(self):
print(f"Value: {self.value}")
def update(self, new_value):
self.value = new_value
# self会自动传递
obj = Example(10)
obj.show() # 实际是 Example.show(obj)
obj.update(20) # 实际是 Example.update(obj, 20)
===== 5.2 类的属性 =====
==== 5.2.1 实例属性 vs 类属性 ====
class Car:
# 类属性 - 所有实例共享
wheels = 4
def __init__(self, brand, color):
# 实例属性 - 每个实例独立
self.brand = brand
self.color = color
self.odometer = 0
# 使用
car1 = Car("Toyota", "Red")
car2 = Car("Honda", "Blue")
print(car1.wheels) # 4
print(car2.wheels) # 4
# 修改类属性
Car.wheels = 3
print(car1.wheels) # 3
print(car2.wheels) # 3
# 给实例添加属性(不影响其他实例)
car1.wheels = 5
print(car1.wheels) # 5(实例属性)
print(car2.wheels) # 3(类属性)
==== 5.2.2 属性访问控制 ====
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self._balance = balance # 约定:受保护属性
self.__transaction_log = [] # 名称修饰:私有属性
def deposit(self, amount):
if amount > 0:
self._balance += amount
self.__transaction_log.append(f"+{amount}")
def get_balance(self):
return self._balance
account = BankAccount("Alice", 100)
# 可以访问,但不建议
print(account._balance) # 100
# 名称修饰后的私有属性
# print(account.__transaction_log) # AttributeError
print(account._BankAccount__transaction_log) # 可以访问,但不建议
==== 5.2.3 @property 装饰器 ====
class Temperature:
def __init__(self, celsius=0):
self._celsius = celsius
@property
def celsius(self):
"""获取摄氏度"""
return self._celsius
@celsius.setter
def celsius(self, value):
"""设置摄氏度"""
if value < -273.15:
raise ValueError("温度不能低于绝对零度")
self._celsius = value
@property
def fahrenheit(self):
"""获取华氏度(计算属性)"""
return self._celsius * 9/5 + 32
@fahrenheit.setter
def fahrenheit(self, value):
"""设置华氏度"""
self._celsius = (value - 32) * 5/9
# 使用
temp = Temperature(25)
print(temp.celsius) # 25(像属性一样访问)
temp.celsius = 30 # 像属性一样设置
print(temp.fahrenheit) # 86.0
===== 5.3 类的方法 =====
==== 5.3.1 实例方法 ====
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
==== 5.3.2 类方法 ====
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_string):
"""从字符串创建日期对象"""
year, month, day = map(int, date_string.split('-'))
return cls(year, month, day)
@classmethod
def today(cls):
"""创建今天的日期"""
import datetime
now = datetime.datetime.now()
return cls(now.year, now.month, now.day)
def __str__(self):
return f"{self.year}-{self.month:02d}-{self.day:02d}"
# 使用
birthday = Date.from_string("1990-05-15")
today = Date.today()
print(birthday) # 1990-05-15
print(today)
==== 5.3.3 静态方法 ====
class MathUtils:
@staticmethod
def add(x, y):
"""静态方法,不需要self或cls"""
return x + y
@staticmethod
def is_prime(n):
if n < 2:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
# 使用(通过类或实例调用都可以)
print(MathUtils.add(3, 5)) # 8
print(MathUtils.is_prime(17)) # True
===== 5.4 继承 =====
==== 5.4.1 基本继承 ====
# 父类(基类)
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("子类必须实现此方法")
def introduce(self):
return f"I am {self.name}"
# 子类(派生类)
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调用父类构造方法
self.breed = breed
def speak(self):
return f"{self.name} says: Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says: Meow!"
# 使用
dog = Dog("Buddy", "Golden Retriever")
cat = Cat("Whiskers")
print(dog.speak()) # Buddy says: Woof!
print(cat.speak()) # Whiskers says: Meow!
print(dog.introduce()) # I am Buddy(继承的方法)
==== 5.4.2 super() 函数 ====
class Rectangle:
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
def area(self):
# 调用父类方法
return super().area()
def diagonal(self):
return self.side * (2 ** 0.5)
square = Square(5)
print(square.area()) # 25
print(square.perimeter()) # 20
==== 5.4.3 多重继承 ====
class Flyable:
def fly(self):
return "I can fly!"
class Swimmable:
def swim(self):
return "I can swim!"
class Duck(Animal, Flyable, Swimmable):
def speak(self):
return f"{self.name} says: Quack!"
duck = Duck("Donald")
print(duck.speak()) # Donald says: Quack!
print(duck.fly()) # I can fly!
print(duck.swim()) # I can swim!
==== 5.4.4 方法解析顺序(MRO) ====
class A:
def method(self):
return "A"
class B(A):
def method(self):
return "B"
class C(A):
def method(self):
return "C"
class D(B, C):
pass
print(D.method()) # B(按MRO顺序)
print(D.__mro__) # (, , , , )
===== 5.5 多态 =====
==== 5.5.1 鸭子类型 ====
Python是动态类型语言,通过"鸭子类型"实现多态。
class Dog:
def speak(self):
return "Woof!"
def move(self):
return "Running on 4 legs"
class Bird:
def speak(self):
return "Tweet!"
def move(self):
return "Flying in the sky"
class Fish:
def speak(self):
return "Blub!"
def move(self):
return "Swimming in water"
# 多态函数
def animal_concert(animals):
for animal in animals:
print(f"{type(animal).__name__}: {animal.speak()}")
# 不同的对象,相同的方法调用
animals = [Dog(), Bird(), Fish()]
animal_concert(animals)
==== 5.5.2 抽象基类 ====
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
"""计算面积"""
pass
@abstractmethod
def perimeter(self):
"""计算周长"""
pass
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 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() # 错误!不能实例化抽象类
rect = Rectangle(3, 4)
circle = Circle(5)
shapes = [rect, circle]
for shape in shapes:
print(f"{type(shape).__name__}: Area={shape.area():.2f}")
===== 5.6 封装 =====
==== 5.6.1 数据封装 ====
class BankAccount:
def __init__(self, owner, balance=0):
self._owner = owner
self._balance = balance
@property
def balance(self):
"""只读属性:余额"""
return self._balance
@property
def owner(self):
"""只读属性:所有者"""
return self._owner
def deposit(self, amount):
"""存款"""
if amount <= 0:
raise ValueError("存款金额必须大于0")
self._balance += amount
return self._balance
def withdraw(self, amount):
"""取款"""
if amount <= 0:
raise ValueError("取款金额必须大于0")
if amount > self._balance:
raise ValueError("余额不足")
self._balance -= amount
return self._balance
# 使用
account = BankAccount("Alice", 1000)
account.deposit(500)
account.withdraw(200)
print(account.balance) # 1300
# account.balance = 5000 # AttributeError: can't set attribute
===== 5.7 代码示例 =====
==== 示例1:图书管理系统 ====
from datetime import datetime, timedelta
class Book:
def __init__(self, isbn, title, author):
self.isbn = isbn
self.title = title
self.author = author
self.is_borrowed = False
self.due_date = None
def borrow(self, days=14):
if self.is_borrowed:
return False
self.is_borrowed = True
self.due_date = datetime.now() + timedelta(days=days)
return True
def return_book(self):
if not self.is_borrowed:
return False
self.is_borrowed = False
self.due_date = None
return True
def __str__(self):
status = "已借出" if self.is_borrowed else "可借阅"
return f"《{self.title}》- {self.author} [{status}]"
class Member:
def __init__(self, member_id, name):
self.member_id = member_id
self.name = name
self.borrowed_books = []
self.max_books = 5
def borrow_book(self, book):
if len(self.borrowed_books) >= self.max_books:
return False, "已达最大借书数量"
if book.borrow():
self.borrowed_books.append(book)
return True, f"成功借阅《{book.title}》"
return False, "该书已被借出"
def return_book(self, book):
if book in self.borrowed_books:
book.return_book()
self.borrowed_books.remove(book)
return True, f"成功归还《{book.title}》"
return False, "未借阅该书"
# 使用示例
library = []
member = Member("M001", "张三")
book1 = Book("978-7-111", "Python编程", "张三")
book2 = Book("978-7-112", "数据结构与算法", "李四")
library.extend([book1, book2])
success, msg = member.borrow_book(book1)
print(msg)
print(book1)
==== 示例2:几何图形类 ====
import math
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
def __str__(self):
return f"{self.__class__.__name__}: area={self.area():.2f}"
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 Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius ** 2
def perimeter(self):
return 2 * math.pi * self.radius
class Triangle(Shape):
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def area(self):
s = self.perimeter() / 2
return math.sqrt(s * (s - self.a) * (s - self.b) * (s - self.c))
def perimeter(self):
return self.a + self.b + self.c
def calculate_total_area(shapes):
return sum(shape.area() for shape in shapes)
# 使用
shapes = [Rectangle(3, 4), Circle(5), Triangle(3, 4, 5)]
for shape in shapes:
print(shape)
print(f"总面积: {calculate_total_area(shapes):.2f}")
===== 5.8 练习题 =====
==== 练习1:实现一个Vector类 ====
class Vector:
def __init__(self, x, y):
self.x = x
self.y = 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 __str__(self):
return f"Vector({self.x}, {self.y})"
def magnitude(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
# 测试
v1 = Vector(3, 4)
v2 = Vector(1, 2)
print(v1 + v2) # Vector(4, 6)
print(v1 * 2) # Vector(6, 8)
print(v1.magnitude()) # 5.0
==== 练习2:实现栈(Stack)类 ====
class Stack:
def __init__(self):
self._items = []
def push(self, item):
self._items.append(item)
def pop(self):
if self.is_empty():
raise IndexError("栈为空")
return self._items.pop()
def peek(self):
if self.is_empty():
raise IndexError("栈为空")
return self._items[-1]
def is_empty(self):
return len(self._items) == 0
def size(self):
return len(self._items)
def __str__(self):
return f"Stack({self._items})"
# 测试
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(stack.pop()) # 3
print(stack) # Stack([1, 2])
===== 本章小结 =====
本章学习了面向对象编程的核心概念:
* **类和对象** - 类的定义、__init__方法、self参数
* **属性** - 实例属性、类属性、@property装饰器
* **方法** - 实例方法、类方法、静态方法
* **继承** - 基本继承、super()、多重继承、MRO
* **多态** - 鸭子类型、抽象基类
* **封装** - 访问控制、数据隐藏
面向对象编程让代码更加模块化、可维护和可扩展。下一章将学习更多高级OOP特性。
===== 进一步阅读 =====
* [[https://docs.python.org/zh-cn/3/tutorial/classes.html|Python官方教程 - 类]]
* [[https://docs.python.org/zh-cn/3/library/abc.html|abc模块 - 抽象基类]]