Python 从入门到精通
第 13 / 36 篇
从环境安装和基础语法出发,逐步学习工程实践、自动化、数据处理与 Web 开发。
-
01
Python 怎么装、怎么用 -
02
变量、数字和字符串 -
03
列表:把一组数据放在一起 -
04
元组、集合和字典 -
05
条件判断:让程序做选择 -
06
循环:重复的事交给程序 -
07
函数:把代码整理成可复用的块 -
08
模块与包:拆分你的程序 -
09
输入、输出与字符串格式化 -
10
文件读写:保存程序的数据 -
11
异常处理:程序出错时怎么办 -
12
基础阶段练习:命令行记账本 -
13
类和对象:面向对象入门正在阅读 -
14
继承、组合与特殊方法 -
15
迭代器与生成器 -
16
列表推导式与生成器表达式 -
17
装饰器:给函数增加能力 -
18
上下文管理器与 with -
19
类型标注与 dataclass -
20
正则表达式:从文本中找规律 -
21
日期、时间与时区 -
22
日志与调试 -
23
虚拟环境与依赖管理 -
24
测试:让修改不再提心吊胆 -
25
网络请求:用 Python 调用 API -
26
网页解析与合规采集 -
27
操作 Excel、CSV 与批量文件 -
28
SQLite:给程序加一个数据库 -
29
数据分析入门:NumPy 与 Pandas -
30
画图:把数据变得直观 -
31
Flask 入门:做一个小网站 -
32
异步编程:同时处理多项任务 -
33
线程、进程与并发选择 -
34
性能分析与优化 -
35
项目结构、配置与发布 -
36
综合项目:从需求到上线
前面写的都是”函数 + 数据”:数据是数据,函数是函数,各管各的。但现实世界的东西是”数据 + 行为”一体的——一只狗有名字、年龄(数据),还会叫、会跑(行为)。面向对象编程(OOP)就是让代码贴近这种思维方式:把数据和操作它的函数绑在一起,组成”对象”。
类和对象的关系
先分清两个词:
- 类(class):模板、图纸。定义了”这种东西”长什么样、会干什么
- 对象(object / 实例):按图纸造出来的具体东西
打个比方:Dog 是类(图纸),楼下那只叫旺财的狗是对象(实例)。
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
print(f"{self.name}:汪汪!")
# 创建对象(实例化)
wangcai = Dog("旺财", 3)
wangcai.bark() # 旺财:汪汪!
print(wangcai.name) # 旺财
__init__ 和 self
两个必须理解的机制:
__init__:构造方法,创建对象时自动调用,用来初始化数据。名字前后各两个下划线,这种叫”魔术方法”self:代表当前这个对象自己。类里的方法第一个参数永远是 self,调用时不用传,Python 自动传入
class Student:
def __init__(self, name, score):
self.name = name # 存到对象上
self.score = score
def intro(self):
print(f"我是{self.name},考了{self.score}分")
s1 = Student("小明", 92)
s2 = Student("小红", 88)
s1.intro() # 我是小明,考了92分(self 自动是 s1)
s2.intro() # 我是小红,考了88分(self 自动是 s2)
注意:__init__ 里 self.name = name 是把参数存到对象上,两个 name 不是一回事(左边是对象的属性,右边是参数)。
属性:对象的”数据”
对象上的数据叫属性(attribute),随时可以访问和修改:
class BankAccount:
def __init__(self, owner):
self.owner = owner
self.balance = 0
def deposit(self, amount):
self.balance += amount
print(f"{self.owner} 存入 {amount},余额 {self.balance}")
def withdraw(self, amount):
if amount > self.balance:
print("余额不足")
return
self.balance -= amount
print(f"取出 {amount},余额 {self.balance}")
acc = BankAccount("南山")
acc.deposit(500) # 南山 存入 500,余额 500
acc.withdraw(200) # 取出 200,余额 300
acc.withdraw(999) # 余额不足
余额是对象自己的状态,方法操作自己的状态——数据和行为绑在一起了。这就是封装:外部只调用方法,不用管内部怎么算。
类属性 vs 实例属性
属性分两种:
class Student:
school = "南山中学" # 类属性:所有实例共享
def __init__(self, name):
self.name = name # 实例属性:每个实例自己的
s1 = Student("小明")
s2 = Student("小红")
print(s1.school, s2.school) # 南山中学 南山中学(共享)
s1.school = "北山中学" # 给 s1 单独设了一个,不影响类
print(s1.school, s2.school) # 北山中学 南山中学
print(Student.school) # 南山中学(类的还是没变)
类属性适合放”所有实例都一样”的常量(校名、配置);实例属性放各自不同的数据。
__str__:让打印好看
直接打印对象会输出一串看不懂的地址:
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __str__(self):
return f"学生({self.name}, {self.score}分)"
s = Student("小明", 92)
print(s) # 学生(小明, 92分) —— 有了 __str__ 就好看了
print(str(s)) # 一样
__str__ 定义”这个对象转成字符串长什么样”,调试打印时非常有用。下一篇还会讲更多魔术方法。
什么时候该用类
不是所有代码都要写成类。经验法则:
- 有一组相关的数据 + 对这些数据的操作 → 用类(如银行账户、学生、订单)
- 只是一些独立函数 → 不用类,普通函数就够(如字符串处理工具)
- 多个地方要维护同一份状态 → 用类很合适
记账本那个例子,如果升级成”多个账户、每个账户有余额”,用类就比散落的函数清晰得多。
新手坑
坑 1:方法忘了写 self。定义时第一个参数必须是 self(名字可以改但别改),调用时不传。
坑 2:忘写 __init__ 或拼错。写成 __init_ 或 __int__,Python 不会报错,只是不初始化,属性访问时才报 AttributeError,很难查。
坑 3:类里直接放可变默认值。和函数默认参数一样的问题:
class Bad:
items = [] # 所有实例共享同一个列表!
a = Bad(); b = Bad()
a.items.append(1)
print(b.items) # [1]!b 也被影响了
要在 __init__ 里创建:self.items = []。
小结
- 类是模板,对象是实例;
类名()创建对象 __init__初始化,self指当前对象,方法第一个参数永远是 self- 属性 = 对象的数据,方法 = 对象的行为,封装在一起
- 类属性共享、实例属性独立;
__str__让打印好看 - 有”数据 + 行为”的捆绑需求才用类,别为了用而用
练习
- 定义一个
Book类:书名、作者、价格,一个discount(rate)方法按折扣改价 - 定义一个
Circle类:半径属性,area()和perimeter()方法 - 思考:
s1 = Student("小明")时,Python 内部到底发生了什么?__init__是什么时候被调用的,self 是谁传进去的?