跳到主要内容

类与面向对象

问题

Python 的面向对象有哪些核心概念?MRO 如何解析?魔术方法有哪些?描述符协议是什么?

答案

类的基础

class Person:
# 类变量:所有实例共享
species = "Homo sapiens"

def __init__(self, name: str, age: int):
# 实例变量:每个实例独立
self.name = name
self.age = age

def greet(self) -> str:
return f"I'm {self.name}, {self.age} years old"

@classmethod
def from_birth_year(cls, name: str, birth_year: int) -> "Person":
"""类方法:可作为替代构造函数"""
return cls(name, 2026 - birth_year)

@staticmethod
def is_adult(age: int) -> bool:
"""静态方法:与类相关但不需要实例/类引用"""
return age >= 18

@property
def info(self) -> str:
"""属性:像访问属性一样调用方法"""
return f"{self.name}({self.age})"

三大特性

封装:通过命名约定控制访问级别

class Account:
def __init__(self, balance: float):
self._balance = balance # 约定私有(单下划线),外部可访问但不建议
self.__secret = "hidden" # 名称改写(双下划线)→ _Account__secret

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

继承:子类继承父类的属性和方法

class Animal:
def speak(self) -> str:
raise NotImplementedError

class Dog(Animal):
def speak(self) -> str:
return "汪汪"

class Cat(Animal):
def speak(self) -> str:
return "喵喵"

多态:不同类型对象对同一方法有不同实现

def animal_sound(animal: Animal) -> str:
return animal.speak() # 运行时根据实际类型调用

animal_sound(Dog()) # "汪汪"
animal_sound(Cat()) # "喵喵"

MRO(方法解析顺序)

Python 使用 C3 线性化算法解决多继承的方法查找顺序:

class A:
def method(self): return "A"

class B(A):
def method(self): return "B"

class C(A):
def method(self): return "C"

class D(B, C):
pass

print(D.mro())
# [D, B, C, A, object]
# D 先找 B,再找 C,再找 A,最后 object

d = D()
d.method() # "B" — 按 MRO 顺序,B 先于 C

super() 与 MRO

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

class B(A):
def method(self):
print("B")
super().method() # 按 MRO 调用下一个,不一定是父类

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

class D(B, C):
def method(self):
print("D")
super().method()

D().method()
# D → B → C → A (按 MRO 顺序链式调用)

魔术方法(Dunder Methods)

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

# 字符串表示
def __repr__(self) -> str:
"""开发者看到的表示,应能重建对象"""
return f"Vector({self.x}, {self.y})"

def __str__(self) -> str:
"""用户看到的表示"""
return f"({self.x}, {self.y})"

# 算术运算
def __add__(self, other: "Vector") -> "Vector":
return Vector(self.x + other.x, self.y + other.y)

# 比较
def __eq__(self, other: object) -> bool:
if not isinstance(other, Vector):
return NotImplemented
return self.x == other.x and self.y == other.y

def __hash__(self) -> int:
return hash((self.x, self.y))

# 容器协议
def __len__(self) -> int:
return 2

def __getitem__(self, index: int) -> float:
return (self.x, self.y)[index]

# 布尔值
def __bool__(self) -> bool:
return self.x != 0 or self.y != 0

# 可调用
def __call__(self, scalar: float) -> "Vector":
return Vector(self.x * scalar, self.y * scalar)

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # (4, 6)
print(v1 == v2) # False
print(len(v1)) # 2
print(v1[0]) # 1
print(v1(3)) # (3, 6) — 实例当函数调用

描述符协议

描述符是实现了 __get____set____delete__ 中任意一个方法的对象:

class Validated:
"""数据描述符:验证赋值"""
def __init__(self, min_value: float = 0):
self.min_value = min_value
self.name = ""

def __set_name__(self, owner, name):
self.name = name

def __get__(self, obj, objtype=None):
if obj is None:
return self
return obj.__dict__.get(self.name, 0)

def __set__(self, obj, value):
if value < self.min_value:
raise ValueError(f"{self.name} 不能小于 {self.min_value}")
obj.__dict__[self.name] = value

class Product:
price = Validated(min_value=0) # 描述符实例作为类变量
quantity = Validated(min_value=0)

p = Product()
p.price = 9.99 # ✅
p.quantity = -1 # ❌ ValueError: quantity 不能小于 0
property 就是描述符

@property 实际上是一个数据描述符的语法糖,底层实现了 __get____set____delete__

__slots__

class Point:
__slots__ = ("x", "y") # 限制实例属性,禁止 __dict__

def __init__(self, x: float, y: float):
self.x = x
self.y = y

p = Point(1, 2)
p.z = 3 # ❌ AttributeError

# 优势:节省约 40% 内存,属性访问略快

常见面试问题

Q1: __new____init__ 的区别?

答案

  • __new__创建实例,是类方法,返回新实例对象
  • __init__初始化实例,是实例方法,设置属性
  • 调用顺序:__new____init__
class Singleton:
_instance = None

def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance

def __init__(self, value):
self.value = value

a = Singleton(1)
b = Singleton(2)
print(a is b) # True — 同一实例
print(a.value) # 2 — __init__ 被调用了两次

Q2: 类方法、静态方法和实例方法的区别?

答案

方法类型装饰器第一个参数能否访问实例能否访问类
实例方法self(实例)
类方法@classmethodcls(类)
静态方法@staticmethod

类方法常用作替代构造函数;静态方法是与类相关的工具函数

Q3: __str____repr__ 的区别?

答案

  • __repr__:面向开发者,应该无歧义,理想情况下可以 eval() 还原对象
  • __str__:面向用户,可读性优先
  • print() 优先调用 __str__;交互式解释器和容器内元素使用 __repr__
import datetime
d = datetime.date(2026, 3, 28)
repr(d) # "datetime.date(2026, 3, 28)" — 可以 eval 还原
str(d) # "2026-03-28" — 人类可读

Q4: 什么是抽象基类(ABC)?

答案

from abc import ABC, abstractmethod

class Shape(ABC):
@abstractmethod
def area(self) -> float:
"""子类必须实现"""
pass

class Circle(Shape):
def __init__(self, radius: float):
self.radius = radius

def area(self) -> float:
return 3.14159 * self.radius ** 2

# Shape() # ❌ TypeError: 不能实例化抽象类
c = Circle(5) # ✅

Q5: Python 如何实现私有属性?

答案

Python 没有真正的私有,使用命名约定

前缀效果示例
无前缀公开self.name
_ 单下划线约定私有self._internal
__ 双下划线名称改写self.__secretself._Class__secret

双下划线会被改写为 _类名__属性名,目的是避免子类意外覆盖,而非安全机制。

相关链接