Skip to content

面向对象编程

Python 是一门多范式编程语言,支持面向对象编程(OOP)。

类与对象

定义类

python
class Person:
    """人员类"""

    # 类属性
    species = "人类"

    # 构造方法
    def __init__(self, name, age):
        # 实例属性
        self.name = name
        self.age = age

    # 实例方法
    def introduce(self):
        print(f"我叫{self.name},今年{self.age}岁")

    # 类方法
    @classmethod
    def from_birth_year(cls, name, birth_year):
        """根据出生年份创建实例"""
        age = 2024 - birth_year
        return cls(name, age)

    # 静态方法
    @staticmethod
    def is_adult(age):
        """判断是否成年"""
        return age >= 18

# 创建实例
person = Person("张三", 25)
person.introduce()  # 我叫张三,今年25岁

# 访问属性
print(person.name)      # 张三
print(Person.species)   # 人类

# 调用类方法
person2 = Person.from_birth_year("李四", 1995)
person2.introduce()

# 调用静态方法
print(Person.is_adult(20))  # True

实例属性与类属性

python
class Student:
    # 类属性
    school = "清华大学"
    count = 0

    def __init__(self, name, grade):
        # 实例属性
        self.name = name
        self.grade = grade
        Student.count += 1

# 类属性被所有实例共享
s1 = Student("张三", "大一")
s2 = Student("李四", "大二")

print(Student.count)    # 2
print(s1.school)        # 清华大学
print(s2.school)        # 清华大学

# 修改类属性
Student.school = "北京大学"
print(s1.school)        # 北京大学

# 实例属性会遮蔽同名类属性
s1.school = "复旦大学"  # 创建实例属性
print(s1.school)        # 复旦大学(实例属性)
print(s2.school)        # 北京大学(类属性)

# 删除实例属性
del s1.school
print(s1.school)        # 北京大学(回到类属性)

继承

基本继承

python
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        pass

    def info(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("旺财", "金毛")
dog.info()    # 我是旺财
dog.speak()   # 旺财说: 汪汪!
dog.fetch()   # 旺财在捡球

cat = Cat("咪咪")
cat.speak()   # 咪咪说: 喵喵!

多继承

python
class Flyable:
    def fly(self):
        print(f"{self.name}在飞翔")

class Swimmable:
    def swim(self):
        print(f"{self.name}在游泳")

class Duck(Animal, Flyable, Swimmable):
    def speak(self):
        print(f"{self.name}说: 嘎嘎!")

duck = Duck("唐老鸭")
duck.speak()  # 唐老鸭说: 嘎嘎!
duck.fly()    # 唐老鸭在飞翔
duck.swim()   # 唐老鸭在游泳

# MRO (Method Resolution Order)
print(Duck.__mro__)
# (<class '__main__.Duck'>, <class '__main__.Animal'>,
#  <class '__main__.Flyable'>, <class '__main__.Swimmable'>, <class 'object'>)

super() 函数

python
class A:
    def __init__(self):
        print("A.__init__")

    def method(self):
        print("A.method")

class B(A):
    def __init__(self):
        super().__init__()  # 调用父类构造方法
        print("B.__init__")

    def method(self):
        super().method()    # 调用父类方法
        print("B.method")

class C(A):
    def __init__(self):
        super().__init__()
        print("C.__init__")

    def method(self):
        super().method()
        print("C.method")

class D(B, C):
    def __init__(self):
        super().__init__()  # 按 MRO 顺序调用
        print("D.__init__")

    def method(self):
        super().method()
        print("D.method")

d = D()
# A.__init__
# C.__init__
# B.__init__
# D.__init__

d.method()
# A.method
# C.method
# B.method
# D.method

多态

方法重写

python
class Shape:
    def area(self):
        pass

    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):
        return 3.14159 * self.radius ** 2

    def perimeter(self):
        return 2 * 3.14159 * self.radius

# 多态
shapes = [Rectangle(4, 5), Circle(3), Rectangle(2, 6)]

for shape in shapes:
    print(f"面积: {shape.area():.2f}")
    print(f"周长: {shape.perimeter():.2f}")
    print("---")

鸭子类型

python
# "如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子"

class Duck:
    def swim(self):
        print("鸭子游泳")

    def quack(self):
        print("鸭子叫")

class Person:
    def swim(self):
        print("人游泳")

    def quack(self):
        print("人模仿鸭子叫")

def make_it_quack(thing):
    # 不关心类型,只关心是否有 quack 方法
    thing.quack()

duck = Duck()
person = Person()

make_it_quack(duck)    # 鸭子叫
make_it_quack(person)  # 人模仿鸭子叫

封装

访问控制

python
class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner           # 公有属性
        self._balance = balance      # 保护属性(约定)
        self.__secret = "password"   # 私有属性(名称修饰)

    # 公有方法
    def deposit(self, amount):
        if amount > 0:
            self._balance += amount
            print(f"存款{amount},余额{self._balance}")

    # 保护方法
    def _validate(self, amount):
        return amount > 0

    # 私有方法
    def __log(self, message):
        print(f"[LOG] {message}")

    # 属性访问器
    @property
    def balance(self):
        return self._balance

account = BankAccount("张三", 1000)
account.deposit(500)

print(account.owner)      # 张三
print(account._balance)   # 1000(可以访问,但不推荐)
# print(account.__secret) # AttributeError

# 名称修饰后可以访问(不推荐)
print(account._BankAccount__secret)  # password

property 装饰器

python
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
print(temp.fahrenheit)  # 77.0

temp.celsius = 30
print(temp.fahrenheit)  # 86.0

temp.fahrenheit = 100
print(temp.celsius)     # 37.77...

# temp.celsius = -300  # ValueError

特殊方法

对象表示

python
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        """str() 和 print() 调用"""
        return f"Point({self.x}, {self.y})"

    def __repr__(self):
        """repr() 调用,应该返回可以重建对象的字符串"""
        return f"Point({self.x}, {self.y})"

    def __format__(self, format_spec):
        """format() 调用"""
        if format_spec == "polar":
            r = (self.x ** 2 + self.y ** 2) ** 0.5
            theta = math.atan2(self.y, self.x)
            return f"r={r:.2f}, θ={theta:.2f}"
        return str(self)

p = Point(3, 4)
print(p)           # Point(3, 4)
print(repr(p))     # Point(3, 4)
print(f"{p:polar}")  # r=5.00, θ=0.93

比较运算

python
from functools import total_ordering

@total_ordering  # 只需实现 __eq__ 和一个比较方法
class Money:
    def __init__(self, amount, currency="CNY"):
        self.amount = amount
        self.currency = currency

    def __eq__(self, other):
        if not isinstance(other, Money):
            return NotImplemented
        return self.amount == other.amount and self.currency == other.currency

    def __lt__(self, other):
        if not isinstance(other, Money):
            return NotImplemented
        if self.currency != other.currency:
            raise ValueError("货币不同无法比较")
        return self.amount < other.amount

m1 = Money(100)
m2 = Money(200)
m3 = Money(100)

print(m1 == m3)   # True
print(m1 < m2)    # True
print(m1 <= m2)   # True
print(m1 > m2)    # False

算术运算

python
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        """加法: v1 + v2"""
        return Vector(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        """减法: v1 - v2"""
        return Vector(self.x - other.x, self.y - other.y)

    def __mul__(self, scalar):
        """乘法: v * scalar"""
        return Vector(self.x * scalar, self.y * scalar)

    def __rmul__(self, scalar):
        """右乘: scalar * v"""
        return self.__mul__(scalar)

    def __truediv__(self, scalar):
        """除法: v / scalar"""
        return Vector(self.x / scalar, self.y / scalar)

    def __neg__(self):
        """负号: -v"""
        return Vector(-self.x, -self.y)

    def __abs__(self):
        """绝对值: abs(v)"""
        return (self.x ** 2 + self.y ** 2) ** 0.5

    def __str__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(3, 4)
v2 = Vector(1, 2)

print(v1 + v2)    # Vector(4, 6)
print(v1 - v2)    # Vector(2, 2)
print(v1 * 2)     # Vector(6, 8)
print(2 * v1)     # Vector(6, 8)
print(-v1)        # Vector(-3, -4)
print(abs(v1))    # 5.0

容器协议

python
class Deck:
    def __init__(self):
        self.cards = list(range(1, 53))

    def __len__(self):
        """len()"""
        return len(self.cards)

    def __getitem__(self, index):
        """索引访问: deck[0]"""
        return self.cards[index]

    def __setitem__(self, index, value):
        """索引赋值: deck[0] = value"""
        self.cards[index] = value

    def __delitem__(self, index):
        """删除: del deck[0]"""
        del self.cards[index]

    def __contains__(self, item):
        """成员检测: item in deck"""
        return item in self.cards

    def __iter__(self):
        """迭代: for card in deck"""
        return iter(self.cards)

deck = Deck()
print(len(deck))       # 52
print(deck[0])         # 1
print(1 in deck)       # True

for card in deck[:5]:
    print(card)

上下文管理器

python
class FileManager:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
        self.file = None

    def __enter__(self):
        """进入 with 语句"""
        self.file = open(self.filename, self.mode)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        """退出 with 语句"""
        if self.file:
            self.file.close()
        # 返回 True 会抑制异常
        return False

# 使用
with FileManager("test.txt", "w") as f:
    f.write("Hello")

# 使用 contextlib
from contextlib import contextmanager

@contextmanager
def file_manager(filename, mode):
    f = open(filename, mode)
    try:
        yield f
    finally:
        f.close()

with file_manager("test.txt", "r") as f:
    print(f.read())

可调用对象

python
class Multiplier:
    def __init__(self, factor):
        self.factor = factor

    def __call__(self, value):
        """使实例可调用"""
        return value * self.factor

double = Multiplier(2)
triple = Multiplier(3)

print(double(5))   # 10
print(triple(5))   # 15

# 检查是否可调用
print(callable(double))  # True

数据类

@dataclass 装饰器

python
from dataclasses import dataclass, field
from typing import List

@dataclass
class Person:
    name: str
    age: int
    city: str = "北京"  # 默认值

    def introduce(self):
        print(f"我叫{self.name}{self.age}岁,来自{self.city}")

p1 = Person("张三", 25)
p2 = Person("李四", 30, "上海")

print(p1)  # Person(name='张三', age=25, city='北京')
print(p1 == p2)  # False

# 更多选项
@dataclass(order=True)  # 支持比较
class Student:
    grade: int          # 排序依据
    name: str = field(compare=False)  # 不参与比较
    scores: List[int] = field(default_factory=list)  # 可变默认值

s1 = Student(1, "张三", [90, 85, 88])
s2 = Student(2, "李四", [95, 92, 90])
print(s1 < s2)  # True

命名元组

python
from collections import namedtuple
from typing import NamedTuple

# 使用 namedtuple
Point = namedtuple('Point', ['x', 'y'])
p1 = Point(3, 4)
print(p1.x, p1.y)  # 3 4

# 使用 NamedTuple(支持类型注解)
class Point3D(NamedTuple):
    x: float
    y: float
    z: float

    def distance(self):
        return (self.x ** 2 + self.y ** 2 + self.z ** 2) ** 0.5

p2 = Point3D(1, 2, 2)
print(p2.distance())  # 3.0

抽象基类

abc 模块

python
from abc import ABC, abstractmethod

class Shape(ABC):
    """抽象基类"""

    @abstractmethod
    def area(self):
        """计算面积"""
        pass

    @abstractmethod
    def perimeter(self):
        """计算周长"""
        pass

    def info(self):
        """具体方法"""
        print(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)

# shape = Shape()  # TypeError: 不能实例化抽象类
rect = Rectangle(4, 5)
rect.info()  # 面积: 20, 周长: 18

实践示例

链表实现

python
class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

    def __repr__(self):
        return f"Node({self.value})"

class LinkedList:
    def __init__(self):
        self.head = None
        self._length = 0

    def append(self, value):
        """添加元素"""
        node = Node(value)
        if not self.head:
            self.head = node
        else:
            current = self.head
            while current.next:
                current = current.next
            current.next = node
        self._length += 1

    def prepend(self, value):
        """头部添加"""
        node = Node(value)
        node.next = self.head
        self.head = node
        self._length += 1

    def delete(self, value):
        """删除元素"""
        if not self.head:
            return

        if self.head.value == value:
            self.head = self.head.next
            self._length -= 1
            return

        current = self.head
        while current.next:
            if current.next.value == value:
                current.next = current.next.next
                self._length -= 1
                return
            current = current.next

    def __len__(self):
        return self._length

    def __iter__(self):
        current = self.head
        while current:
            yield current.value
            current = current.next

    def __str__(self):
        return " -> ".join(str(v) for v in self)

# 使用
lst = LinkedList()
lst.append(1)
lst.append(2)
lst.append(3)
lst.prepend(0)
print(lst)  # 0 -> 1 -> 2 -> 3
print(len(lst))  # 4

银行账户系统

python
from dataclasses import dataclass
from datetime import datetime
from typing import List
from enum import Enum

class TransactionType(Enum):
    DEPOSIT = "存款"
    WITHDRAW = "取款"
    TRANSFER = "转账"

@dataclass
class Transaction:
    type: TransactionType
    amount: float
    balance: float
    timestamp: datetime = field(default_factory=datetime.now)
    description: str = ""

class BankAccount:
    def __init__(self, owner: str, balance: float = 0):
        self.owner = owner
        self._balance = balance
        self._transactions: List[Transaction] = []

    @property
    def balance(self) -> float:
        return self._balance

    def deposit(self, amount: float, description: str = ""):
        if amount <= 0:
            raise ValueError("存款金额必须大于0")
        self._balance += amount
        self._transactions.append(Transaction(
            TransactionType.DEPOSIT, amount, self._balance, description=description
        ))

    def withdraw(self, amount: float, description: str = ""):
        if amount <= 0:
            raise ValueError("取款金额必须大于0")
        if amount > self._balance:
            raise ValueError("余额不足")
        self._balance -= amount
        self._transactions.append(Transaction(
            TransactionType.WITHDRAW, amount, self._balance, description=description
        ))

    def transfer(self, other: 'BankAccount', amount: float):
        self.withdraw(amount, f"转账给{other.owner}")
        other.deposit(amount, f"来自{self.owner}的转账")

    def get_statement(self):
        print(f"\n{'='*50}")
        print(f"账户持有人: {self.owner}")
        print(f"当前余额: {self._balance:.2f}")
        print(f"{'='*50}")
        print(f"{'类型':<8}{'金额':<12}{'余额':<12}{'时间':<20}{'备注'}")
        print("-" * 50)
        for t in self._transactions:
            print(f"{t.type.value:<8}{t.amount:<12.2f}{t.balance:<12.2f}"
                  f"{t.timestamp.strftime('%Y-%m-%d %H:%M:%S'):<20}{t.description}")

# 使用
acc1 = BankAccount("张三", 1000)
acc2 = BankAccount("李四", 500)

acc1.deposit(500, "工资")
acc1.withdraw(200, "购物")
acc1.transfer(acc2, 300)

acc1.get_statement()
acc2.get_statement()