提升 Python 函数质量的“五大习惯”
笔记简介
本文将探讨五个提升Python函数美观度与可维护性的良好习惯。同时,我将示例代码进行演示。
习惯一:指定返回类型
为函数显式指定返回类型可以极大地帮助静态类型检查器提前识别逻辑错误和类型不匹配问题,从而提高代码的鲁棒性。
比如,现在需要一个计算输入两数字之和,并最后以 float 类型返回的函数,可以想到写成这样
def num_sum(a, b):
return float(a + b)调用后传入参数 2 和 4 并打印结果和结果类型
temp = num_sum(2, 4)
print(temp)
print(type(temp))可以得到我们想要的结果 6.0 ,和 float 类型
6.0
<class 'float'>但如果代码写错了,让最后的返回结果强制转换成了 str 类型
def num_sum(a, b):
return str(a + b)依旧调用后传入参数 2 和 4 并打印结果
temp = num_sum(2, 4)
print(temp)
print(type(temp))虽然没报错,但是计算结果和返回类型却是不是我们预期的 6.0 和 float
6
<class 'str'>如果这个错误发生在一个大型 Python 项目中,你需要在没有任何提示的情况下去找到这个逻辑错误
说以就要有指定返回类型
def num_sum(a, b) -> float:
return float(a + b)如果我们错误的写成了强制转换成
def num_sum(a, b) -> float:
return str(a + b)IDE 会进行类型警告,虽然脚本依旧可以运行。但是,和刚刚提到的一样,如果这个错误发生在一个大型 Python 项目中,但你现在有了 IDE 的报警提示(如果非要说 AI ,那确实没辙了)

习惯二:指定参数类型
给函数参数明确指定类型同样重要,这样做可以在传入不兼容数据类型时及时收到编辑器警告,不仅增强了代码的可读性,还提高了调试效率。
还是刚刚的那段代码
def num_sum(a, b) -> float:
return float(a + b)但是,传入的参数是 "2" 和 "4"
temp = num_sum("2", "4")
print(temp)
print(type(temp))虽然没报错,但输出的结果是 float 类型的 24.0
24.0
<class 'float'>当然,你现在肯定能够一眼看出来。还是刚刚那句话:如果这个错误发生在一个大型 Python 项目中呢?
现在,为每个参数指定类型
def num_sum(a : float, b : float) -> float:
return float(a + b)现在去传入参数 "2" 和 "4" 。虽然依旧可以运行,但如果这个错误发生在一个大型 Python 项目中,但你现在有了 IDE 的报警提示(如果非要说 AI ,那确实没辙了,这句话怎么好像说过)

习惯三:避免使用可变默认参数
深入分析了在Python中采用可变默认参数(如空列表)可能引发的内存引用问题,并推荐了一种更优的做法——以None作为默认值,并通过适当的条件判断来初始化这些变量。
比如创建了一个就将元素添加到数组的函数
def addend_number(num : int, target : list[int] = []) -> list[int]:
target.append(num)
return target新增 l1 = [1,2,3] 数组,并调用该方法在 l1 中添加元素 6
l1 = [1,2,3]
addend_number(6,l1)
print(l1)得到了预想的输出结果
[1, 2, 3, 6]但是,如果为空数组 l2 和 l3 ,分别调用函数,并打印
l2 = addend_number(5)
l3 = addend_number(54)
print(l2,l3,sep='\n')会发现, 为 l2 和 l3 赋了同一个列表
由于 Python 在创建可变参数时,只会在内存上的一个地址上创建。赋值时, l2 和 l3 其实指向的同一个内存地址
[5, 54]
[5, 54]正确的做法应该是,避免使用可变默认参数
def addend_number(num : int, target : list[int] | None = None) -> list[int]:
target = target or []
target.append(num)
return target现在运行以下代码,会发现,l2 和 l3 的值现在相互独立。及每次调用该函数,不再是只使用同一块内存了
l2 = addend_number(1)
addend_number(5,l2)
addend_number(3,l2)
l3 = addend_number(54)
addend_number(23,l3)
print(l2,l3,sep='\n')输出如下,l2 和 l3 的值现在相互独立
[1, 5, 3]
[54, 23]习惯四:减少不必要的依赖
介绍了如何通过降低对全局变量或特定模块状态的依赖来加强函数的独立性和复用性,使每个函数都能成为一个自包含的代码单元。
比如,现在我需要一个能够为传入的 int 类型加 10 并返回结果的方法,可以写成这两种形式
第一种形式:
add_num : int = 10 def add_10(num : float) -> float: return num + add_num
第二种形式:
def add_10(num : float) -> float: add_num : int = 10 return num + add_num
都传入参数 5 ,运行结果都是 15
print(add_10(5))然而,第二种写法是优于第一种写法的
如果,我们想将 add_10() 函数,移动到 math_utils.py 里面,第二种将常用的变量封装在函数内部的写法是更加推荐的
习惯五:使用卫语句 (Guard Clauses)
对比了复杂的多层if-else嵌套结构与卫语句的应用,强调后者能够有效简化代码结构,减少嵌套层级,让程序逻辑更加简洁明了。
比如,我现在需要一个账户登录的方法,需要判断
账号是否存在
密码长度是否在 6 到 12 位
验证码是否正确
密码是否正确
下面我可以写一段小 Demo:
测试用户数据
user = { "username" : "playereg", "password" : "123456", "secure_code" : "S34U23" }主体登录验证方法:
def login_demo_1( user_name : str, password : str, secure_code : str, ) -> bool: if user_name == user["username"]: if 6 <= len(password) <= 12: if password == user["password"]: if secure_code == user["secure_code"]: print("登录成功") return True else: print("安全码错误") return False else: print("密码错误") return False else: print("密码长度不符合要求") return False else: print("用户名错误") return False
糟糕的嵌套!足足有四层!如果是大项目就很容易发生这件事👇👇👇

但是,可以使用卫语句
def login_demo_2(
user_name : str,
password : str,
secure_code : str,
) -> bool:
if not user_name == user["username"]:
print("用户名错误")
return False
if not 6 <= len(password) <= 12:
print("密码长度不符合要求")
return False
if not password == user["password"]:
print("密码错误")
return False
if not secure_code == user["secure_code"]:
print("安全码错误")
return False
print("登录成功")
return True并且,两段代码是等效代码,使用以下代码进行测试
if __name__ == "__main__":
user_name = "playereg"
user_name2 = "playereg2"
password = "123456"
password2 = "1234"
password3 = "abcdefghijkl"
secure_code = "S34U23"
secure_code2 = "S34U24"
# 登录成功
print(f"{'登录成功测试':-^50}")
print(login_demo_1(user_name,password,secure_code))
print(login_demo_2(user_name,password,secure_code))
# 登录失败 - 用户名错误
print(f"{'登录失败测试 - 用户名错误':-^50}")
print(login_demo_1(user_name2,password,secure_code))
print(login_demo_2(user_name2,password,secure_code))
# 登录失败 - 密码长度不符合要求
print(f"{'登录失败测试 - 密码长度不符合要求':-^50}")
print(login_demo_1(user_name,password2,secure_code))
print(login_demo_2(user_name,password2,secure_code))
# 登录失败 - 密码错误
print(f"{'登录失败测试 - 密码错误':-^50}")
print(login_demo_1(user_name,password3,secure_code))
print(login_demo_2(user_name,password3,secure_code))
# 登录失败 - 安全码错误
print(f"{'登录失败测试 - 安全码错误':-^50}")
print(login_demo_1(user_name,password,secure_code2))
print(login_demo_2(user_name,password,secure_code2))输出结果:
----------------------登录成功测试----------------------
登录成功
True
登录成功
True
------------------登录失败测试 - 用户名错误------------------
用户名错误
False
用户名错误
False
----------------登录失败测试 - 密码长度不符合要求----------------
密码长度不符合要求
False
密码长度不符合要求
False
------------------登录失败测试 - 密码错误-------------------
密码错误
False
密码错误
False
------------------登录失败测试 - 安全码错误------------------
安全码错误
False
安全码错误
False