8.代码风格调试%结课竞赛
作业系统链接
1.代码风格
- 前提:在对代码进行“修饰”之前,确保代码没有任何bug。
- 非强制性:代码风格的学习不是强制性的,不影响后续课程的学习,但良好的代码风格有助于提高代码的可读性和维护性。
缩进与换行
代码布局 - 缩进
- 如果有开始定界符(如函数定义或控制结构),其余行的缩进需与开始定界符对齐。
- 需要额外的4个空格(相当于一个Tab键)来区分开传入参数和其他内容。
- 空格一般用于添加这种缩进,而Tab键通常用来保持行间的一致性。
- 对于多行if语句,需要一个额外的缩进来区分其他内容。
代码布局 - 换行
- 将所有行限制为最多79个字符。
- 一般语句接受隐式延续,但
with
语句等不支持,这时需要用反斜杠\
来衔接。 assert
语句同样不支持隐式延续,也需要使用反斜杠\
。- 多行if语句的缩进详见上一小节。
- 不鼓励使用复合语句(同一行上的多个语句)。
导入规范
导入本地模块/包
- 当需要导入自己封装的模块时,使用
import
关键字直接导入。 - 同目录下的模块可以直接通过名称导入。
导入Python库的模块/包
- Python自带了丰富的基础库,可以通过简单的
import
语句来享用这些功能。 - 使用
from x import y
语法,其中x
是包前缀,y
是没有前缀的模块名。
命名规则
- 变量名只能包含字母、数字和下划线;变量名可以以字母或下划线开头,但不能以数字开头。
- 变量名不应包含空格,可以使用下划线来分隔单词。
- 不应使用Python关键字作为变量名。
- 变量名应该简短且具有描述性。
关于空格
- 圆括号、方括号或大括号内不需要多余空格。
- 在逗号、分号或冒号之前及尾随逗号之后不需要多余空格。
- 切片中两个冒号之间必须有相同的间距。
- 函数调用的左括号前不需要多余空格。
- 赋值运算符周围需要适当空格以提高可读性。
代码注释
- 注释是对代码的解释说明,目的是帮助他人更容易理解代码。
- 单行注释使用
#
符号,多行注释则使用三个引号'''
或"""
。 - 在处理逻辑复杂的代码段中,有效注释量应至少占源代码的20%。
2. Debug
调试理论与常见错误
- 程序作为状态机:将程序视为一个状态机,其执行过程即为状态的变化。出现bug意味着在某个环节发生了异常。
- Bug分类:
- 理解需求差异产生的bug:设计偏离了最初规划的功能,导致无法满足用户需求。
- 具体实现中的bug:技术原因导致程序不能很好地完成任务,通常是数学模型存在缺陷。
- 常见错误:
- 漏掉了末尾的冒号(如if语句、循环语句、函数定义)。
- 缩进错误,没有正确缩进。
- 英文符号写成中文符号(如引号、括号等)。
- 字符串和数字拼接错误。
- 变量未定义。
- “==”(比较运算符)与“=”(赋值运算符)混用。
- Tab键与空格混用。
利用IDE进行基本错误检测
- 现代IDE(如VSCode)可以自动指出语法错误,这些错误通过本地Python解释器检测。
- IDE仅能帮助识别基础问题,如函数书写方式或API调用方式;对于逻辑问题,通常需要从最终结果中判断并测试。
示例:质数查找
- 示例代码展示了如何找出[10,20)之间的所有质数,同时指出了即使代码运行成功也可能存在隐藏的逻辑问题,这需要通过大量测试数据才能发现。
使用print
与assert
进行调试
print
方法简单易懂但可能使代码难以阅读和维护,在多线程或异步编程中可能导致输出混乱。assert
用于检查假设条件,但在生产环境中不应使用,且只能提供简单的错误提示。
利用logging
模块进行日志记录
logging
模块允许开发者设置不同级别的日志信息,包括DEBUG、INFO、WARNING、ERROR、CRITICAL。- 设置日志级别后,只有大于等于该级别的信息会被记录。
- 示例:
import logging logging.basicConfig(level=logging.DEBUG) logging.debug('This is a debug message')
在VSCode中配置调试
- 创建
launch.json
文件以配置调试参数,例如添加"args"参数。{ "version": "0.2.0", "configurations": [ { "name": "Python: 当前文件", "type": "python", "request": "launch", "program": "${file}", "console": "integratedTerminal", "justMyCode": true, "args": ["6"] } ] }
- 选择创建好的
launch.json
配置文件并打上断点运行。
使用pdb
进行交互式调试
pdb
是Python自带的调试器,可以通过命令行启动。- 命令:
python -m pdb your_script.py
- 常用命令:
break
或b
:设置断点continue
或c
:继续执行程序list
或l
:查看当前代码段step
或s
:进入函数return
或r
:执行到当前函数返回next
或n
:执行下一行up
或u
:返回到上个调用点p x
:打印变量x的值exit
或q
:退出调试器help
:获取帮助
- 命令:
其他调试技巧
- 使用
breakpoint()
:Python 3.7及以上版本引入了breakpoint()
函数,它会在执行时暂停程序并启动调试器。 - 推荐工具:除了
pdb
外,还有PySnooper
等第三方库可以帮助更高效地调试。
3.结课竞赛
3.1题目描述
小鲸鱼最近学习了 Datawhale 的聪明办法学 Python 线下课程,这门课程的总成绩计算方法是:
总成绩 = 作业成绩 ×20%+×20%+ 小测成绩 ×30%+×30%+ 期末考试成绩 ×50%×50%
小鲸鱼想知道,这门课程自己最终能得到多少分。
输入格式
三个非负整数 A,B,CA,B,C,分别表示小鲸鱼的作业成绩、小测成绩和期末考试成绩。相邻两个数之间用一个空格隔开,三项成绩满分都是 100100 分。
输出格式
一个整数,即小鲸鱼这门课程的总成绩,满分也是 100100 分。
代码
def calculate_total_score(A, B, C):
# 计算总成绩
total_score = A * 0.20 + B * 0.30 + C * 0.50
# 返回总成绩的整数部分
return round(total_score)
# 读取输入数据
A, B, C = map(int, input().split())
# 计算并输出总成绩
print(calculate_total_score(A, B, C))
3.2.题目描述
亚运会开始了,小鲸鱼在拼命练习游泳准备参加游泳比赛,可怜的小鲸鱼并不知道鱼类是不能参加人类的奥运会的。
这一天,小鲸鱼给自己的游泳时间做了精确的计时(本题中的计时都按 2424 小时制计算),它发现自己从 aa 时 bb 分一直游泳到当天的 cc 时 dd 分,请你帮小鲸鱼计算一下,它这天一共游了多少时间呢?
小鲸鱼游的好辛苦呀,你可不要算错了哦。
输入格式
一行内输入四个整数,以空格隔开,分别表示题目中的 a,b,c,da,b,c,d。
输出格式
一行内输出两个整数 ee 和 ff,用空格间隔,依次表示小鲸鱼这天一共游了多少小时多少分钟。其中表示分钟的整数 ff 应该小于 6060。
代码
def calculate_swimming_time(a, b, c, d):
# 将时间转换为分钟
start_time = a * 60 + b
end_time = c * 60 + d
# 计算时间差
time_difference = end_time - start_time
# 将时间差转换为小时和分钟
hours = time_difference // 60
minutes = time_difference % 60
return hours, minutes
# 读取输入数据
a, b, c, d = map(int, input().split())
# 计算并输出游泳时间
hours, minutes = calculate_swimming_time(a, b, c, d)
print(hours, minutes)
3.3.题目描述
小鲸鱼上初中了。鲸鱼妈妈认为小鲸鱼应该更加用功学习,所以小鲸鱼除了上学之外,还要参加鲸鱼妈妈为它报名的各科复习班。另外每周鲸鱼妈妈还会送它去学习朗诵、舞蹈和钢琴。但是小鲸鱼如果一天上课超过八个小时就会不高兴,而且上得越久就会越不高兴。假设小鲸鱼不会因为其它事不高兴,并且它的不高兴不会持续到第二天。请你帮忙检查一下小鲸鱼下周的日程安排,看看下周它会不会不高兴;如果会的话,哪天最不高兴。
输入格式
输入包括 77 行数据,分别表示周一到周日的日程安排。每行包括两个小于 1010 的非负整数,用空格隔开,分别表示小鲸鱼在学校上课的时间和鲸鱼妈妈安排它上课的时间。
输出格式
一个数字。如果不会不高兴则输出 00,如果会则输出最不高兴的是周几(用 1,2,3,4,5,6,71,2,3,4,5,6,7 分别表示周一,周二,周三,周四,周五,周六,周日)。如果有两天或两天以上不高兴的程度相当,则输出时间最靠前的一天。
代码
def find_most_unhappy_day():
max_hours = 0
most_unhappy_day = 0
for i in range(1, 8):
school_hours, extra_hours = map(int, input().split())
total_hours = school_hours + extra_hours
if total_hours > max_hours:
max_hours = total_hours
most_unhappy_day = i
if max_hours > 8:
return most_unhappy_day
else:
return 0
# 执行函数并输出结果
print(find_most_unhappy_day())
3.4.题目描述
小鲸鱼的词汇量很小,所以每次做英语选择题的时候都很头疼。但是它找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大!
这种方法的具体描述如下:假设 maxnmaxn 是单词中出现次数最多的字母的出现次数,minnminn 是单词中出现次数最少的字母的出现次数,如果 maxn−minnmaxn−minn 是一个质数,那么小鲸鱼就认为这是个 Lucky Word,这样的单词很可能就是正确的答案。
输入格式
一个单词,其中只可能出现小写字母,并且长度小于 100100。
输出格式
共两行,第一行是一个字符串,假设输入的单词被验证是 Lucky Word
,那么输出字符串 Lucky Word
,否则输出 No Answer
;
第二行是一个整数,如果输入单词被验证是 Lucky Word
,输出 maxn−minnmaxn−minn 的值,否则输出 00。
代码
def is_prime(n):
if n <= 1:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
def find_lucky_word(word):
# 计算每个字母的出现次数
letter_counts = {}
for letter in word:
if letter in letter_counts:
letter_counts[letter] += 1
else:
letter_counts[letter] = 1
# 找出出现次数最多的字母和出现次数最少的字母
max_count = max(letter_counts.values())
min_count = min(letter_counts.values())
# 计算差值
difference = max_count - min_count
# 检查差值是否是质数
if is_prime(difference):
return "Lucky Word", difference
else:
return "No Answer", 0
# 读取输入数据
word = input().strip()
# 执行函数并输出结果
result, difference = find_lucky_word(word)
print(result)
print(difference)
3.5.题目描述
输入一个偶数 NN,验证 4∼N4∼N 所有偶数是否符合哥德巴赫猜想:任一大于 22 的偶数都可写成两个质数之和。如果一个数不止一种分法,则输出第一个加数相比其他分法最小的方案。例如 1010,10=3+7=5+510=3+7=5+5,则 10=5+510=5+5 是要被舍去的答案。
输入格式
第一行输入一个正偶数 NN
输出格式
输出 N−222N−2 行。对于第 ii 行:
首先先输出正偶数 2i+22i+2,然后输出等号,再输出加和为 2i+22i+2 且第一个加数最小的两个质数,以加号隔开。
代码
def is_prime(n):
if n <= 1:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
def find_prime_pair(even_number):
for i in range(2, even_number // 2 + 1):
if is_prime(i) and is_prime(even_number - i):
return i, even_number - i
return None
# 读取输入数据
N = int(input())
# 验证从 4 到 N 的所有偶数
for i in range(2, N // 2 + 1):
even_number = 2 * i
prime_pair = find_prime_pair(even_number)
if prime_pair:
# 正确地打印出两个质数
print(f"{even_number}={prime_pair[0]}+{prime_pair[1]}")