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实例+类操作实例数据(最常用)
类方法@classmethodcls工厂方法、修改类属性
静态方法@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 就是这员工;

类属性大家共享,实例属性各不同。

相关链接