Day 08:面向对象入门
🎯 学习目标:理解面向对象编程思想,掌握类与对象的定义,学会三种方法
1. 图片拷贝案例(带优化)
以文件拷贝的场景引入 OOP 的优势:
# 面向过程的写法
def copy_image(source_path, dest_path):
"""拷贝图片:面向过程"""
with open(source_path, "rb") as src:
data = src.read()
with open(dest_path, "wb") as dst:
dst.write(data)
# 面向对象的写法
class ImageCopier:
"""图片拷贝器"""
def __init__(self, buffer_size=8192):
self.buffer_size = buffer_size
def copy(self, source, dest):
"""拷贝单个文件(带进度显示)"""
import os
total = os.path.getsize(source)
copied = 0
with open(source, "rb") as src, open(dest, "wb") as dst:
while True:
chunk = src.read(self.buffer_size)
if not chunk:
break
dst.write(chunk)
copied += len(chunk)
progress = copied / total * 100
print(f"\r进度:{progress:.1f}%", end="")
def batch_copy(self, file_pairs):
"""批量拷贝"""
for src, dst in file_pairs:
print(f"\n拷贝:{src} → {dst}")
self.copy(src, dst)
2. 面向过程 vs 面向函数式 vs 面向对象
# 方式一:面向过程(数据与操作分离)
student_scores = {"张三": 85, "李四": 92}
def get_average(scores):
return sum(scores.values()) / len(scores)
avg = get_average(student_scores)
# 方式二:面向函数式(纯函数,无副作用)
from statistics import mean
avg = mean(student_scores.values())
# 方式三:面向对象(数据与操作绑定)
class StudentManager:
def __init__(self):
self.students = {} # 数据封装在对象内部
def add(self, name, score):
self.students[name] = score
def average(self):
return sum(self.students.values()) / len(self.students)
def top_n(self, n):
return sorted(self.students.items(),
key=lambda x: x[1], reverse=True)[:n]
mgr = StudentManager()
mgr.add("张三", 85)
mgr.add("李四", 92)
print(mgr.average()) # 88.5
print(mgr.top_n(1)) # [('李四', 92)]
| 范式 | 核心思想 | 优点 |
|---|---|---|
| 面向过程 | 函数 + 全局数据 | 简单直接,小脚本够用 |
| 面向函数式 | 纯函数、无副作用 | 易于测试、并发安全 |
| 面向对象 | 对象 = 数据 + 方法 | 封装、复用、扩展性强 |
3. 类和对象的概念
graph TB Class[🏗️ 类 Class<br/>模板/蓝图<br/>狗类:有名字、会叫] Class --> |"实例化"| Obj1[🐕 对象 Object1<br/>旺财,哈士奇] Class --> |"实例化"| Obj2[🐕 对象 Object2<br/>大黄,金毛]
# 类 = 模板(抽象概念)
class Dog:
"""狗类:定义狗有什么属性和行为"""
species = "犬科" # 类属性(所有狗共享)
def __init__(self, name, breed):
self.name = name # 实例属性(每只狗不同)
self.breed = breed
def bark(self):
return f"{self.name}:汪汪!"
# 对象 = 根据模板创建的具体实例
dog1 = Dog("旺财", "哈士奇") # 实例化
dog2 = Dog("大黄", "金毛")
print(dog1.name) # "旺财"
print(dog1.bark()) # "旺财:汪汪!"
print(dog1.species) # "犬科"(类属性,所有狗共享)
3.1 内存分析
# 每个对象在内存中有独立的空间
class Person:
def __init__(self, name):
self.name = name
p1 = Person("张三")
p2 = Person("张三")
print(p1 == p2) # False(两个不同的对象,虽然值相同)
print(p1 is p2) # False(不是同一个对象)
# 除非定义了 __eq__ 方法(Day 10 Magic Method 会讲)
4. __init__ 方法与 self
4.1 __init__ 初始化方法
class Student:
def __init__(self, name, age):
"""
初始化方法:创建对象时自动调用
self 指向新创建的对象本身
"""
print(f"创建学生:{name}")
self.name = name # 把参数存到对象的属性
self.age = age
s = Student("张三", 20)
# 3. __init__ 中的 self.name = name 等价于 s.name = name
4.2 self 的理解
class Counter:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
c = Counter()
c.increment() # 等价于 Counter.increment(c)
# self 就是 c,所以 self.count 就是 c.count
self 不是关键字!
self只是一个约定俗成的名字,你可以用别的名字,但绝对不要这样做。
5. 类属性 vs 实例属性
class Student:
school = "尚硅谷" # 类属性:所有实例共享
student_count = 0 # 类属性:计数用
def __init__(self, name):
self.name = name # 实例属性:每个实例独有
Student.student_count += 1 # 修改类属性需用类名
s1 = Student("张三")
s2 = Student("李四")
print(s1.name) # "张三"(实例属性)
print(Student.student_count) # 2(类属性)
print(s1.school) # "尚硅谷"(继承类属性)
print(s2.school) # "尚硅谷"(同一个类属性)
# ⚠️ 陷阱:通过实例修改类属性
s1.school = "北大" # 这不是修改类属性!是给 s1 创建了一个同名实例属性
print(s1.school) # "北大"(s1 的实例属性遮蔽了类属性)
print(s2.school) # "尚硅谷"(s2 没受影响,访问的还是类属性)
print(Student.school) # "尚硅谷"(类属性没变!)
6. 实例方法、类方法、静态方法
class MyClass:
class_var = "类变量"
def __init__(self, name):
self.name = name
# 实例方法:第一个参数是 self,操作实例数据
def instance_method(self):
return f"我是 {self.name}"
# 类方法:第一个参数是 cls,操作类数据
@classmethod
def class_method(cls):
return f"类变量 = {cls.class_var}"
# 静态方法:不需要 self 或 cls,独立工具函数
@staticmethod
def static_method(x, y):
return x + y
# 使用
obj = MyClass("测试")
print(obj.instance_method()) # "我是 测试"
print(MyClass.class_method()) # "类变量 = 类变量"
print(MyClass.static_method(3, 5)) # 8
| 方法类型 | 装饰器 | 第一个参数 | 访问 | 典型用途 |
|---|---|---|---|---|
| 实例方法 | 无 | self | 实例+类 | 操作实例数据(最常用) |
| 类方法 | @classmethod | cls | 类 | 工厂方法、修改类属性 |
| 静态方法 | @staticmethod | 无 | 无 | 与类相关的工具函数 |
7. 动态添加属性和方法
class Person:
def __init__(self, name):
self.name = name
p = Person("张三")
# 动态添加实例属性(只对这个实例有效)
p.age = 20
print(p.age) # 20
# 动态添加方法
def say_hello(self):
return f"你好,我是 {self.name}"
# 方式一:添加到实例(需要特殊处理)
import types
p.say_hello = types.MethodType(say_hello, p)
# 方式二:添加到类(所有实例都能用)
Person.say_hello = say_hello
p2 = Person("李四")
print(p2.say_hello()) # "你好,我是 李四"
动态添加虽然灵活,但会降低代码可读性和 IDE 提示效果,正式项目中尽量在类定义中写清楚所有属性和方法。
速记卡(面试闪卡)
Q1:一句话讲清「Day 08:面向对象入门」到底是什么?
A:面向对象编程(OOP)把数据和操作绑进”对象”,用类做模板、对象做实例,主打封装、复用与扩展。
Q2:1. 图片拷贝案例(带优化) —— 怎么理解?
A:拷贝图片时,面向过程要把路径传来传去、加进度条就越来越乱;面向对象把 buffer_size、进度封装进 ImageCopier 类,copy/batch_copy 成方法。像请了个专职文员(对象,Object),资料和数据都归他管,比你自己满地找文件清爽(封装,Encapsulation)。
Q3:2. 面向过程 vs 面向函数式 vs 面向对象 —— 怎么理解?
A:三范式对比——面向过程:函数+全局数据,小脚本够用;面向函数式:纯函数无副作用,易测试并发安全;面向对象:对象=数据+方法,封装复用扩展强。像一个人干活(过程)、流水线标准件(函数式)、成立部门各管一摊(OOP,Object-Oriented Programming)。
Q4:3. 类和对象的概念 —— 怎么理解?
A:类(Class)是模板/蓝图,比如”狗类”定义有名字、会叫;对象(Object)是按模板实例化的具体狗(旺财、大黄)。类属性 species 所有实例共享,实例属性 name 每只不同。像建房图纸 vs 按图盖出的每栋楼。
Q5:4. __init__ 方法与 self —— 怎么理解?
A:init 创建对象时自动调用,self 指向新对象本身,self.name = name 等价于 s.name = name。self 不是关键字,只是约定俗成的名字。像员工入职登记:init 是入职流程,self 就是”这位新员工”(绑定,Binding)。
Q6:核心速记主线有哪些?
-
类 Class 是模板,对象 Object 是按模板实例化的具体实例
-
三范式:面向过程 / 函数式 / 面向对象,OOP 主打封装复用
-
init 自动初始化,self 指向对象自身(非关键字)
-
类属性共享、实例属性独有;实例/类/静态方法三种
口诀
A:类当图纸对象楼,OOP 封装不用愁;
过程函数函数式,面向对象各管一摊;
init 入职登个记,self 就是这员工;
类属性大家共享,实例属性各不同。
相关链接
-
上一篇:07_函数进阶与文件
-
下一篇:09_面向对象三大特性