跳到主要内容

Python 面向对象编程

面向对象编程(OOP)是一种程序设计思想。本章将介绍类和对象的核心概念。

类和对象基础​

什么是类?​

类是一种抽象的数据类型,它定义了一类事物的共同属性和行为。

什么是对象?​

对象是类的实例,是具体的存在。

定义类​

class Person:
"""人类"""

# 类属性(所有实例共享)
species = "智人"

# 初始化方法
def __init__(self, name, age):
# 实例属性
self.name = name
self.age = age

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

# 私有方法(以双下划线开头)
def __private_method(self):
print("这是私有方法")

# 魔术方法
def __str__(self):
return f"Person: {self.name}, {self.age}"

创建对象​

person = Person("张三", 20)
print(person.name) # 张三
print(person.age) # 20
person.say_hello() # 你好,我叫张三,今年20岁
print(person) # Person: 张三, 20

self 参数​

self 指向当前对象本身,类似于其他语言中的 this。

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

def bark(self):
# self 指向调用此方法的对象
print(f"{self.name} 叫了一声")

初始化方法 init​

__init__ 在创建对象时自动调用,用于初始化对象的属性。

class Car:
def __init__(self, brand, color):
self.brand = brand
self.color = color
self.speed = 0

def drive(self):
print(f"{self.color}的{self.brand}汽车正在行驶")

car = Car("比亚迪", "红色")
car.drive() # 红色的比亚迪汽车正在行驶

属性​

实例属性​

每个对象独有的属性:

class Person:
def __init__(self, name):
self.name = name # 实例属性

person1 = Person("张三")
person2 = Person("李四")
print(person1.name) # 张三
print(person2.name) # 李四

类属性​

所有对象共享的属性:

class Person:
species = "智人" # 类属性

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

print(Person.species) # 智人
person = Person("张三")
print(person.species) # 智人(通过实例访问类属性)

属性访问优先级​

实例属性 > 类属性

class Person:
name = "人类" # 类属性

def __init__(self, name):
self.name = name # 实例属性

person = Person("张三")
print(person.name) # 张三(优先访问实例属性)
print(Person.name) # 人类(访问类属性)

使用 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.14 * self._radius ** 2

circle = Circle(5)
print(circle.radius) # 5
print(circle.area) # 78.5
circle.radius = 10
print(circle.area) # 314.0

方法类型​

实例方法​

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

def say_hello(self): # 实例方法
print(f"你好,我叫{self.name}")

类方法​

使用 @classmethod 装饰器,第一个参数是类本身:

class Person:
count = 0 # 类属性

def __init__(self, name):
self.name = name
Person.count += 1

@classmethod
def get_count(cls):
return cls.count

print(Person.get_count()) # 0
person = Person("张三")
print(Person.get_count()) # 1

静态方法​

使用 @staticmethod 装饰器,不需要 self 或 cls 参数:

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

result = Math.add(3, 5) # 8
print(Math.add(1, 2)) # 3

访问权限​

公有属性和方法​

class Person:
def __init__(self, name):
self.name = name # 公有属性

def public_method(self): # 公有方法
print("公有方法")

私有属性和方法​

使用双下划线 __ 前缀:

class Person:
def __init__(self, name):
self.__name = name # 私有属性

def __private_method(self): # 私有方法
print("私有方法")

def public_method(self):
# 可以在类内部访问私有成员
print(f"姓名:{self.__name}")
self.__private_method()

person = Person("张三")
person.public_method() # 姓名:张三 私有方法
# person.__name # 错误:无法直接访问
# person.__private_method() # 错误:无法直接访问

访问私有属性(不推荐)​

# 使用 name mangling
person._Person__name # 张三
person._Person__private_method() # 私有方法

保护属性​

使用单下划线 _ 前缀,表示protected(受保护):

class Person:
def __init__(self, name):
self._name = name # 保护属性

person = Person("张三")
print(person._name) # 可以访问,但不推荐

继承​

基本语法​

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

def eat(self):
print(f"{self.name}在吃饭")

def sleep(self):
print(f"{self.name}在睡觉")

# 子类
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调用父类的初始化方法
self.breed = breed # 子类新增属性

def bark(self): # 子类新增方法
print(f"{self.name}叫了一声")

def eat(self): # 重写父类方法
print(f"{self.name}正在吃狗粮")

dog = Dog("旺财", "金毛")
dog.eat() # 旺财正在吃狗粮(重写的方法)
dog.sleep() # 旺财在睡觉(继承的方法)
dog.bark() # 旺财叫了一声(子类新增的方法)

多继承​

class Flyable:
def fly(self):
print("可以飞")

class Swimmable:
def swim(self):
print("可以游泳")

class Duck(Animal, Flyable, Swimmable):
pass

duck = Duck("鸭子")
duck.eat()
duck.fly()
duck.swim()

方法解析顺序(MRO)​

class A:
pass

class B(A):
pass

class C(A):
pass

class D(B, C):
pass

print(D.__mro__) # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

多态​

同一接口,不同实现:

class Animal:
def speak(self):
pass

class Dog(Animal):
def speak(self):
print("汪汪")

class Cat(Animal):
def speak(self):
print("喵喵")

class Cow(Animal):
def speak(self):
print("哞哞")

# 多态调用
animals = [Dog(), Cat(), Cow()]
for animal in animals:
animal.speak()

# 输出:
# 汪汪
# 喵喵
# 哞哞

特殊方法(魔术方法)​

方法描述示例
__init__初始化方法对象创建时调用
__str__字符串表示print(obj) 时调用
__repr__调试字符串repr(obj) 时调用
__len__长度len(obj) 时调用
__add__加法obj1 + obj2 时调用
__eq__相等obj1 == obj2 时调用
__lt__小于obj1 < obj2 时调用

示例​

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

def __str__(self):
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):
return 2

def __getitem__(self, index):
if index == 0:
return self.x
elif index == 1:
return self.y
else:
raise IndexError("索引超出范围")

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1) # Vector(1, 2)
print(v1 + v2) # Vector(4, 6)
print(v1 == v2) # False
print(len(v1)) # 2
print(v1[0]) # 1

抽象类和接口​

使用 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)

rect = Rectangle(5, 3)
print(rect.area()) # 15
print(rect.perimeter()) # 16

# 抽象类不能直接实例化
# shape = Shape() # 错误

数据类(dataclass)​

Python 3.7 引入了 dataclasses 模块,可以自动生成 __init__、__repr__、__eq__ 等特殊方法,大大简化了数据类的定义。

基本用法​

from dataclasses import dataclass

@dataclass
class Person:
name: str
age: int
city: str = "未知"

# 自动生成了 __init__ 方法
person = Person("张三", 25)
print(person) # Person(name='张三', age=25, city='未知')

# 自动生成了 __eq__ 方法
person2 = Person("张三", 25)
print(person == person2) # True

对比传统写法,可以看到 dataclass 的简洁性:

# 传统写法:需要手动编写多个方法
class PersonTraditional:
def __init__(self, name: str, age: int, city: str = "未知"):
self.name = name
self.age = age
self.city = city

def __repr__(self):
return f"PersonTraditional(name={self.name!r}, age={self.age!r}, city={self.city!r})"

def __eq__(self, other):
if not isinstance(other, PersonTraditional):
return False
return (self.name, self.age, self.city) == (other.name, other.age, other.city)

field 函数详解​

使用 field() 函数可以对字段进行更精细的控制:

from dataclasses import dataclass, field

@dataclass
class Student:
name: str
age: int
# 默认值
grade: str = "大一"
# 使用 default_factory 创建可变默认值
scores: list = field(default_factory=list)
# 排除某些字段
_internal_id: int = field(default=0, repr=False)
# 不参与初始化
full_name: str = field(init=False)
# 不参与比较
email: str = field(compare=False, default="")

def __post_init__(self):
# 初始化后处理
self.full_name = f"学生:{self.name}"

student = Student("张三", 20)
print(student) # Student(name='张三', age=20, grade='大一', scores=[], full_name='学生:张三', email='')

为什么需要 default_factory?

Python 中可变对象作为默认值会导致所有实例共享同一个对象:

# 错误示例
class Wrong:
items = [] # 所有实例共享同一个列表

w1, w2 = Wrong(), Wrong()
w1.items.append(1)
print(w2.items) # [1] - 被污染了!

# 正确示例:使用 default_factory
@dataclass
class Correct:
items: list = field(default_factory=list)

c1, c2 = Correct(), Correct()
c1.items.append(1)
print(c2.items) # [] - 独立的列表

dataclass 参数​

@dataclass(
init=True, # 生成 __init__ 方法
repr=True, # 生成 __repr__ 方法
eq=True, # 生成 __eq__ 方法
order=False, # 生成 __lt__、__le__、__gt__、__ge__ 方法
frozen=False, # 创建不可变实例
slots=False, # 使用 __slots__ 优化内存
)
class Config:
host: str
port: int = 8080

# frozen=True 创建不可变实例
@dataclass(frozen=True)
class Point:
x: float
y: float

p = Point(1.0, 2.0)
# p.x = 3.0 # FrozenInstanceError: cannot assign to field 'x'

# order=True 支持排序
@dataclass(order=True)
class Version:
major: int
minor: int

v1 = Version(1, 0)
v2 = Version(2, 0)
print(v1 < v2) # True

继承​

dataclass 支持继承,子类会继承父类的所有字段:

@dataclass
class Animal:
name: str
age: int

@dataclass
class Dog(Animal):
breed: str

dog = Dog("旺财", 3, "金毛")
print(dog) # Dog(name='旺财', age=3, breed='金毛')

实用函数​

from dataclasses import dataclass, asdict, astuple, fields, replace

@dataclass
class Person:
name: str
age: int

person = Person("张三", 25)

# 转换为字典
print(asdict(person)) # {'name': '张三', 'age': 25}

# 转换为元组
print(astuple(person)) # ('张三', 25)

# 获取字段信息
for f in fields(person):
print(f.name, f.type) # name <class 'str'> age <class 'int'>

# 创建修改后的副本
new_person = replace(person, age=26)
print(new_person) # Person(name='张三', age=26)

描述符(Descriptor)​

描述符是 Python 中实现属性访问控制的底层机制。理解描述符有助于深入理解 property、classmethod、staticmethod 等的实现原理。

什么是描述符?​

描述符是一个实现了 __get__、__set__ 或 __delete__ 方法的类。当描述符作为另一个类的类属性时,对这些属性的访问会被描述符拦截。

class Descriptor:
"""简单的描述符示例"""

def __get__(self, obj, owner):
print(f"获取值,obj={obj}, owner={owner}")
return obj._value if obj else None

def __set__(self, obj, value):
print(f"设置值:{value}")
obj._value = value

def __delete__(self, obj):
print("删除值")
del obj._value

class MyClass:
attr = Descriptor() # 描述符作为类属性

obj = MyClass()
obj.attr = 10 # 调用 __set__
print(obj.attr) # 调用 __get__
del obj.attr # 调用 __delete__

数据描述符与非数据描述符​

根据实现的方法不同,描述符分为两类:

数据描述符:同时实现 __get__ 和 __set__,优先级最高。

非数据描述符:只实现 __get__,优先级低于实例属性。

# 数据描述符
class DataDescriptor:
def __get__(self, obj, owner):
return "数据描述符的值"

def __set__(self, obj, value):
print(f"数据描述符设置值:{value}")

# 非数据描述符
class NonDataDescriptor:
def __get__(self, obj, owner):
return "非数据描述符的值"

class Example:
data_desc = DataDescriptor()
non_data_desc = NonDataDescriptor()

e = Example()

# 数据描述符优先级高于实例属性
e.__dict__['data_desc'] = "实例属性"
print(e.data_desc) # 数据描述符的值(数据描述符优先)

# 非数据描述符优先级低于实例属性
e.__dict__['non_data_desc'] = "实例属性"
print(e.non_data_desc) # 实例属性(实例属性优先)

实际应用:类型检查​

class Typed:
"""类型检查描述符"""

def __init__(self, name, expected_type):
self.name = name
self.expected_type = expected_type

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

def __set__(self, obj, value):
if not isinstance(value, self.expected_type):
raise TypeError(f"{self.name} 应该是 {self.expected_type} 类型")
obj.__dict__[self.name] = value

class Person:
name = Typed("name", str)
age = Typed("age", int)

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

person = Person("张三", 25)
# person.age = "二十五" # TypeError: age 应该是 <class 'int'> 类型

实际应用:延迟计算属性​

class LazyProperty:
"""延迟计算属性描述符"""

def __init__(self, func):
self.func = func
self.attr_name = func.__name__

def __get__(self, obj, owner):
if obj is None:
return self
# 计算并缓存结果
value = self.func(obj)
obj.__dict__[self.attr_name] = value
return value

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

@LazyProperty
def area(self):
print("计算面积...")
return 3.14159 * self.radius ** 2

circle = Circle(5)
print(circle.area) # 计算面积... 78.53975
print(circle.area) # 78.53975(第二次直接从缓存获取)

property 的实现原理​

property 本质上是一个描述符:

class MyProperty:
"""模拟 property 的实现"""

def __init__(self, fget=None, fset=None, fdel=None):
self.fget = fget
self.fset = fset
self.fdel = fdel

def __get__(self, obj, owner):
if obj is None:
return self
return self.fget(obj)

def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("属性只读")
self.fset(obj, value)

def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("属性不可删除")
self.fdel(obj)

def setter(self, fset):
self.fset = fset
return self

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

@MyProperty
def radius(self):
return self._radius

@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("半径不能为负")
self._radius = value

c = Circle(5)
print(c.radius) # 5
c.radius = 10
print(c.radius) # 10

slots 优化​

使用 __slots__ 可以限制类的属性,并减少内存占用。

基本用法​

class Point:
__slots__ = ['x', 'y'] # 只允许 x 和 y 属性

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

p = Point(1, 2)
print(p.x, p.y) # 1 2

# p.z = 3 # AttributeError: 'Point' has no attribute 'z'

内存优化效果​

import sys

# 普通类
class NormalPoint:
def __init__(self, x, y):
self.x = x
self.y = y

# 使用 __slots__ 的类
class SlotPoint:
__slots__ = ['x', 'y']

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

n = NormalPoint(1, 2)
s = SlotPoint(1, 2)

print(f"普通类实例大小: {sys.getsizeof(n)} 字节")
print(f"slots类实例大小: {sys.getsizeof(s)} 字节")

# 创建大量实例时的内存差异
normal_points = [NormalPoint(i, i) for i in range(100000)]
slot_points = [SlotPoint(i, i) for i in range(100000)]

slots 的限制​

class Example:
__slots__ = ['x', 'y']

# 无法使用 __dict__
# print(__dict__) # 不存在

e = Example()
e.x = 1
# e.z = 3 # AttributeError

# 无法动态添加属性
# e.new_attr = 10 # AttributeError

继承时的注意事项​

class Base:
__slots__ = ['x']

class Derived(Base):
__slots__ = ['y'] # 子类也需要定义 __slots__

d = Derived()
d.x = 1
d.y = 2

# 如果子类不定义 __slots__,会有 __dict__
class DerivedNoSlots(Base):
pass

d2 = DerivedNoSlots()
d2.x = 1
d2.z = 3 # 可以,因为有 __dict__

属性访问控制​

getattr 和 getattribute​

这两个方法用于控制属性访问,但触发时机不同:

  • __getattribute__:访问任何属性时都会触发
  • __getattr__:只有属性不存在时才触发
class Example:
def __init__(self):
self.exists = "存在的属性"

def __getattribute__(self, name):
print(f"__getattribute__ 被调用:{name}")
return super().__getattribute__(name)

def __getattr__(self, name):
print(f"__getattr__ 被调用:{name}")
return f"属性 {name} 不存在"

e = Example()
print(e.exists) # 先调用 __getattribute__,属性存在,返回值
print(e.missing) # 先调用 __getattribute__,再调用 __getattr__

动态属性代理​

class Proxy:
"""代理模式:将属性访问转发给目标对象"""

def __init__(self, target):
self._target = target

def __getattr__(self, name):
# 当代理对象没有该属性时,转发给目标对象
return getattr(self._target, name)

def __setattr__(self, name, value):
if name == '_target':
super().__setattr__(name, value)
else:
setattr(self._target, name, value)

class Target:
def __init__(self):
self.value = 100

target = Target()
proxy = Proxy(target)

print(proxy.value) # 100(从 target 获取)
proxy.value = 200
print(target.value) # 200(修改了 target)

setattr 和 delattr​

class Protected:
def __init__(self):
self._data = {}

def __setattr__(self, name, value):
if name.startswith('_'):
super().__setattr__(name, value)
else:
self._data[name] = value
print(f"设置属性 {name} = {value}")

def __getattr__(self, name):
if name in self._data:
return self._data[name]
raise AttributeError(f"没有属性 {name}")

def __delattr__(self, name):
if name in self._data:
del self._data[name]
print(f"删除属性 {name}")
else:
super().__delattr__(name)

p = Protected()
p.x = 10 # 设置属性 x = 10
print(p.x) # 10
del p.x # 删除属性 x

元类(Metaclass)​

元类是创建类的"类"。理解元类需要明白:在 Python 中,类也是对象,而元类就是创建这些类对象的类。

类的本质​

class MyClass:
pass

obj = MyClass()

# 类是 type 的实例
print(type(MyClass)) # <class 'type'>
print(type(obj)) # <class '__main__.MyClass'>

# 类可以动态创建
DynamicClass = type('DynamicClass', (), {'x': 10})
print(DynamicClass.x) # 10

使用元类​

class Meta(type):
"""自定义元类"""

def __new__(mcs, name, bases, namespace):
print(f"创建类: {name}")
print(f"基类: {bases}")
print(f"命名空间: {namespace}")

# 可以修改类的创建过程
namespace['added_by_meta'] = "元类添加的属性"

return super().__new__(mcs, name, bases, namespace)

def __init__(cls, name, bases, namespace):
print(f"初始化类: {name}")
super().__init__(name, bases, namespace)

class MyClass(metaclass=Meta):
x = 10

def method(self):
pass

# 输出:
# 创建类: MyClass
# 基类: ()
# 命名空间: {'x': 10, 'method': <function...>}
# 初始化类: MyClass

print(MyClass.added_by_meta) # 元类添加的属性

元类应用:单例模式​

class SingletonMeta(type):
"""单例元类"""

_instances = {}

def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]

class Database(metaclass=SingletonMeta):
def __init__(self):
print("初始化数据库连接")

db1 = Database() # 初始化数据库连接
db2 = Database() # 不会再次初始化
print(db1 is db2) # True

元类应用:自动注册​

class RegistryMeta(type):
"""自动注册元类"""

registry = {}

def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)
if name != 'BasePlugin': # 不注册基类
mcs.registry[name] = cls
return cls

class BasePlugin(metaclass=RegistryMeta):
"""插件基类"""

def run(self):
raise NotImplementedError

class PluginA(BasePlugin):
def run(self):
return "插件 A 运行"

class PluginB(BasePlugin):
def run(self):
return "插件 B 运行"

# 自动注册
print(RegistryMeta.registry)
# {'PluginA': <class 'PluginA'>, 'PluginB': <class 'PluginB'>}

# 通过名称获取插件
plugin = RegistryMeta.registry['PluginA']()
print(plugin.run()) # 插件 A 运行

元类应用:接口检查​

class InterfaceMeta(type):
"""接口检查元类"""

def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)

if bases: # 不是接口本身
# 检查是否实现了所有抽象方法
interface = bases[0]
if hasattr(interface, '_abstract_methods'):
for method in interface._abstract_methods:
if method not in namespace:
raise TypeError(
f"{name} 必须实现抽象方法 {method}"
)
return cls

class Animal(metaclass=InterfaceMeta):
_abstract_methods = ['speak', 'move']

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

def move(self):
return "跑"

# class Cat(Animal): # TypeError: Cat 必须实现抽象方法 speak
# def move(self):
# return "走"

dog = Dog()
print(dog.speak()) # 汪汪

上下文管理器​

通过实现 __enter__ 和 __exit__ 方法,可以让对象支持 with 语句。

基本实现​

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

def __enter__(self):
print("打开文件")
self.file = open(self.filename, self.mode)
return self.file

def __exit__(self, exc_type, exc_val, exc_tb):
print("关闭文件")
if self.file:
self.file.close()
# 返回 False 会重新抛出异常
# 返回 True 会抑制异常
return False

with FileManager("test.txt", "w") as f:
f.write("Hello")
# 输出:
# 打开文件
# 关闭文件

异常处理​

class SuppressError:
def __init__(self, error_type):
self.error_type = error_type

def __enter__(self):
return self

def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is self.error_type:
print(f"抑制了异常: {exc_val}")
return True # 抑制异常
return False # 其他异常正常抛出

with SuppressError(ValueError):
raise ValueError("测试异常")
print("继续执行") # 会执行,异常被抑制

contextlib 模块​

使用 contextlib 可以更简洁地创建上下文管理器:

from contextlib import contextmanager

@contextmanager
def timer():
import time
start = time.time()
yield # 在这里执行 with 块中的代码
end = time.time()
print(f"耗时: {end - start:.2f} 秒")

with timer():
sum(range(10000000))
# 耗时: 0.15 秒

小结​

本章我们学习了:

  1. 类和对象的概念
  2. 类的定义和对象的创建
  3. 初始化方法 __init__
  4. 属性(实例属性、类属性、property)
  5. 方法类型(实例方法、类方法、静态方法)
  6. 访问权限(公有、私有、保护)
  7. 继承和多继承
  8. 多态
  9. 特殊方法(魔术方法)
  10. 抽象类
  11. 数据类(dataclass)
  12. 描述符(Descriptor)
  13. slots 优化
  14. 属性访问控制(getattr、getattribute)
  15. 元类(Metaclass)
  16. 上下文管理器

练习​

  1. 创建一个学生类,包含姓名、学号、成绩属性
  2. 创建一个银行账户类,支持存款、取款、查询余额
  3. 实现一个图形类层次结构(圆形、矩形、三角形)
  4. 实现一个栈数据结构(压入、弹出、查看栈顶)
  5. 使用 dataclass 重写学生类
  6. 实现一个类型检查描述符
  7. 使用元类实现一个简单的 ORM 框架