查看全课大纲(第 31 / 51 节)
  1. 1.Python 是什么?为什么值得学?
  2. 2.如何为 Python 编程准备一个“全家桶”环境?
  3. 3.Python 有哪几种执行方法?
  4. 4.行与缩进到底有什么用?
  5. 5.注释有什么用,怎么写?
  6. 6.变量:如何给数据起个好名字?
  7. 7.运算符的优先级和常见坑点是什么?
  8. 8.Python 中,数字和布尔值究竟有哪几种?
  9. 9.输入函数 `input()` 和文件读取 `open()` 怎么用?
  10. 10.如何将程序的结果展示给用户或保存下来?
  11. 11.CSV 文件怎么读怎么写才方便?
  12. 12.列表是什么,如何创建和访问它?
  13. 13.列表的修改操作,有哪些方法?
  14. 14.列表切片为什么能取到部分元素?排序和查找又该怎么用?
  15. 15.判断语句:如何让程序根据条件做不同的事?
  16. 16.for 循环如何遍历序列中的元素?
  17. 17.while 循环什么时候才会停?
  18. 18.while 循环的结束条件,你写对了吗?
  19. 19.循环中途想提前结束,怎么办?
  20. 20.`break` 和 `continue` 到底有什么区别?
  21. 21.列表推导式如何让代码更简洁高效?
  22. 22.字符串怎么定义、拼接和操作?
  23. 23.元组:为什么定义单个元素时要加逗号?
  24. 24.集合(set)怎么用?如何快速去重和做集合运算?
  25. 25.字典是什么,为什么比列表更方便?
  26. 26.字典的增删改查与遍历,如何避免 KeyError?
  27. 27.函数里的变量,为什么有的能用有的不能用?
  28. 28.在函数内部修改全局变量,为什么有时不生效?
  29. 29.函数里能修改全局变量吗?返回值怎么用?
  30. 30.匿名函数:如何用 lambda 快速定义简单函数?
  31. 31.面向对象编程是什么,为什么需要它?
  32. 32.类中的方法和属性到底怎么定义和使用?
  33. 33.如何控制对对象内部属性和方法的访问?
  34. 34.异常发生时,程序会怎样继续执行?
  35. 35.如何组织代码:包和模块
  36. 36.`if __name__ == '__main__'` 到底有什么用?
  37. 37.如何用 IDE 的 Debug 功能排查程序错误?
  38. 38.Matplotlib 绘图的第一步该做什么?
  39. 39.Figure 是什么,如何设置图形尺寸?
  40. 40.如何自定义 Matplotlib 的坐标轴?
  41. 41.如何为图表添加图例,让不同曲线一目了然?
  42. 42.饼图怎么画才能直观展示数据占比?
  43. 43.如何用 Matplotlib 绘制散点图?
  44. 44.柱状图怎么画?
  45. 45.如何使用 Matplotlib 绘制等高线图?
  46. 46.如何在 Matplotlib 中绘制子图?
  47. 47.zip 函数如何把多个列表“打包”在一起?
  48. 48.如何给可迭代对象加上索引?
  49. 49.random 模块如何生成随机数?
  50. 50.math 模块怎么用?
  51. 51.Counter 如何快速统计元素出现次数?

面向对象编程是什么,为什么需要它?

约 10 分钟

小象实战讲义 · Python 编程从入门到实践

在之前的课程中,我们主要使用函数来组织代码逻辑。但当程序变得复杂,需要管理多个具有相似属性和行为的“事物”时,函数式编程会显得力不从心。本节将带你认识一种更强大的代码组织方式——面向对象编程(OOP),理解其核心概念“类”与“对象”,并学会如何用它们来更清晰地管理数据和逻辑。这是进入 Python 高级知识领域的重要一步。

💡 核心导读

  • 面向对象编程的动机:通过一个管理多只小狗状态的传统函数式代码示例,揭示其可读性差、易出错、难扩展的问题。
  • 面向对象的核心概念:理解“类”作为事物模板、“对象”作为具体实例的关系。
  • 类的定义与使用:掌握如何定义一个类,以及如何创建和使用对象。
  • 对象的数据与行为:把每只小狗的名字和状态保存为对象自己的数据,把啃骨头、喝水等动作写成对象自己的方法。

从函数式编程的痛点说起

假设我们需要编写一个程序来模拟给小狗发指令:小狗可以“啃骨头”和“喝水”,并且每只小狗有自己的名字和状态(比如是否啃过骨头、是否喝过水)。

一种直观但繁琐的实现方式是使用多个列表和函数:

# 使用列表和函数管理小狗状态
name_list = ['旺财', '来福']
done_list = [0, 0]  # 0:无事可做,1:已啃骨头,2:已喝水

def eat(name):
    print("{}正在啃骨头,休息几秒钟后喝水...".format(name))

def drink(name):
    print("{}小狗正在喝水".format(name))

def action(index):
    if done_list[index] == 0:
        done_list[index] = 1
        return eat(name_list[index])
    elif done_list[index] == 1:
        done_list[index] = 2
        return drink(name_list[index])
    else:
        print("{}准备好了!".format(name_list[index]))

# 调用示例
action(0)  # 对旺财发起指令
action(1)  # 对来福发起指令
action(0)  # 再次对旺财发起指令
action(0)  # 第三次对旺财发起指令
旺财正在啃骨头,休息几秒钟后喝水...
来福正在啃骨头,休息几秒钟后喝水...
旺财小狗正在喝水
旺财准备好了!

这段代码虽然能运行,但存在几个明显问题:

  1. 数据分散:小狗的名字和状态分散在两个独立的列表(name_list, done_list)中,管理困难。
  2. 可读性差:调用 action(0) 时,如果不看代码,很难知道 0 代表哪只小狗。
  3. 扩展性差:如果想增加一只小狗,必须同时修改 name_list 和 done_list,且要确保它们的索引对应。如果状态更复杂,需要管理的列表会更多,极易出错。

面向对象编程正是为了解决这类问题而生的。

面向对象的核心:类与对象

面向对象编程(OOP) 是一种将软件系统中相似的操作逻辑、数据和状态,以“类”的形式组织起来,并通过创建“对象”实例来复用的设计和编程方法。

  • 类(Class):一个事物的抽象模板,定义了一类事物共有的属性(数据)和方法(行为)。例如,“小狗”这个类可以定义属性“名字”和“状态”,以及方法“啃骨头”、“喝水”。
  • 对象(Object):通过类创建出来的具体实例。每个对象都拥有类中定义的属性和方法,但各自的状态(属性值)可以不同。例如,根据“小狗”类可以创建出名叫“旺财”和“来福”的两个具体小狗对象。

关系:类是创建对象的蓝图(模板),对象是根据蓝图制造出的具体产品。

定义类与创建对象

让我们用面向对象的方式重写小狗的例子。

# 定义一个Dog类
class Dog:
    # 构造方法,在创建对象时自动调用,用于初始化属性
    def __init__(self, name):
        self.name = name  # 实例属性:小狗的名字
        self.done = 0     # 实例属性:小狗的状态,0-无事,1-已啃骨,2-已喝水

    # 公开方法:啃骨头
    def eat(self):
        print("{}正在啃骨头,休息几秒钟后喝水...".format(self.name))

    # 公开方法:可以在类外部调用
    def drink(self):
        print("{}小狗正在喝水".format(self.name))

    # 公开方法:对外提供的指令接口
    def action(self):
        if self.done == 0:
            self.done = 1
            return self.eat()  # 内部调用自己的方法
        elif self.done == 1:
            self.done = 2
            return self.drink()
        else:
            print("{}准备好了!".format(self.name))

# 创建对象:根据Dog类模板,实例化具体的小狗
wang_cai = Dog('旺财')  # wang_cai 是一个Dog类的对象
laifu = Dog('来福')     # laifu 是另一个Dog类的对象

# 对对象进行操作
wang_cai.action()
laifu.action()
wang_cai.action()
wang_cai.action()
旺财正在啃骨头,休息几秒钟后喝水...
来福正在啃骨头,休息几秒钟后喝水...
旺财小狗正在喝水
旺财准备好了!

代码解析:

  1. class Dog::定义一个名为 Dog 的类。类名通常采用“大驼峰”命名法(每个单词首字母大写)。
  2. def __init__(self, name)::这是构造方法。当使用 Dog('旺财') 创建对象时,Python 会自动调用它。self 参数代表当前正在创建的对象实例本身,必须是方法的第一个参数。name 是创建对象时传入的参数。
  3. self.name = name:将传入的 name 赋值给当前对象的 name 属性。这样,每个小狗对象就拥有了自己独立的名字。
  4. wang_cai = Dog('旺财'):这行代码做了两件事:a) 在内存中创建一个新的 Dog 对象;b) 调用 __init__ 方法初始化这个对象,并将其赋值给变量 wang_cai。
  5. wang_cai.action():通过对象调用其方法。此时,方法内的 self 会自动指向 wang_cai 这个对象,因此 self.name 就是“旺财”,self.done 也是旺财自己的状态。

现在,每只小狗的数据(名字、状态)和行为(action 逻辑)都被封装在各自的对象里。添加新小狗只需 Dog('新名字'),代码清晰且不易出错。

📝 动手练一练

👉 点击查看参考答案

题目:请定义一个 Student 类。

  • 属性包括:name(姓名)、score(分数)。
  • 方法包括:
    • __init__(self, name, score):初始化姓名和分数。
    • get_grade(self):根据分数返回等级(≥90为‘A’,≥80为‘B’,≥70为‘C’,<70为‘D’)。
    • show_info(self):打印学生的姓名和等级。
  • 创建两个学生对象(如“张三”, 85 和 “李四”, 92),并调用 show_info 方法。

思路:仿照 Dog 类的结构,将学生相关的数据和逻辑封装在一起。

参考答案:

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def get_grade(self):
        if self.score >= 90:
            return 'A'
        elif self.score >= 80:
            return 'B'
        elif self.score >= 70:
            return 'C'
        else:
            return 'D'

    def show_info(self):
        grade = self.get_grade()
        print("学生{}的成绩等级是:{}".format(self.name, grade))

# 创建对象并测试
stu1 = Student("张三", 85)
stu2 = Student("李四", 92)
stu1.show_info()
stu2.show_info()
学生张三的成绩等级是:B
学生李四的成绩等级是:A

常见错误

错误写法现象或报错原因正确写法
def my_method(): (在类中定义方法时漏写self)TypeError: MyClass.my_method() takes 0 positional arguments but 1 was given(MyClass 为示例类名)类的方法在调用时,Python会自动传入调用该方法的对象本身作为第一个参数。如果定义时没有预留self参数,就会导致参数数量不匹配。def my_method(self):
class dog: (类名用小写)无语法错误,但不符合约定Python 社区约定类名使用“大驼峰命名法”(CamelCase),以提高代码可读性,一眼就能区分类和普通变量/函数。class Dog:
在类的方法内部访问属性时忘记加self.NameError: name 'xxx' is not defined 或 逻辑错误(使用了局部变量或全局变量)在类的方法中,要访问属于当前对象的属性或调用其他方法,必须通过self.来引用。否则Python会在局部或全局作用域中查找,通常找不到。确保访问属性时使用self.属性名,调用方法时使用self.方法名()。

小结

  1. 面向对象编程(OOP) 通过“类”和“对象”来组织代码,将数据(属性)和操作数据的方法封装在一起,提高了代码的复用性、可读性和可维护性。
  2. 类是创建对象的蓝图或模板,定义了同类对象共有的属性和方法。
  3. 对象是类的具体实例,拥有类中定义的属性和方法,并且每个对象的状态独立。
  4. 使用 class ClassName: 来定义一个类。类中的方法第一个参数通常是 self,代表对象实例本身。
  5. __init__(self, ...) 是构造方法,在创建对象时自动调用,用于初始化对象的属性。
  6. 创建对象的语法是 对象名 = 类名(参数);同一个类可以创建多个互相独立的对象。

📋 行动清单

  • 将本节“动手练一练”的 Student 类代码完整敲一遍并运行,确保理解每个部分的作用。
  • 为 Student 类增加一个 update_score(self, new_score) 方法,用来修改分数,修改后重新调用 show_info 查看等级变化。
  • 回顾开头的函数式小狗管理代码,思考如果增加“小狗品种”和“是否打过疫苗”两个状态,用面向对象的方式改写会比用函数式简单多少。

—— 小象教研组

配套资料 · 直接下载

课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。

  • 第4章代码包:面向对象基础(含1 个 Notebook)
    ZIP · 2KB
    下载