4. Python基础速通(下):函数定义、文件读写、异常处理、面向对象编程入门
好,咱们接着聊。上一章我们把Python的“语法骨架”搭好了,变量、循环、条件判断这些基本功都过了一遍。这一章,咱们要开始给这个骨架“装器官”了——让代码真正具备组织能力、容错能力和复用能力。
说白了,写代码就像盖房子。你光有砖头(变量)和图纸(流程控制)还不够,你得有“预制板”(函数),得有“水电管线”(文件读写),还得有“消防系统”(异常处理)。最后,面向对象编程(OOP)就像是给房子装上“智能家居系统”——让数据和操作它的方法住在一起。
我个人习惯是,学Python一定要先啃下这四块硬骨头。啃完之后,你写出来的代码就不再是“脚本”,而是“程序”了。
4.1 函数定义:把代码“打包”成工具
先问个问题:你写了一段代码,能计算两个数的平均值。过了一会,你又需要算另一组数的平均值。怎么办?复制粘贴?
嗯,这确实是最原始的办法。但相信我,我在项目中见过有人复制粘贴了200行一模一样的代码,只因为改了三个变量名。后来需求一变,他改了一个地方,忘了改另外两个……那场面,简直是灾难现场。
函数就是用来解决这个问题的。它把一段逻辑“打包”成一个工具,随叫随到。
4.1.1 定义和调用
Python里定义函数用 def 关键字,语法很简单:
def 函数名(参数列表):
"""文档字符串(可选,但强烈建议写)"""
函数体
return 返回值
举个例子,写一个计算平均值的函数:
def average(a, b):
"""计算两个数的平均值"""
result = (a + b) / 2
return result
# 调用
avg = average(10, 20)
print(avg) # 输出 15.0
你看,调用的时候只需要传两个数进去,它就把结果吐出来了。干净利落。
4.1.2 参数的那些“坑”
Python的参数机制很灵活,但也容易踩坑。我重点说两个最常见的:
默认参数: 给参数一个默认值,调用时可以不传。
def greet(name, greeting="你好"):
print(f"{greeting},{name}!")
greet("张三") # 输出:你好,张三!
greet("李四", "Hello") # 输出:Hello,李四!
⚠️ 避坑指南: 我曾经在项目里用了一个空列表作为默认参数,结果发现每次调用函数,列表里的数据居然会“累积”!
def add_item(item, items=[]):
items.append(item)
return items
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] —— 嗯?怎么还有1?
为什么会这样?因为默认参数 [] 只在函数定义时创建一次,之后每次调用用的都是同一个列表。正确的做法是用 None 作为默认值,在函数内部再创建新列表。
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
4.1.3 返回值:一个函数只能返回一个值?
不,Python可以返回多个值,实际上是以元组的形式返回:
def min_max(numbers):
return min(numbers), max(numbers)
result = min_max([3, 1, 4, 1, 5])
print(result) # (1, 5)
print(type(result)) # <class 'tuple'>
你可以直接解包:
min_val, max_val = min_max([3, 1, 4, 1, 5])
print(min_val, max_val) # 1 5
4.2 文件读写:让数据“持久化”
程序跑完,内存里的数据就没了。怎么把数据存下来?写文件。
Python的文件操作核心就三个字:开、读/写、关。
4.2.1 打开文件
用 open() 函数,指定文件名和模式:
| 模式 | 含义 |
|---|---|
'r' |
只读(默认) |
'w' |
写入(会覆盖原有内容) |
'a' |
追加(在文件末尾添加) |
'r+' |
读写 |
4.2.2 读写操作
写文件:
file = open("notes.txt", "w", encoding="utf-8")
file.write("第一行内容\n")
file.write("第二行内容\n")
file.close()
读文件:
file = open("notes.txt", "r", encoding="utf-8")
content = file.read() # 一次性读完
print(content)
file.close()
或者一行一行读:
file = open("notes.txt", "r", encoding="utf-8")
for line in file:
print(line.strip()) # strip() 去掉换行符
file.close()
close()。忘记关闭文件,轻则资源泄露,重则文件损坏。我曾经在跑一个批量处理任务时,因为忘记关闭文件,导致系统文件句柄耗尽,整个程序崩溃了。
4.2.3 更优雅的方式:with 语句
Python提供了 with 语句,它会自动帮你关闭文件,再也不用担心忘记 close() 了:
# 写文件
with open("notes.txt", "w", encoding="utf-8") as f:
f.write("自动关闭,省心省力\n")
# 读文件
with open("notes.txt", "r", encoding="utf-8") as f:
for line in f:
print(line.strip())
我个人强烈建议:所有文件操作都用 with 语句。这是Python社区的共识,也是专业代码的标配。
4.3 异常处理:给程序穿上“防弹衣”
程序运行中,意外总会发生。文件不存在、网络断开、用户输入了非法数据……这些“异常”如果不处理,程序就会直接崩溃。
异常处理,说白了就是给程序穿上防弹衣——遇到子弹(错误),不是直接倒下,而是先挡一下,然后优雅地处理。
4.3.1 try-except 结构
try:
# 可能出错的代码
num = int(input("请输入一个数字:"))
result = 10 / num
print(f"结果是:{result}")
except ValueError:
print("输入的不是有效数字!")
except ZeroDivisionError:
print("不能除以零!")
except Exception as e:
print(f"发生了未知错误:{e}")
你看,try 块里放“危险”代码,except 块里放“救火”代码。可以针对不同的错误类型分别处理。
4.3.2 else 和 finally
这两个是“配角”,但很有用:
else:没有异常时执行finally:不管有没有异常,都会执行(常用于关闭资源)
try:
file = open("data.txt", "r")
content = file.read()
except FileNotFoundError:
print("文件没找到,请检查路径")
else:
print("文件读取成功!")
finally:
file.close() # 无论如何都要关闭文件
if 判断的,就别用 try。比如检查文件是否存在,用 os.path.exists() 比用 try-except 更清晰。异常处理应该留给那些“你无法预判”的错误。
4.4 面向对象编程入门:从“函数”到“对象”
前面我们学的都是“面向过程”——数据是数据,函数是函数,各管各的。
面向对象编程(OOP)则不同。它把数据和操作数据的方法“打包”在一起,形成一个“对象”。你想想看,现实世界里的东西不都是这样吗?一辆车有属性(颜色、速度),也有方法(加速、刹车)。
4.4.1 类和对象
类 是模板,对象 是实例。
就像“汽车设计图”是类,路上跑的那辆“红色特斯拉”就是对象。
class Car:
"""汽车类"""
def __init__(self, brand, color):
self.brand = brand # 属性
self.color = color
self.speed = 0
def accelerate(self, amount):
"""加速方法"""
self.speed += amount
print(f"{self.color}的{self.brand}加速到 {self.speed} km/h")
def brake(self):
"""刹车方法"""
self.speed = 0
print("已刹停")
# 创建对象
my_car = Car("特斯拉", "红色")
my_car.accelerate(50) # 红色的特斯拉加速到 50 km/h
my_car.brake() # 已刹停
注意那个 __init__ 方法,它是“构造方法”,在创建对象时自动调用,用来初始化对象的属性。self 代表对象本身,每个方法第一个参数都是 self。
4.4.2 封装:把“内部细节”藏起来
封装是OOP的核心思想之一。简单说就是:对外只暴露必要的接口,内部实现细节隐藏起来。
Python里用下划线表示“私有”:
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self._balance = balance # 一个下划线,表示“受保护的”
def deposit(self, amount):
if amount > 0:
self._balance += amount
print(f"存款成功,余额:{self._balance}")
else:
print("存款金额必须大于0")
def get_balance(self):
return self._balance
# 外部代码不应该直接访问 _balance
account = BankAccount("张三", 1000)
account.deposit(500)
print(account.get_balance()) # 1500
嗯,这里要注意:Python没有真正的“私有”,这只是约定。但专业程序员都会遵守这个约定。
4.4.3 继承:站在巨人的肩膀上
继承允许你基于一个已有的类创建新类,新类会自动拥有父类的所有属性和方法。
class ElectricCar(Car): # 继承自 Car
def __init__(self, brand, color, battery_capacity):
super().__init__(brand, color) # 调用父类的构造方法
self.battery = battery_capacity
def charge(self):
print(f"{self.brand}正在充电,电池容量:{self.battery} kWh")
# 使用
my_ev = ElectricCar("比亚迪", "白色", 75)
my_ev.accelerate(30) # 继承自父类的方法
my_ev.charge() # 子类自己的方法
super() 用来调用父类的方法,这是继承中的标准写法。
知识体系总览
这一章的内容,我用一张图帮你理清脉络:
这一章的内容就到这里。函数、文件、异常、OOP——这四个东西组合起来,你已经能写出结构清晰、健壮可靠的Python程序了。下一章,我们会把这些知识用在法律文本处理上,那才是真正有意思的地方。