查看全课大纲(第 28 / 51 节)
- 1.为什么人人都该学点编程?
- 2.计算机与程序设计:你的电脑到底在“想”什么?
- 3.Python 这门语言,到底有什么魔力?
- 4.Python 开发环境配置:如何拥有你的第一个“编程工具箱”?
- 5.如何安装一个更强大的代码编辑器?
- 6.汇率兑换1.0:你的第一个“钱生钱”小程序
- 7.你的第一行代码,藏着哪些秘密?
- 8.汇率兑换2.0(上):让程序看懂“CNY”和“USD”
- 9.汇率兑换程序,如何让它“聪明”地判断币种?
- 10.汇率兑换程序能一直运行,直到我喊停吗?
- 11.汇率兑换4.0:如何把“算钱”功能打包成积木?
- 12.汇率兑换5.0:给你的程序穿上“结构化”的西装
- 13.怎么用 Python 画一个会动的五角星?
- 14.如何让程序自动画出不同大小的五角星?
- 15.函数自己调用自己,会发生什么?
- 16.用递归画出神奇的分形树
- 17.Python 怎么计算你一天躺着不动能消耗多少热量?
- 18.如何让程序“活”起来,持续为你服务?
- 19.如何让 BMR 计算器更“聪明”?
- 20.当程序遇到“不按套路出牌”的用户怎么办?
- 21.怎么用 Python 模拟一个“滚雪球”式的存钱计划?
- 22.如何用列表记录每一周的存款?
- 23.如何让程序自己数数?—— 用 for 循环解放你的双手
- 24.52周存钱挑战4.0:你的存款计算器能有多灵活?
- 25.52周存钱挑战:怎么根据日期自动查存款?
- 26.Python 怎么判断一年中的第几天?
- 27.列表 vs 元组:哪个更适合存储月份天数?
- 28.用集合给月份分个队,换个思路算天数
- 29.把月份和天数“锁”在一起:用字典给数据配个对
- 30.Python 怎么判断密码强弱?
- 31.Python 怎么给密码设置“防沉迷”系统?
- 32.Python 怎么把密码保存到文件里?
- 33.Python 怎么读取文件里的密码?
- 34.Python 怎么把密码检查工具“打包”起来?
- 35.Python 怎么把文件操作也“打包”起来?
- 36.计算机怎么“扔骰子”?——用Python模拟随机事件
- 37.如何用 Python 模拟掷两个骰子?
- 38.怎么用 Python 把骰子轨迹画出来?
- 39.骰子点数分布长啥样?用直方图一眼看穿!
- 40.用科学计算库让骰子程序“起飞”
- 41.空气质量指数怎么算?手把手教你写一个AQI计算器
- 42.空气质量指数计算:从零开始,让程序看懂空气
- 43.如何读取空气质量数据文件并找出空气质量最好的五个监测点?
- 44.JSON 数据怎么变成 Excel 表格?—— 让数据换个“马甲”
- 45.Python 怎么让程序自己判断文件类型?
- 46.如何让程序自己上网查空气质量?
- 47.用美味的汤来解析网页:Beautiful Soup 入门
- 48.如何让爬虫自动抓取全国所有城市的空气质量?
- 49.爬到的数据怎么存下来?—— 把全国空气质量数据保存为CSV文件
- 50.用 pandas 玩转空气质量数据:找出全国空气最好和最差的城市
- 51.空气质量数据怎么“洗”干净,还能画成图?
用集合给月份分个队,换个思路算天数
约 15 分钟
小象实战讲义 · Python 入门:8 个小项目边玩边学
还记得我们之前是怎么算天数的吗?我们用一个列表(或元组)把每个月的天数按顺序存起来,然后累加。这就像你有一个日历,一页一页翻过去,把天数加起来。
这一节,我们要玩点不一样的!我们来给月份重新“分个队”:把所有有31天的月份放到一个“篮子里”,把所有有30天的月份放到另一个“篮子里”。然后,我们只需要判断每个月份属于哪个“篮子”,就知道该加多少天了。这个神奇的“篮子”,就是Python里的集合(set)。
学完这一节,你的“判断第几天”程序将拥有第三种解法,你的编程工具箱里又会多一个趁手的工具——集合。
💡 核心导读
- 认识新朋友:集合:一种不重复、无顺序的数据容器,就像数学里的集合。
- 集合的魔法操作:求差集、交集、并集,和数学课上学的一模一样。
- 换个思路解决问题:把月份按天数分组,用集合的“成员检查”来快速判断。
- 修复一个“小陷阱”:在累加天数时,别忘了把“当天”也加进去。
集合:一个不装重复数据的“篮子”
我们已经认识了列表(list)和元组(tuple),它们都能装下一系列数据。今天要认识的集合(set),和它们有两个最大的不同:
- 集合里的元素不能重复。你往集合里放两个“苹果”,它最终只会保留一个。
- 集合里的元素没有顺序。你不能说“我要集合里的第3个元素”,因为它压根就没排第几。
这和我们数学里学的“集合”概念几乎一样。它特别适合用来做两件事:快速判断一个东西在不在这个组里,以及给一组数据去重。
怎么创建一个集合呢?很简单,用 set() 函数,或者直接用花括号 {} 把元素括起来。
# 创建一个集合
fruits = {'apple', 'banana', 'orange'}
print(sorted(fruits)) # sorted() 会把集合排序后转成列表:['apple', 'banana', 'orange']
# 用set()函数从列表转换
number_list = [1, 2, 2, 3, 3, 3]
number_set = set(number_list)
print(sorted(number_set)) # 输出排好序的列表:[1, 2, 3],重复的都被去掉了!['apple', 'banana', 'orange']
[1, 2, 3]本节新增了什么? 没错,这一节的主角就是集合 set 这个新数据类型!我们用它来给月份重新分类。
集合的“社交”操作:并集、交集、差集
集合之间可以像数学集合一样进行运算,非常直观。
假设我们有两个集合:
S = {1, 3, 5, 7, 9}T = {1, 2, 3, 4, 5}
S = {1, 3, 5, 7, 9}
T = {1, 2, 3, 4, 5}
# 差集:在S中但不在T中 (S - T)
print("差集 S - T:", S - T) # 输出:{9, 7}
# 也可以用方法
print("差集 S.difference(T):", S.difference(T)) # 输出:{9, 7}
# 交集:既在S中又在T中 (S & T)
print("交集 S & T:", S & T) # 输出:{1, 3, 5}
print("交集 S.intersection(T):", S.intersection(T)) # 输出:{1, 3, 5}
# 并集:S和T中所有的元素 (S | T)
print("并集 S | T:", S | T) # 输出:{1, 2, 3, 4, 5, 7, 9}
print("并集 S.union(T):", S.union(T)) # 输出:{1, 2, 3, 4, 5, 7, 9}
# 对称差集:在S或T中,但不同时在两者中 (S ^ T)
print("对称差集 S ^ T:", S ^ T) # 输出:{2, 4, 7, 9}差集 S - T: {9, 7}
差集 S.difference(T): {9, 7}
交集 S & T: {1, 3, 5}
交集 S.intersection(T): {1, 3, 5}
并集 S | T: {1, 2, 3, 4, 5, 7, 9}
并集 S.union(T): {1, 2, 3, 4, 5, 7, 9}
对称差集 S ^ T: {2, 4, 7, 9}这些操作在我们这个项目里暂时用不上,但它们是集合非常强大的功能,以后你会经常遇到。
实战:用集合重新构思“判断第几天”
好了,理论说再多不如动手。我们怎么用集合来改造我们的程序呢?
核心思路:我们创建两个集合,一个装所有有31天的月份,另一个装所有有30天的月份。二月份比较特殊,我们单独处理。
然后,对于输入月份之前的每一个月(比如输入3月,就看1月和2月),我们检查它属于哪个集合:
- 如果在“31天集合”里,总天数就加31。
- 如果在“30天集合”里,总天数就加30。
- 如果都不在(那只能是2月了),就先按平年加28天。
最后,再判断是否是闰年且月份大于2月,如果是,总天数再加1。
听起来是不是很清晰?我们来看看代码怎么写。
# 判断第几天 v3.0 - 使用集合
from datetime import datetime
def is_leap_year(year):
"""判断是否为闰年"""
return (year % 400 == 0) or ((year % 4 == 0) and (year % 100 != 0))
# 原程序这里使用 input() 获取用户输入,为了你能直接运行,我们用一个固定日期代替
# 你可以将下面这行改为你自己的日期,格式必须是 yyyy/mm/dd
date_str = '2018/03/05' # 例如:2018年3月5日
# 解析日期字符串为datetime对象,方便获取年、月、日
input_date = datetime.strptime(date_str, '%Y/%m/%d')
year = input_date.year
month = input_date.month
day = input_date.day
# 核心变化在这里!我们创建两个集合来给月份"分篮"
_30_days_month_set = {4, 6, 9, 11} # 有30天的月份集合
_31_days_month_set = {1, 3, 5, 7, 8, 10, 12} # 有31天的月份集合
# 初始化总天数,注意!这里一定要把"当天"的天数先加上
days = day
# 循环累加之前月份的天数
for i in range(1, month): # range(1, month) 会产生 1, 2, ..., month-1
if i in _30_days_month_set:
days += 30
elif i in _31_days_month_set:
days += 31
else:
# 既不在30天集合,也不在31天集合,那只能是2月了
days += 28
# 闰年且月份大于2月的特殊处理
if is_leap_year(year) and month > 2:
days += 1
print(f'{date_str} 是{year}年的第{days}天。')2018/03/05 是2018年的第64天。看,if i in _30_days_month_set: 这行代码就是集合的魔力!它用 in 关键字快速检查月份 i 是否在30天的集合里,非常直观和高效。
⚠️ 注意那个“小陷阱”:在最初的版本中,我们很容易忘记把输入日期中的“日”(day)加到总天数 days 里,导致结果总是少几天。所以初始化 days = 0 之后,一定要立刻 days += day。
思维升级:从“顺序查找”到“分组判断”
对比一下我们学过的三种方法:
- v1.0 用元组:把每月天数按顺序存好,直接按索引取。
- v2.0 用列表:和元组几乎一样,但列表可变。
- v3.0 用集合:不关心月份顺序,只关心它属于哪一组(30天组还是31天组)。
v3.0的思维是一种逆向思维。前两种方法是“我知道每个位置(月份)对应多少天”,而集合方法是“我知道多少天对应哪些月份”。哪种更好?没有绝对答案,但掌握多种思路,能让你在遇到问题时更加灵活。
📝 动手练一练
集合去重实验:请你写一段代码,将一个包含重复元素的列表
[5, 2, 5, 8, 2, 1, 8, 8]转换成集合,看看结果是什么。然后再将这个集合转换回列表,观察列表的变化。月份集合操作:我们定义了
_31_days_month_set和_30_days_month_set。请你思考一下,这两个集合的并集(union)应该是什么?写代码验证你的想法。那么,如果用一个集合表示“非31天的月份”,该怎么用集合运算得到?“陷阱”重现与修复:故意将上面v3.0程序中的
days = day改成days = 0,然后计算2018/01/05是第几天。运行程序,观察结果错在哪里。这能帮你深刻理解为什么那个初始化步骤如此重要。
👉 点击查看第1题参考答案
思路:利用集合自动去重的特性,先将列表转为集合,再将集合转回列表,即可得到无重复元素的列表。
original_list = [5, 2, 5, 8, 2, 1, 8, 8]
print("原始列表:", original_list)
# 转换为集合,自动去重
unique_set = set(original_list)
print("转换后的集合:", unique_set) # 顺序可能不同,如 {1, 2, 5, 8}
# 再转换回列表
new_list = list(unique_set)
print("去重后的列表:", new_list) # 如 [8, 1, 2, 5]原始列表: [5, 2, 5, 8, 2, 1, 8, 8]
转换后的集合: {8, 1, 2, 5}
去重后的列表: [8, 1, 2, 5]👉 点击查看第2题参考答案
思路:一年中除了2月,就是30天或31天的月份。所以这两个集合的并集应该包含1-12月除了2月以外的所有月份。“非31天的月份”就是全年月份集合减去“31天月份集合”,但要注意2月。
_31_days_month_set = {1, 3, 5, 7, 8, 10, 12}
_30_days_month_set = {4, 6, 9, 11}
# 1. 求并集:所有30天和31天的月份
union_set = _31_days_month_set | _30_days_month_set
print("30天与31天月份的并集:", union_set) # 输出:{1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
# 2. "非31天的月份" = 全年月份 - 31天月份
all_months_set = set(range(1, 13)) # {1,2,3,...,12}
non_31_days_set = all_months_set - _31_days_month_set
print("非31天的月份:", non_31_days_set) # 输出:{2, 4, 6, 9, 11}30天与31天月份的并集: {1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
非31天的月份: {2, 4, 6, 9, 11}本章小结
恭喜!你又解锁了一种新的数据容器——集合(set),并用它以一种全新的分组思维解决了“判断第几天”的问题。
📋 行动清单
- 运行今天的v3.0代码,尝试几个不同的日期,确保结果正确。
- 完成动手练习题,特别是体验一下集合去重和集合运算。
- 思考一下:如果让你设计一个程序,统计一篇文章里出现了哪些不同的单词,用集合是不是非常合适?
我们已经用元组、列表、集合三种方式解决了同一个问题。Python里还有一种非常强大、几乎每天都会用到的“键值对”容器,它能将月份和天数更直观地关联起来。想知道是什么吗?我们下节揭晓!
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。
- 下载第6章代码包:判断第几天(含全部版本 4 个 .py)ZIP · 3KB