第 6 章 面向对象编程(OOP)
6.1 类与实例
类是模板,实例是根据模板创建的对象:
class Dog:
"""一只狗(这是 docstring)"""
species = "Canis lupus"
def __init__(self, name, age):
"""构造方法:创建实例时自动调用"""
self.name = name
self.age = age
def bark(self):
return f"{self.name} 汪汪!"
dog = Dog("旺财", 3)
dog.bark()
dog.name
Dog.species
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
查找顺序:实例属性 → 类属性 → 父类。同名时实例属性遮蔽类属性。给实例做"每个对象一份"的数据用实例属性;全类共享的常量/统计用类属性。
6.3 封装:下划线约定
Python 没有 private 关键字,靠命名约定:
class Account:
def __init__(self, balance):
self._balance = balance
self.__pin = "1234"
acct = Account(100)
acct._balance
acct.__pin
暴露受控读写的惯用法是 @property:
class Account:
def __init__(self, balance):
self._balance = balance
@property
def balance(self):
return self._balance
@balance.setter
def balance(self, value):
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):
def speak(self):
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__
多继承容易出问题(菱形继承),谨慎使用;组合优于继承是通用原则。Mixin(如 class LogMixin)是它较安全的用法。
6.5 常用魔术方法(Dunder Methods)
魔术方法让自定义对象接入 Python 语法:
class Vector:
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self):
return f"Vector({self.x}, {self.y})"
def __str__(self):
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):
return 2
def __getitem__(self, i):
return (self.x, self.y)[i]
v1, v2 = Vector(1, 2), Vector(3, 4)
print(v1 + v2)
最常用的一批:
| 方法 | 触发 |
|---|
__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):
return "肉" not in ingredients
p = Pizza.margherita()
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)
p == Point(1.0, 2.0)
@dataclass(frozen=True) 可得到不可变数据类,可哈希、能当字典键。
6.8 本章小结
__init__(self, ...) 初始化;self 由自动传入;
类属性共享、实例属性独有;_x 约定内部、__x 名称改写;
继承重写 + super();Python 崇尚鸭子类型;
魔术方法让对象自然融入语法;@property 优雅封装;
纯数据类优先 @dataclass。
下一章:模块、包与导入系统。