Appearance
面向对象编程
面向对象编程(Object-Oriented Programming,简称OOP)是一种程序设计范式。Python是一种支持面向对象编程的语言,它提供了类(Class)和对象(Object)的概念,以及封装、继承、多态等特性。本章将详细介绍Python中的面向对象编程。
面向对象基础概念
什么是面向对象
面向对象编程是一种将程序组织为对象集合的编程方式。每个对象都包含数据(属性)和操作数据的方法。
python
# 面向对象 vs 面向过程
# 面向过程的方式
student_name = "张三"
student_age = 20
student_score = 85
def print_student_info(name, age, score):
print(f"姓名: {name}, 年龄: {age}, 成绩: {score}")
def update_score(score, new_score):
return new_score
print_student_info(student_name, student_age, student_score)
# 面向对象的方式
class Student:
def __init__(self, name, age, score):
self.name = name # 属性
self.age = age # 属性
self.score = score # 属性
def print_info(self): # 方法
print(f"姓名: {self.name}, 年龄: {self.age}, 成绩: {self.score}")
def update_score(self, new_score): # 方法
self.score = new_score
# 创建对象
student = Student("张三", 20, 85)
student.print_info()
student.update_score(90)
student.print_info()类与对象
python
# 类的定义和对象的创建
# 定义一个简单的类
class Person:
"""这是一个表示人的类""" # 类的文档字符串
# 类属性(所有实例共享)
species = "人类"
# 构造方法(初始化方法)
def __init__(self, name, age):
# 实例属性(每个实例独有)
self.name = name
self.age = age
# 实例方法
def say_hello(self):
print(f"大家好,我是{self.name},今年{self.age}岁")
# 实例方法
def have_birthday(self):
self.age += 1
print(f"{self.name}过生日了,现在{self.age}岁")
# 创建对象(实例化)
person1 = Person("张三", 20)
person2 = Person("李四", 25)
# 访问属性
print(f"person1的名字: {person1.name}")
print(f"person2的年龄: {person2.age}")
# 访问类属性
print(f"person1的物种: {person1.species}")
print(f"person2的物种: {person2.species}")
# 调用方法
person1.say_hello()
person2.say_hello()
# 修改属性
person1.have_birthday()
# 查看对象的类型
print(f"person1的类型: {type(person1)}")
print(f"person1是Person的实例: {isinstance(person1, Person)}")类的属性
实例属性与类属性
python
# 实例属性与类属性的区别
class Student:
# 类属性:所有实例共享
school = "清华大学"
count = 0 # 统计学生数量
def __init__(self, name, age):
# 实例属性:每个实例独有
self.name = name
self.age = age
# 每创建一个实例,计数加1
Student.count += 1
def info(self):
print(f"姓名: {self.name}, 年龄: {self.age}, 学校: {self.school}")
# 创建实例
s1 = Student("张三", 20)
s2 = Student("李四", 21)
# 访问实例属性
print(f"s1.name: {s1.name}") # 张三
print(f"s2.name: {s2.name}") # 李四
# 访问类属性
print(f"s1.school: {s1.school}") # 清华大学
print(f"s2.school: {s2.school}") # 清华大学
print(f"Student.count: {Student.count}") # 2
# 修改类属性(影响所有实例)
Student.school = "北京大学"
print(f"修改后 s1.school: {s1.school}") # 北京大学
print(f"修改后 s2.school: {s2.school}") # 北京大学
# 通过实例修改类属性(实际上创建了新的实例属性)
s1.school = "复旦大学" # 这会创建一个新的实例属性
print(f"s1.school: {s1.school}") # 复旦大学(实例属性)
print(f"s2.school: {s2.school}") # 北京大学(类属性)
print(f"Student.school: {Student.school}") # 北京大学
# 删除实例属性后,会访问类属性
del s1.school
print(f"删除后 s1.school: {s1.school}") # 北京大学(类属性)属性的访问控制
python
# 属性的访问控制
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner # 公有属性
self._balance = balance # 保护属性(约定,单下划线)
self.__password = "123456" # 私有属性(双下划线,名称修饰)
def deposit(self, amount):
"""存款"""
if amount > 0:
self._balance += amount
print(f"存入{amount}元,余额: {self._balance}元")
def withdraw(self, amount, password):
"""取款"""
if password != self.__password:
print("密码错误!")
return
if amount > self._balance:
print("余额不足!")
return
self._balance -= amount
print(f"取出{amount}元,余额: {self._balance}元")
def get_balance(self):
"""获取余额"""
return self._balance
def change_password(self, old_pwd, new_pwd):
"""修改密码"""
if old_pwd == self.__password:
self.__password = new_pwd
print("密码修改成功!")
else:
print("原密码错误!")
# 创建账户
account = BankAccount("张三", 1000)
# 访问公有属性
print(f"账户持有人: {account.owner}")
# 访问保护属性(可以访问,但不推荐)
print(f"余额: {account._balance}") # 可以访问,但约定不直接访问
# 访问私有属性(无法直接访问)
# print(account.__password) # AttributeError
# 通过名称修饰访问私有属性(不推荐)
print(f"私有属性: {account._BankAccount__password}") # 可以访问
# 通过方法访问
account.deposit(500)
account.withdraw(200, "123456")
account.withdraw(200, "wrong") # 密码错误
print(f"当前余额: {account.get_balance()}元")使用 property 装饰器
python
# 使用 property 装饰器实现属性访问控制
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""获取半径"""
print("获取半径")
return self._radius
@radius.setter
def radius(self, value):
"""设置半径"""
print("设置半径")
if value <= 0:
raise ValueError("半径必须为正数")
self._radius = value
@radius.deleter
def radius(self):
"""删除半径"""
print("删除半径")
del self._radius
@property
def area(self):
"""计算面积(只读属性)"""
import math
return math.pi * self._radius ** 2
@property
def circumference(self):
"""计算周长(只读属性)"""
import math
return 2 * math.pi * self._radius
# 创建圆
circle = Circle(5)
# 获取半径
print(f"半径: {circle.radius}")
# 设置半径
circle.radius = 10
print(f"新半径: {circle.radius}")
# 尝试设置无效值
try:
circle.radius = -5
except ValueError as e:
print(f"错误: {e}")
# 访问只读属性
print(f"面积: {circle.area:.2f}")
print(f"周长: {circle.circumference:.2f}")
# 只读属性不能设置
# circle.area = 100 # AttributeError
# 删除属性
del circle.radius
# circle.radius # AttributeError类的方法
实例方法、类方法和静态方法
python
# 实例方法、类方法和静态方法
class Employee:
# 类属性
company = "科技公司"
count = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.count += 1
# 实例方法:第一个参数是 self
def get_info(self):
"""获取员工信息"""
return f"姓名: {self.name}, 薪资: {self.salary}, 公司: {self.company}"
def raise_salary(self, amount):
"""涨薪"""
self.salary += amount
print(f"{self.name}涨薪{amount}元,新薪资: {self.salary}元")
# 类方法:第一个参数是 cls
@classmethod
def get_company(cls):
"""获取公司名称"""
return cls.company
@classmethod
def set_company(cls, new_name):
"""设置公司名称"""
cls.company = new_name
print(f"公司名称已更改为: {cls.company}")
@classmethod
def from_string(cls, emp_str):
"""从字符串创建员工(工厂方法)"""
name, salary = emp_str.split("-")
return cls(name, int(salary))
# 静态方法:不需要 self 或 cls
@staticmethod
def is_workday(date):
"""判断是否是工作日"""
from datetime import date as d
if isinstance(date, str):
# 简单示例,假设输入格式为 "2024-01-15"
year, month, day = map(int, date.split("-"))
date = d(year, month, day)
# 周六(5)和周日(6)不是工作日
return date.weekday() < 5
@staticmethod
def calculate_tax(salary, rate=0.1):
"""计算税费"""
return salary * rate
# 创建员工
emp1 = Employee("张三", 10000)
emp2 = Employee("李四", 12000)
# 调用实例方法
print(emp1.get_info())
emp1.raise_salary(1000)
# 调用类方法
print(f"公司: {Employee.get_company()}")
Employee.set_company("创新科技")
print(f"新公司名: {emp1.get_company()}")
# 使用类方法创建实例
emp3 = Employee.from_string("王五-15000")
print(emp3.get_info())
# 调用静态方法
print(f"2024-01-15是工作日: {Employee.is_workday('2024-01-15')}")
print(f"税费: {Employee.calculate_tax(10000)}元")
# 静态方法也可以通过实例调用
print(f"税费: {emp1.calculate_tax(emp1.salary)}元")
# 打印员工总数
print(f"员工总数: {Employee.count}")特殊方法(魔术方法)
python
# 常用特殊方法
class Point:
"""表示二维点的类"""
def __init__(self, x, y):
self.x = x
self.y = y
# __str__: 定义 print() 和 str() 的输出
def __str__(self):
return f"Point({self.x}, {self.y})"
# __repr__: 定义 repr() 的输出,应该返回可以重建对象的表达式
def __repr__(self):
return f"Point({self.x}, {self.y})"
# __eq__: 定义 == 运算符
def __eq__(self, other):
if not isinstance(other, Point):
return False
return self.x == other.x and self.y == other.y
# __add__: 定义 + 运算符
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
# __sub__: 定义 - 运算符
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
# __mul__: 定义 * 运算符
def __mul__(self, scalar):
return Point(self.x * scalar, self.y * scalar)
# __len__: 定义 len() 函数
def __len__(self):
return 2 # 返回维度
# __getitem__: 定义索引访问
def __getitem__(self, index):
if index == 0:
return self.x
elif index == 1:
return self.y
raise IndexError("Point index out of range")
# __setitem__: 定义索引赋值
def __setitem__(self, index, value):
if index == 0:
self.x = value
elif index == 1:
self.y = value
else:
raise IndexError("Point index out of range")
# __contains__: 定义 in 运算符
def __contains__(self, value):
return value == self.x or value == self.y
# __bool__: 定义 bool() 函数
def __bool__(self):
return self.x != 0 or self.y != 0
# __abs__: 定义 abs() 函数
def __abs__(self):
import math
return math.sqrt(self.x ** 2 + self.y ** 2)
# 使用特殊方法
p1 = Point(3, 4)
p2 = Point(1, 2)
print(f"p1: {p1}") # __str__
print(f"repr(p1): {repr(p1)}") # __repr__
print(f"p1 == p2: {p1 == p2}") # __eq__
print(f"p1 + p2: {p1 + p2}") # __add__
print(f"p1 - p2: {p1 - p2}") # __sub__
print(f"p1 * 2: {p1 * 2}") # __mul__
print(f"len(p1): {len(p1)}") # __len__
print(f"p1[0]: {p1[0]}") # __getitem__
p1[0] = 5 # __setitem__
print(f"修改后 p1: {p1}")
print(f"3 in p1: {3 in p1}") # __contains__
print(f"bool(p1): {bool(p1)}") # __bool__
print(f"abs(p1): {abs(p1)}") # __abs__继承
基本继承
python
# 基本继承
# 父类(基类)
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print(f"{self.name}正在吃东西")
def sleep(self):
print(f"{self.name}正在睡觉")
def make_sound(self):
print(f"{self.name}发出声音")
# 子类(派生类)
class Dog(Animal):
def __init__(self, name, age, breed):
# 调用父类的构造方法
super().__init__(name, age)
self.breed = breed # 子类特有的属性
# 重写父类方法
def make_sound(self):
print(f"{self.name}汪汪叫")
# 子类特有的方法
def fetch(self):
print(f"{self.name}正在捡球")
# 另一个子类
class Cat(Animal):
def __init__(self, name, age, color):
super().__init__(name, age)
self.color = color
def make_sound(self):
print(f"{self.name}喵喵叫")
def climb(self):
print(f"{self.name}正在爬树")
# 创建实例
dog = Dog("旺财", 3, "金毛")
cat = Cat("咪咪", 2, "白色")
# 调用继承的方法
dog.eat()
cat.sleep()
# 调用重写的方法
dog.make_sound()
cat.make_sound()
# 调用子类特有的方法
dog.fetch()
cat.climb()
# 访问子类特有的属性
print(f"{dog.name}是{dog.breed}")
print(f"{cat.name}是{cat.color}的")
# 检查继承关系
print(f"dog是Dog的实例: {isinstance(dog, Dog)}")
print(f"dog是Animal的实例: {isinstance(dog, Animal)}")
print(f"Dog是Animal的子类: {issubclass(Dog, Animal)}")多重继承
python
# 多重继承
class Flyable:
"""会飞的"""
def fly(self):
print(f"{self.name}正在飞翔")
class Swimmable:
"""会游泳的"""
def swim(self):
print(f"{self.name}正在游泳")
class Walkable:
"""会走的"""
def walk(self):
print(f"{self.name}正在走路")
# 多重继承
class Duck(Animal, Flyable, Swimmable, Walkable):
def __init__(self, name, age):
super().__init__(name, age)
def make_sound(self):
print(f"{self.name}嘎嘎叫")
# 创建鸭子
duck = Duck("唐老鸭", 5)
# 调用各种方法
duck.make_sound() # 自己的方法
duck.eat() # 继承自 Animal
duck.fly() # 继承自 Flyable
duck.swim() # 继承自 Swimmable
duck.walk() # 继承自 Walkable
# 方法解析顺序(MRO)
print(f"\nDuck的MRO: {Duck.__mro__}")
# 菱形继承问题
class A:
def method(self):
print("A.method")
class B(A):
def method(self):
print("B.method")
super().method()
class C(A):
def method(self):
print("C.method")
super().method()
class D(B, C):
def method(self):
print("D.method")
super().method()
# 创建实例并调用方法
d = D()
d.method()
# 输出顺序: D.method -> B.method -> C.method -> A.method
# Python使用C3线性化算法解决菱形继承问题抽象类
python
# 抽象类
from abc import ABC, abstractmethod
# 定义抽象类
class Shape(ABC):
"""形状抽象类"""
@abstractmethod
def area(self):
"""计算面积(抽象方法)"""
pass
@abstractmethod
def perimeter(self):
"""计算周长(抽象方法)"""
pass
# 具体方法
def info(self):
return f"面积: {self.area()}, 周长: {self.perimeter()}"
# 具体子类
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() # TypeError
# 实例化具体子类
rect = Rectangle(4, 5)
circle = Circle(3)
print(f"矩形: {rect.info()}")
print(f"圆形: {circle.info()}")
# 使用抽象类作为类型提示
shapes = [rect, circle]
for shape in shapes:
print(f"类型: {type(shape).__name__}, {shape.info()}")多态
多态的实现
python
# 多态:同一操作作用于不同对象,产生不同的结果
class Animal:
def make_sound(self):
pass
class Dog(Animal):
def make_sound(self):
return "汪汪汪"
class Cat(Animal):
def make_sound(self):
return "喵喵喵"
class Cow(Animal):
def make_sound(self):
return "哞哞哞"
# 多态函数
def animal_sound(animal):
"""接受任何 Animal 类型的对象"""
print(f"动物叫声: {animal.make_sound()}")
# 多态的应用
animals = [Dog(), Cat(), Cow()]
for animal in animals:
animal_sound(animal)
# 另一个多态示例:文件处理
class FileHandler:
def read(self):
pass
def write(self, content):
pass
class TextFileHandler(FileHandler):
def __init__(self, filename):
self.filename = filename
def read(self):
with open(self.filename, 'r', encoding='utf-8') as f:
return f.read()
def write(self, content):
with open(self.filename, 'w', encoding='utf-8') as f:
f.write(content)
class JsonFileHandler(FileHandler):
def __init__(self, filename):
self.filename = filename
def read(self):
import json
with open(self.filename, 'r', encoding='utf-8') as f:
return json.load(f)
def write(self, content):
import json
with open(self.filename, 'w', encoding='utf-8') as f:
json.dump(content, f, ensure_ascii=False, indent=2)
# 多态使用
def process_file(handler, content=None):
"""处理文件的通用函数"""
if content:
handler.write(content)
print(f"写入成功: {handler.filename}")
data = handler.read()
print(f"读取内容: {data}")
return data鸭子类型
python
# 鸭子类型(Duck Typing)
# "如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子"
class Duck:
def quack(self):
print("嘎嘎嘎")
def walk(self):
print("摇摇摆摆地走")
class Person:
def quack(self):
print("人在模仿鸭子叫")
def walk(self):
print("人在模仿鸭子走")
class Robot:
def quack(self):
print("机器人发出嘎嘎声")
def walk(self):
print("机器人移动")
# 鸭子类型函数
def make_it_quack_and_walk(thing):
"""不关心对象的具体类型,只关心它是否有这些方法"""
thing.quack()
thing.walk()
# 使用
duck = Duck()
person = Person()
robot = Robot()
print("=== 真正的鸭子 ===")
make_it_quack_and_walk(duck)
print("\n=== 人模仿鸭子 ===")
make_it_quack_and_walk(person)
print("\n=== 机器人 ===")
make_it_quack_and_walk(robot)
# 鸭子类型的实际应用:迭代器协议
class Countdown:
"""倒计时迭代器"""
def __init__(self, start):
self.start = start
def __iter__(self):
return self
def __next__(self):
if self.start <= 0:
raise StopIteration
self.start -= 1
return self.start + 1
# 使用自定义迭代器
print("\n=== 倒计时 ===")
for num in Countdown(5):
print(num, end=" ")
print()
# 可以在任何需要迭代器的地方使用
print(list(Countdown(3)))封装
封装的实现
python
# 封装:隐藏内部实现细节,只暴露必要的接口
class BankAccount:
"""银行账户类"""
def __init__(self, owner, initial_balance=0):
self.owner = owner
self.__balance = initial_balance # 私有属性
self.__transaction_history = [] # 私有属性
def deposit(self, amount):
"""存款"""
if amount <= 0:
raise ValueError("存款金额必须大于0")
self.__balance += amount
self.__add_transaction("存款", amount)
return f"存款成功,当前余额: {self.__balance}元"
def withdraw(self, amount):
"""取款"""
if amount <= 0:
raise ValueError("取款金额必须大于0")
if amount > self.__balance:
raise ValueError("余额不足")
self.__balance -= amount
self.__add_transaction("取款", amount)
return f"取款成功,当前余额: {self.__balance}元"
def get_balance(self):
"""查询余额"""
return self.__balance
def get_transaction_history(self):
"""获取交易历史"""
return self.__transaction_history.copy() # 返回副本,防止外部修改
def __add_transaction(self, trans_type, amount):
"""添加交易记录(私有方法)"""
from datetime import datetime
transaction = {
"type": trans_type,
"amount": amount,
"time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"balance": self.__balance
}
self.__transaction_history.append(transaction)
def transfer(self, other_account, amount):
"""转账"""
# 使用公开方法进行操作
self.withdraw(amount)
other_account.deposit(amount)
return f"转账成功: {self.owner} -> {other_account.owner}, {amount}元"
# 使用封装好的类
account1 = BankAccount("张三", 1000)
account2 = BankAccount("李四", 500)
# 通过公开方法操作
print(account1.deposit(500))
print(account1.withdraw(200))
print(account1.transfer(account2, 300))
# 查询余额
print(f"张三余额: {account1.get_balance()}元")
print(f"李四余额: {account2.get_balance()}元")
# 查看交易历史
print("\n张三的交易记录:")
for record in account1.get_transaction_history():
print(f" {record}")组合与聚合
python
# 组合与聚合
# 组合:整体与部分的生命周期一致
class Engine:
"""发动机"""
def __init__(self, horsepower):
self.horsepower = horsepower
def start(self):
print(f"发动机启动,马力: {self.horsepower}")
def stop(self):
print("发动机停止")
class Wheel:
"""车轮"""
def __init__(self, size):
self.size = size
def rotate(self):
print(f"车轮转动,尺寸: {self.size}")
class Car:
"""汽车(组合关系)"""
def __init__(self, brand, horsepower, wheel_size):
self.brand = brand
# 组合:Car 创建时同时创建 Engine 和 Wheel
self.engine = Engine(horsepower)
self.wheels = [Wheel(wheel_size) for _ in range(4)]
def start(self):
print(f"{self.brand}汽车启动")
self.engine.start()
for wheel in self.wheels:
wheel.rotate()
def stop(self):
self.engine.stop()
print(f"{self.brand}汽车停止")
# 聚合:整体与部分的生命周期独立
class Student:
"""学生"""
def __init__(self, name, student_id):
self.name = name
self.student_id = student_id
def study(self):
print(f"{self.name}正在学习")
class Classroom:
"""教室(聚合关系)"""
def __init__(self, room_number):
self.room_number = room_number
self.students = [] # 学生列表
def add_student(self, student):
"""添加学生"""
self.students.append(student)
print(f"{student.name}加入{self.room_number}教室")
def remove_student(self, student):
"""移除学生"""
if student in self.students:
self.students.remove(student)
print(f"{student.name}离开{self.room_number}教室")
def show_students(self):
"""显示所有学生"""
print(f"{self.room_number}教室的学生:")
for student in self.students:
print(f" - {student.name} ({student.student_id})")
# 使用组合
car = Car("丰田", 200, 18)
car.start()
car.stop()
print()
# 使用聚合
student1 = Student("张三", "001")
student2 = Student("李四", "002")
classroom = Classroom("A101")
classroom.add_student(student1)
classroom.add_student(student2)
classroom.show_students()
# 学生可以独立存在
student1.study()数据类
python
# 数据类(Python 3.7+)
from dataclasses import dataclass, field
from typing import List
# 基本数据类
@dataclass
class Person:
name: str
age: int
city: str = "北京" # 默认值
# 创建实例
p1 = Person("张三", 25)
p2 = Person("李四", 30, "上海")
p3 = Person("张三", 25) # 与 p1 相同
print(f"p1: {p1}")
print(f"p2: {p2}")
print(f"p1 == p3: {p1 == p3}") # 自动实现 __eq__
# 更多选项
@dataclass(frozen=True) # 不可变
class Point:
x: float
y: float
point = Point(3.0, 4.0)
print(f"Point: {point}")
# point.x = 5.0 # FrozenInstanceError
# 带默认工厂的数据类
@dataclass
class Student:
name: str
scores: List[int] = field(default_factory=list)
grade: str = field(default="未评定", repr=False) # 不显示在 repr 中
def add_score(self, score):
self.scores.append(score)
def calculate_average(self):
if not self.scores:
return 0
return sum(self.scores) / len(self.scores)
student = Student("张三")
student.add_score(85)
student.add_score(90)
student.add_score(78)
print(f"学生: {student}")
print(f"平均分: {student.calculate_average():.1f}")
# 继承数据类
@dataclass
class Employee(Person):
department: str
salary: float = 0.0
emp = Employee("王五", 28, "深圳", "技术部", 15000)
print(f"员工: {emp}")小结
本章学习了Python面向对象编程的核心概念:
类与对象:
- 使用
class关键字定义类 - 使用
__init__方法初始化对象 - 区分实例属性和类属性
封装:
- 使用
_和__控制属性访问 - 使用
@property装饰器实现属性访问控制
继承:
- 单继承和多继承
- 使用
super()调用父类方法 - 抽象类和抽象方法
多态:
- 方法重写实现多态
- 鸭子类型
特殊方法:
__str__,__repr__,__eq__等- 运算符重载
面向对象编程是Python的重要特性,掌握OOP可以帮助你编写更加模块化、可维护的代码。
