一种全新的思维方式|《Think Python》第14章
14. 类和函数
到目前为止,你已经知道如何使用函数组织代码,如何使用内置类型组织数据。下一步是学习面向对象编程,它使用自定义类型同时管理代码与数据。
本章我们从最简单的自定义类开始,学习定义类、创建对象、属性、纯函数、对象复制与时间计算,为面向对象编程打下基础。
14.1 自定义类型
我们可以定义新的类型,例如定义一个表示时间的类 Time。
自定义类型也叫作类(class)。
# 定义 Time 类,表示一天中的时间classTime:"""代表一天中的时间"""
定义类会创建一个类对象,它像一个工厂,用来生产具体的对象。
# 创建 Time 对象(实例化)lunch = Time()# 查看对象类型type(lunch)
运行结果:
__main__.Time
# 打印对象(显示类型和内存地址)print(lunch)
运行结果:
<__main__.Time object at 0x7f31440ad0c0>
创建对象的过程叫作实例化,这个对象就是 Time 类的一个实例。
14.2 属性
对象可以包含变量,这些变量叫作属性(attribute)。
我们用点号给对象添加属性:
# 给 lunch 对象添加时分秒属性lunch.hour = 11lunch.minute = 59lunch.second = 1
读取属性也用点号:
lunch.hour
运行结果:
11
格式化打印时间
# 打印 Time 对象,格式化为 00:00:00defprint_time(time): s = f'{time.hour:02d}:{time.minute:02d}:{time.second:02d}'print(s)
# 调用打印print_time(lunch)
运行结果:
11:59:01
14.3 函数返回对象
函数可以创建并返回对象。
# 创建并返回一个 Time 对象defmake_time(hour, minute, second): time = Time() time.hour = hour time.minute = minute time.second = secondreturn time
# 使用工厂函数创建时间start = make_time(9, 20, 0)print_time(start)
运行结果:
09:20:00
14.4 对象是可变的
可以直接修改对象的属性。
# 给时间增加时长(直接修改原对象)defincrement_time(time, hours, minutes, seconds): time.hour += hours time.minute += minutes time.second += seconds
# 测试:电影开始时间 + 时长start = make_time(9, 20, 0)increment_time(start, 1, 32, 0)print_time(start)
运行结果:
10:52:00
问题:这种方式会修改原对象。
14.5 对象复制
使用 copy 模块复制对象,不修改原数据。
from copy import copy# 复制一个对象start = make_time(9, 20, 0)end = copy(start)
# 两个对象内容相同print_time(start)print_time(end)
运行结果:
09:20:0009:20:00
# 但不是同一个对象start is end
运行结果:
False
注意:自定义对象默认 == 比较的是身份,不是内容。
14.6 纯函数
纯函数:不修改传入的对象,只返回新对象。
# 纯函数:时间相加,返回新对象,不修改原对象defadd_time(time, hours, minutes, seconds):# 复制原时间 total = copy(time)# 增加时间 increment_time(total, hours, minutes, seconds)# 返回新时间return total
start = make_time(9, 20, 0)end = add_time(start, 1, 32, 0)print_time(start)print_time(end)
运行结果:
09:20:0010:52:00
纯函数优点:
14.7 原型与修补(先跑再改)
先写出简单版本,再逐步修补边界问题。
原始 increment_time 没有处理满60进1:
start = make_time(9, 40, 0)end = add_time(start, 1, 32, 0)print_time(end)
运行结果:
10:72:00
改进版(处理进位)
defincrement_time(time, hours, minutes, seconds):# 先直接相加 time.hour += hours time.minute += minutes time.second += seconds# 秒进位到分 carry, time.second = divmod(time.second, 60)# 分进位到时 carry, time.minute = divmod(time.minute + carry, 60)# 时进位(24小时制) carry, time.hour = divmod(time.hour + carry, 24)
现在结果正确:
start = make_time(9, 40, 0)end = add_time(start, 1, 32, 0)print_time(end)
运行结果:
11:12:00
14.8 先设计再开发(更优思路)
把时间看成 60进制数字,先转成秒,运算后再转回来。
# 时间 → 总秒数deftime_to_int(time): minutes = time.hour * 60 + time.minute seconds = minutes * 60 + time.secondreturn seconds
# 总秒数 → 时间defint_to_time(seconds): minute, second = divmod(seconds, 60) hour, minute = divmod(minute, 60)return make_time(hour, minute, second)
超级简洁的时间相加
defadd_time(time, hours, minutes, seconds):# 时长转为 Time 对象 duration = make_time(hours, minutes, seconds)# 都转为秒相加 seconds_total = time_to_int(time) + time_to_int(duration)# 转回时间return int_to_time(seconds_total)
start = make_time(9, 40, 0)end = add_time(start, 0, 90, 120)print_time(end)
运行结果:
11:12:00
优点:
14.9 调试工具
Python 提供了调试对象的工具:
# 查看类型type(start)
运行结果:
__main__.Time
# 判断是否为某个类的实例isinstance(start, Time)
运行结果:
True
# 判断是否有某个属性hasattr(start, 'hour')
运行结果:
True
# 将对象属性转为字典查看vars(start)
运行结果:
{'hour': 9, 'minute': 40, 'second': 0}
14.10 术语表
| | |
|---|
| object-oriented programming | | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
14.11 练习(带标准答案)
14.11.2 时间相减(求间隔秒数)
defsubtract_time(t1, t2):"""返回两个时间的间隔秒数"""return time_to_int(t1) - time_to_int(t2)
14.11.3 判断时间先后
defis_after(t1, t2):"""判断 t1 是否在 t2 之后"""return time_to_int(t1) > time_to_int(t2)
14.11.4 日期类练习
# 定义日期类classDate:"""代表年月日"""
# 创建日期对象defmake_date(year, month, day): d = Date() d.year = year d.month = month d.day = dayreturn d
# 格式化打印日期defprint_date(date):print(f'{date.year:04d}-{date.month:02d}-{date.day:02d}')
# 判断日期先后defis_after_date(d1, d2):return (d1.year, d1.month, d1.day) > (d2.year, d2.month, d2.day)