Python 面向对象编程

Python(03)学习笔记

发布于 2025-04-12

类与对象

定义类

在 Python 中,使用 class 关键字定义类:

class Person:
    """人类的简单模型"""

    def __init__(self, name, age):
        """初始化属性name和age"""
        self.name = name
        self.age = age

    def say_hello(self):
        """问候方法"""
        return f"你好,我是{self.name},今年{self.age}岁!"

创建对象

定义类后,可以创建该类的实例(对象):

# 创建Person类的实例
alice = Person("爱丽丝", 25)

# 访问对象的属性
print(alice.name)  # 输出: 爱丽丝

# 调用对象的方法
print(alice.say_hello())  # 输出: 你好,我是爱丽丝,今年25岁!

__init__ 方法

__init__ 是 Python 的特殊方法(构造器),在创建对象时自动调用,用于初始化对象的属性。 self 参数表示对象本身,是约定俗成的命名,必须是方法的第一个参数。

封装

封装是将数据和方法绑定在一起,限制外部对对象内部数据的直接访问,只能通过对象提供的方法进行交互。

私有属性和方法

在 Python 中,约定以双下划线( __ )开头的属性或方法为私有:

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.__balance = balance  # 私有属性

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            return True
        return False

    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            return True
        return False

    def get_balance(self):
        return self.__balance

尽管 Python 没有真正的私有属性(可以通过 _类名__属性名 访问),但这种命名约定提供了一定程度的封装。

继承

继承允许我们基于现有类(父类/基类)创建新类(子类/派生类),子类继承父类的属性和方法,并可以添加新的属性和方法或覆盖父类方法。

继承实现示例

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

    def make_sound(self):
        pass

    def move(self):
        print(f"{self.name}正在移动")

class Dog(Animal):
    def __init__(self, name, age, breed):
        super().__init__(name, age)  # 调用父类的__init__方法
        self.breed = breed

    def make_sound(self):
        return "汪汪!"

    def fetch(self):
        return f"{self.name}在捡球"

# 创建Dog实例
dog = Dog("旺财", 3, "拉布拉多")
print(dog.name)  # 继承自Animal的属性
print(dog.make_sound())  # 重写的方法
print(dog.fetch())  # Dog特有的方法

super()函数

super() 函数用于调用父类方法,避免直接引用父类,使代码更加灵活,特别是在多重继承的情况下。

多态

多态允许不同类的对象对同一消息(方法调用)做出不同的响应。在 Python 中,由于其动态类型特性,多态是自然而然的:

def animal_sound(animal):
    return animal.make_sound()

dog = Dog("旺财", 3, "拉布拉多")
cat = Cat("咪咪", 2, "橘色")
bird = Bird("小黄", 1, 15.5)

print(animal_sound(dog))  # 输出: 汪汪!
print(animal_sound(cat))  # 输出: 喵喵!
print(animal_sound(bird))  # 输出: 啾啾!

这里的 animal_sound 函数不关心传入的对象类型,只要对象实现了 make_sound 方法即可。

特殊方法与属性

Python 提供了许多特殊方法(也称为"魔术方法"或"双下方法"),以双下划线开头和结尾,用于实现对象的特定行为。

常用特殊方法

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

    def __str__(self):
        """返回对象的字符串表示,用于print()"""
        return f"Vector({self.x}, {self.y})"

    def __repr__(self):
        """返回对象的"官方"字符串表示,用于调试"""
        return f"Vector({self.x}, {self.y})"

    def __add__(self, other):
        """实现加法操作符+"""
        return Vector(self.x + other.x, self.y + other.y)

    def __eq__(self, other):
        """实现等于操作符=="""
        return self.x == other.x and self.y == other.y

    def __len__(self):
        """实现len()函数"""
        return int((self.x**2 + self.y**2)**0.5)

使用这些特殊方法,我们可以使自定义对象的行为更像 Python 内置类型:

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

print(v1)  # 调用__str__
print(v1 + v2)  # 调用__add__
print(v1 == v2)  # 调用__eq__
print(len(v1))  # 调用__len__

高级特性

类方法和静态方法

  • 类方法(classmethod): 操作类而非实例的方法,第一个参数是类本身( cls )
  • 静态方法(staticmethod): 与类关联但不操作类或实例的方法
class MathUtils:
    def __init__(self, a=0, b=0):
        self.a = a
        self.b = b

    @staticmethod
    def add(a, b):
        return a + b

    @classmethod
    def from_string(cls, string):
        a, b = map(int, string.split(','))
        return cls(a, b)  # 创建类实例

属性装饰器

使用 @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)  # 访问属性
circle.radius = 10    # 设置属性
print(circle.area)    # 计算属性

多重继承

Python 支持多重继承,一个类可以继承多个父类:

class A:
    def method_a(self):
        return "A方法"

class B:
    def method_b(self):
        return "B方法"

class C(A, B):  # 继承A和B
    def method_c(self):
        return "C方法"

# 使用
c = C()
print(c.method_a())  # 继承自A
print(c.method_b())  # 继承自B
print(c.method_c())  # C自己的方法

在多重继承中,Python 使用 C3 线性化算法来确定方法解析顺序(MRO),可以通过 类名.__mro__ 查看。

抽象基类

使用 abc 模块可以定义抽象基类,强制子类实现特定方法:

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)

尝试实例化抽象基类或不实现抽象方法的子类会引发错误。

最佳实践

1. 遵循命名约定

  • 类名使用驼峰命名法(CamelCase)
  • 方法和属性使用小写下划线命名法(snake_case)
  • 常量使用大写下划线命名法(UPPERCASE_WITH_UNDERSCORES)
  • 受保护的属性和方法以单下划线开头(_protected)
  • 私有属性和方法以双下划线开头(__private)

2. 单一职责原则

每个类应该只有一个职责,一个改变的理由。这使得类更加内聚、易于维护。

3. 组合优于继承

过度使用继承可能导致复杂的层次结构。在许多情况下,组合(让一个类包含另一个类的实例)是更好的选择:

# 继承
class Car(Vehicle):
    pass

# 组合
class Car:
    def __init__(self):
        self.engine = Engine()
        self.wheels = [Wheel() for _ in range(4)]

4. 使用描述性的文档字符串

为类和方法提供清晰的文档字符串,说明其用途、参数和返回值:

class Person:
    """
    表示一个具有姓名和年龄的人。
    属性:
        name (str): 人的姓名
        age (int): 人的年龄
    """

    def __init__(self, name, age):
        """
        初始化Person实例。参数:
            name (str): 人的姓名
            age (int): 人的年龄
        """
        self.name = name
        self.age = age

5. 使用类型提示

在 Python3.5+中,可以使用类型提示提高代码的可读性和可维护性:

from typing import List, Optional

class ShoppingCart:
    def __init__(self) -> None:
        self.items: List[str] = []
    def add_item(self, item: str) -> None:
        self.items.append(item)

    def get_item(self, index: int) -> Optional[str]:
        if 0 <= index < len(self.items):
            return self.items[index]
        return None