第 6 章 面向对象编程(OOP)

6.1 类与实例

类是模板,实例是根据模板创建的对象:

class Dog:
    """一只狗(这是 docstring)"""

    species = "Canis lupus"        # 类属性:所有实例共享

    def __init__(self, name, age):
        """构造方法:创建实例时自动调用"""
        self.name = name            # 实例属性:每个实例独有
        self.age = age

    def bark(self):                 # 实例方法:第一个参数永远是 self
        return f"{self.name} 汪汪!"

dog = Dog("旺财", 3)     # 创建实例(不需要 new)
dog.bark()               # '旺财 汪汪!'
dog.name                 # '旺财'
Dog.species              # 'Canis lupus'

self 是什么:指向实例本身的引用,Python 会自动传入(dog.bark() 等价 Dog.bark(dog))。名字叫 self 只是约定,但请永远遵守。

__init__ 不是"创建"对象,而是初始化;真正创建是 __new__(日常用不到)。

6.2 实例属性 vs 类属性

class Counter:
    count = 0                    # 类属性

    def __init__(self):
        Counter.count += 1       # 修改类属性要显式用类名

Counter(); Counter()
Counter.count     # 2

查找顺序:实例属性 → 类属性 → 父类。同名时实例属性遮蔽类属性。给实例做"每个对象一份"的数据用实例属性;全类共享的常量/统计用类属性。

6.3 封装:下划线约定

Python 没有 private 关键字,靠命名约定:

class Account:
    def __init__(self, balance):
        self._balance = balance      # 单下划线:内部使用(约定,仍可访问)
        self.__pin = "1234"          # 双下划线:触发名称改写(name mangling)

acct = Account(100)
acct._balance        # 能访问,但这是"别碰"的信号
acct.__pin           # AttributeError!实际被改名为 _Account__pin

暴露受控读写的惯用法是 @property:

class Account:
    def __init__(self, balance):
        self._balance = balance

    @property
    def balance(self):               # 读:acct.balance
        return self._balance

    @balance.setter
    def balance(self, value):        # 写:acct.balance = 200
        if value < 0:
            raise ValueError("余额不能为负")
        self._balance = value

调用方看起来像访问属性,实际走了方法——接口不用改,这是 Python 版的 getter/setter。

6.4 继承与多态

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

    def speak(self):
        return "..."

class Cat(Animal):                   # Cat 继承 Animal
    def speak(self):                 # 重写(override)
        return "喵"

class Kitten(Cat):
    def __init__(self, name):
        super().__init__(name)       # 调用父类方法(推荐写法)
        self.age = 0

animals = [Animal("A"), Cat("B"), Kitten("C")]
for a in animals:
    print(a.speak())    # ... / 喵 / 喵 —— 多态:同一接口不同行为

要点:

  • 子类重写后想用父类逻辑,用 super().method();

  • isinstance(obj, Cat) 判断类型;issubclass(Kitten, Cat) 判断类关系;

  • Python 鼓励"鸭子类型":"走起来像鸭子、叫起来像鸭子,那它就是鸭子"——不关心类型,只关心有没有需要的方法/属性。

多重继承与 MRO

class FlyingFish(Swimmer, Flyer):    # 支持多继承
    ...

FlyingFish.__mro__    # 方法解析顺序:C3 线性化,从左到右查找

多继承容易出问题(菱形继承),谨慎使用;组合优于继承是通用原则。Mixin(如 class LogMixin)是它较安全的用法。

6.5 常用魔术方法(Dunder Methods)

魔术方法让自定义对象接入 Python 语法:

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

    def __repr__(self):              # repr() 和调试显示
        return f"Vector({self.x}, {self.y})"

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

    def __eq__(self, other):         # == 
        return self.x == other.x and self.y == other.y

    def __add__(self, other):        # +
        return Vector(self.x + other.x, self.y + other.y)

    def __len__(self):               # len()
        return 2

    def __getitem__(self, i):        # v[0]  —— 支持 for 遍历也靠它
        return (self.x, self.y)[i]

v1, v2 = Vector(1, 2), Vector(3, 4)
print(v1 + v2)      # (4, 6)

最常用的一批:

方法触发
__init__初始化
__repr__ / __str__显示
__eq__ / __lt__ 等比较
__add__ / __sub__ 等算术
__len__ __getitem__ __iter__容器协议
__call__像函数一样调用实例
__enter__ / __exit__with 上下文管理

6.6 @classmethod 与 @staticmethod

class Pizza:
    def __init__(self, ingredients):
        self.ingredients = ingredients

    @classmethod
    def margherita(cls):             # 第一个参数是类本身
        return cls(["番茄", "芝士"])  # 常用作替代构造函数

    @staticmethod
    def is_vegetarian(ingredients):  # 不接收 self/cls
        return "肉" not in ingredients

p = Pizza.margherita()
  • 实例方法:需要访问实例数据 → 普通 def;

  • 类方法:需要类信息或做工厂 → @classmethod;

  • 静态方法:跟类和实例都无关、只是逻辑归属 → @staticmethod。

6.7 dataclass:告别样板代码(3.7+)

只存数据的类,用 @dataclass 自动生成 __init__、__repr__、__eq__:

from dataclasses import dataclass, field

@dataclass
class Point:
    x: float
    y: float
    tags: list[str] = field(default_factory=list)   # 可变默认值的正确写法

p = Point(1.0, 2.0)
print(p)          # Point(x=1.0, y=2.0, tags=[])
p == Point(1.0, 2.0)   # True

@dataclass(frozen=True) 可得到不可变数据类,可哈希、能当字典键。

6.8 本章小结

  • __init__(self, ...) 初始化;self 由自动传入;

  • 类属性共享、实例属性独有;_x 约定内部、__x 名称改写;

  • 继承重写 + super();Python 崇尚鸭子类型;

  • 魔术方法让对象自然融入语法;@property 优雅封装;

  • 纯数据类优先 @dataclass。

下一章:模块、包与导入系统。

Python3 基础知识 / 第 6 章 面向对象编程(OOP) 0 字 0 行 cosolar
2026-09-17T11:10:47.715776414Z 2026-09-17T11:25:54.388640254Z