面向对象编程
面向对象编程(OOP)是一种编程范式,通过"类"和"对象"来组织代码。
类与对象
基本概念
┌─────────────────────────────────────────────────────────┐
│ 类 (Class) │
│ 类是对象的模板/蓝图,定义了对象的属性和行为 │
├─────────────────────────────────────────────────────────┤
│ 对象 (Object) │
│ 对象是类的实例,具有类定义的属性和方法 │
└─────────────────────────────────────────────────────────┘
例如:
类 = 汽车设计图
对象 = 具体的一辆汽车
定义类
class Dog:
"""这是一个狗类"""
# 类属性(所有实例共享)
species = "犬科"
# 初始化方法(构造函数)
def __init__(self, name, age):
# 实例属性(每个实例独有)
self.name = name
self.age = age
# 实例方法
def bark(self):
print(f"{self.name} 汪汪叫!")
def introduce(self):
print(f"我是{self.name},今年{self.age}岁")
# 创建对象(实例化)
dog1 = Dog("旺财", 3)
dog2 = Dog("小白", 2)
# 访问属性
print(dog1.name) # 旺财
print(dog1.species) # 犬科
# 调用方法
dog1.bark() # 旺财 汪汪叫!
dog2.introduce() # 我是小白,今年2岁
self 的作用
class Person:
def __init__(self, name):
self.name = name # self 指向当前实例
def greet(self):
# self.name 访问当前实例的 name 属性
print(f"Hello, I'm {self.name}")
p1 = Person("张三")
p2 = Person("李四")
p1.greet() # Hello, I'm 张三
p2.greet() # Hello, I'm 李四
# 本质上等价于
Person.greet(p1) # Hello, I'm 张三
属性
实例属性 vs 类属性
class Student:
# 类属性:所有实例共享
school = "北京大学"
student_count = 0
def __init__(self, name):
# 实例属性:每个实例独有
self.name = name
Student.student_count += 1
s1 = Student("张三")
s2 = Student("李四")
print(s1.school) # 北京大学
print(Student.school) # 北京大学
print(Student.student_count) # 2
# 修改类属性
Student.school = "清华大学"
print(s1.school) # 清华大学
print(s2.school) # 清华大学
# 注意:通过实例修改会创建实例属性
s1.school = "复旦大学"
print(s1.school) # 复旦大学(实例属性)
print(s2.school) # 清华大学(类属性)
私有属性
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self._balance = balance # 约定私有(单下划线)
self.__secret = "机密" # 真正私有(双下划线)
def get_balance(self):
return self._balance
def deposit(self, amount):
if amount > 0:
self._balance += amount
account = BankAccount("张三", 1000)
print(account.owner) # 张三
print(account._balance) # 1000(可以访问,但不建议)
# print(account.__secret) # AttributeError
print(account._BankAccount__secret) # 机密(名称改编后可访问)
property 装饰器
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""获取半径"""
return self._radius
@radius.setter
def radius(self, value):
"""设置半径"""
if value < 0:
raise ValueError("半径不能为负数")
self._radius = value
@property
def area(self):
"""计算面积(只读属性)"""
return 3.14159 * self._radius ** 2
circle = Circle(5)
print(circle.radius) # 5
print(circle.area) # 78.53975
circle.radius = 10 # 使用 setter
print(circle.area) # 314.159
# circle.radius = -1 # ValueError: 半径不能为负数
# circle.area = 100 # AttributeError(只读)
方法
实例方法、类方法、静态方法
class MyClass:
class_attr = "类属性"
def __init__(self, value):
self.instance_attr = value
# 实例方法:第一个参数是 self
def instance_method(self):
print(f"实例方法,访问实例属性: {self.instance_attr}")
# 类方法:第一个参数是 cls
@classmethod
def class_method(cls):
print(f"类方法,访问类属性: {cls.class_attr}")
# 静态方法:没有特殊参数
@staticmethod
def static_method():
print("静态方法,不访问类或实例属性")
obj = MyClass("实例值")
# 调用方式
obj.instance_method() # 实例方法
MyClass.class_method() # 类方法
MyClass.static_method() # 静态方法
obj.class_method() # 也可以通过实例调用
obj.static_method() # 也可以通过实例调用
典型案例:日期类
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def __str__(self):
return f"{self.year}-{self.month:02d}-{self.day:02d}"
@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
today = datetime.date.today()
return cls(today.year, today.month, today.day)
@staticmethod
def is_valid_date(year, month, day):
"""验证日期是否有效"""
if month < 1 or month > 12:
return False
if day < 1 or day > 31:
return False
return True
# 使用
d1 = Date(2024, 1, 15)
d2 = Date.from_string("2024-06-20")
d3 = Date.today()
print(d1) # 2024-01-15
print(d2) # 2024-06-20
print(Date.is_valid_date(2024, 13, 1)) # False
继承
基本继承
# 父类(基类)
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print("动物在叫")
def eat(self):
print(f"{self.name}在吃东西")
# 子类(派生类)
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调用父类构造函数
self.breed = breed
def speak(self): # 重写父类方法
print(f"{self.name}汪汪叫")
def fetch(self): # 新增方法
print(f"{self.name}在捡球")
class Cat(Animal):
def speak(self):
print(f"{self.name}喵喵叫")
# 使用
dog = Dog("旺财", "金毛")
cat = Cat("咪咪")
dog.speak() # 旺财汪汪叫
dog.eat() # 旺财在吃东西(继承的方法)
dog.fetch() # 旺财在捡球
cat.speak() # 咪咪喵喵叫
# 检查继承关系
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Animal)) # True
print(issubclass(Dog, Animal)) # True
多重继承
class Flyable:
def fly(self):
print("我会飞")
class Swimmable:
def swim(self):
print("我会游泳")
class Duck(Animal, Flyable, Swimmable):
def speak(self):
print(f"{self.name}嘎嘎叫")
duck = Duck("唐老鸭")
duck.speak() # 唐老鸭嘎嘎叫
duck.fly() # 我会飞
duck.swim() # 我会游泳
方法解析顺序 (MRO)
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
class C(A):
def method(self):
print("C")
class D(B, C):
pass
d = D()
d.method() # B
# 查看 MRO
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
魔术方法
魔术方法(双下划线方法)用于自定义类的行为。
常用魔术方法
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
"""str() 和 print() 时调用"""
return f"Vector({self.x}, {self.y})"
def __repr__(self):
"""repr() 时调用,通常用于调试"""
return f"Vector({self.x!r}, {self.y!r})"
def __len__(self):
"""len() 时调用"""
return 2
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 __eq__(self, other):
"""== 运算符"""
return self.x == other.x and self.y == other.y
def __abs__(self):
"""abs() 时调用"""
return (self.x ** 2 + self.y ** 2) ** 0.5
def __bool__(self):
"""bool() 时调用"""
return self.x != 0 or self.y != 0
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 * 2) # Vector(6, 8)
print(abs(v1)) # 5.0
print(v1 == v2) # False
print(len(v1)) # 2
容器类魔术方法
class MyList:
def __init__(self, items=None):
self._items = items or []
def __len__(self):
return len(self._items)
def __getitem__(self, index):
"""支持索引访问"""
return self._items[index]
def __setitem__(self, index, value):
"""支持索引赋值"""
self._items[index] = value
def __delitem__(self, index):
"""支持 del"""
del self._items[index]
def __contains__(self, item):
"""支持 in 运算符"""
return item in self._items
def __iter__(self):
"""支持迭代"""
return iter(self._items)
def append(self, item):
self._items.append(item)
ml = MyList([1, 2, 3])
print(len(ml)) # 3
print(ml[0]) # 1
ml[0] = 10
print(ml[0]) # 10
print(2 in ml) # True
for item in ml:
print(item) # 10, 2, 3
典型案例:自定义字符串类
class MyString:
def __init__(self, value=""):
self._value = str(value)
def __str__(self):
return self._value
def __repr__(self):
return f"MyString({self._value!r})"
def __len__(self):
return len(self._value)
def __add__(self, other):
return MyString(self._value + str(other))
def __mul__(self, n):
return MyString(self._value * n)
def __eq__(self, other):
return self._value == str(other)
def __getitem__(self, key):
return self._value[key]
def __contains__(self, item):
return item in self._value
def upper(self):
return MyString(self._value.upper())
def lower(self):
return MyString(self._value.lower())
s = MyString("Hello")
print(s + " World") # Hello World
print(s * 3) # HelloHelloHello
print(len(s)) # 5
print(s[0]) # H
print("ell" in s) # True
抽象类与接口
from abc import ABC, abstractmethod
class Shape(ABC):
"""抽象基类"""
@abstractmethod
def area(self):
"""抽象方法:子类必须实现"""
pass
@abstractmethod
def perimeter(self):
pass
def describe(self):
"""普通方法:子类可以继承"""
print(f"这是一个图形,面积: {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 3.14159 * self.radius ** 2
def perimeter(self):
return 2 * 3.14159 * self.radius
# shape = Shape() # TypeError: 不能实例化抽象类
rect = Rectangle(5, 3)
rect.describe() # 这是一个图形,面积: 15.00
circle = Circle(4)
print(circle.area()) # 50.26...
print(circle.perimeter()) # 25.13...
数据类 (dataclass)
Python 3.7+ 提供的简化类定义方式。
from dataclasses import dataclass, field
from typing import List
@dataclass
class Person:
name: str
age: int
city: str = "北京" # 默认值
@dataclass
class Student:
name: str
age: int
scores: List[int] = field(default_factory=list)
def average(self):
if not self.scores:
return 0
return sum(self.scores) / len(self.scores)
# 自动生成 __init__, __repr__, __eq__ 等方法
p1 = Person("张三", 25)
p2 = Person("张三", 25)
print(p1) # Person(name='张三', age=25, city='北京')
print(p1 == p2) # True
s = Student("李四", 20, [85, 90, 78])
print(s.average()) # 84.33...
典型案例:银行账户系统
class BankAccount:
"""银行账户类"""
# 类属性:利率
interest_rate = 0.02
def __init__(self, owner, balance=0):
self.owner = owner
self._balance = balance
self._transactions = []
@property
def balance(self):
return self._balance
def deposit(self, amount):
"""存款"""
if amount <= 0:
raise ValueError("存款金额必须大于0")
self._balance += amount
self._transactions.append(f"存款: +{amount}")
return self._balance
def withdraw(self, amount):
"""取款"""
if amount <= 0:
raise ValueError("取款金额必须大于0")
if amount > self._balance:
raise ValueError("余额不足")
self._balance -= amount
self._transactions.append(f"取款: -{amount}")
return self._balance
def transfer(self, other, amount):
"""转账"""
self.withdraw(amount)
other.deposit(amount)
self._transactions.append(f"转账给{other.owner}: -{amount}")
def add_interest(self):
"""添加利息"""
interest = self._balance * self.interest_rate
self.deposit(interest)
def get_transactions(self):
"""获取交易记录"""
return self._transactions.copy()
def __str__(self):
return f"账户({self.owner}, 余额: ¥{self._balance:.2f})"
class SavingsAccount(BankAccount):
"""储蓄账户,利率更高"""
interest_rate = 0.05
def __init__(self, owner, balance=0, min_balance=100):
super().__init__(owner, balance)
self.min_balance = min_balance
def withdraw(self, amount):
if self._balance - amount < self.min_balance:
raise ValueError(f"余额不能低于最低限额 ¥{self.min_balance}")
return super().withdraw(amount)
# 使用示例
account1 = BankAccount("张三", 1000)
account2 = SavingsAccount("李四", 2000)
print(account1) # 账户(张三, 余额: ¥1000.00)
account1.deposit(500)
account1.withdraw(200)
account1.transfer(account2, 300)
print(account1) # 账户(张三, 余额: ¥1000.00)
print(account2) # 账户(李四, 余额: ¥2300.00)
print("张三的交易记录:")
for t in account1.get_transactions():
print(f" {t}")
练习
练习1:图书管理类
class Book:
def __init__(self, title, author, isbn):
self.title = title
self.author = author
self.isbn = isbn
self.is_borrowed = False
def borrow(self):
if self.is_borrowed:
return False
self.is_borrowed = True
return True
def return_book(self):
self.is_borrowed = False
def __str__(self):
status = "已借出" if self.is_borrowed else "可借阅"
return f"《{self.title}》- {self.author} [{status}]"
class Library:
def __init__(self, name):
self.name = name
self.books = []
def add_book(self, book):
self.books.append(book)
def find_book(self, title):
for book in self.books:
if book.title == title:
return book
return None
def available_books(self):
return [b for b in self.books if not b.is_borrowed]
# 测试
lib = Library("市图书馆")
lib.add_book(Book("Python入门", "张三", "ISBN001"))
lib.add_book(Book("数据结构", "李四", "ISBN002"))
book = lib.find_book("Python入门")
book.borrow()
print(book) # 《Python入门》- 张三 [已借出]
小结
- 类是对象的模板,对象是类的实例
__init__是构造函数,self指向实例本身@property用于创建只读或可控属性@classmethod和@staticmethod是类方法和静态方法- 继承实现代码复用,
super()调用父类方法 - 魔术方法用于自定义类的行为
ABC和@abstractmethod创建抽象类@dataclass简化数据类的定义