HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • Python 新手入门教程

    • Python新手入门教程 - 零基础学Python,8章+HTTP服务实战 | HiHuo
    • Python简介与环境搭建 - 安装配置Python开发环境 | HiHuo
    • Python基础语法 - 缩进、变量、注释、输入输出 | HiHuo
    • Python数据类型详解 - 字符串、列表、字典、元组 | HiHuo
    • Python流程控制 - if条件判断、for/while循环 | HiHuo
    • Python函数与模块 - 定义函数、参数传递、模块导入 | HiHuo
    • Python面向对象编程 - 类、对象、继承、多态 | HiHuo
    • Python文件与异常处理 - 读写文件、try/except异常捕获 | HiHuo
    • Python HTTP服务项目实战 - 构建待办事项API服务 | HiHuo

面向对象编程

面向对象编程(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 简化数据类的定义
Prev
Python函数与模块 - 定义函数、参数传递、模块导入 | HiHuo
Next
Python文件与异常处理 - 读写文件、try/except异常捕获 | HiHuo