5个简单而有效的Python技巧,用于编写更好、更可维护的函数
off999 2024-10-23 12:40 31 浏览 0 评论
Python 中的函数可让编写模块化代码。当需要在多个地方执行一项任务时,可以将任务的逻辑包装到 Python 函数中。并且可以在每次需要执行该特定任务时调用该函数。虽然开始使用 Python 函数似乎很简单,但编写可维护且性能良好的函数并不那么简单。
一个函数只做一件事
在用 Python 编写函数时,人们常常会将所有相关任务放入一个函数中。虽然这可以帮助您快速编写代码,但在不久的将来,这只会使您的代码难以维护。这不仅会使理解函数的功能变得更加困难,还会导致其他问题,例如参数过多。
作为一种良好做法,应该始终让您数只做一件事(一项任务),并做好这件事。但有时,对于一项任务,可能需要完成一系列子任务。那么,如何决定是否以及如何重构该函数?
根据函数要执行的操作以及任务的复杂程度,可以确定子任务之间的关注点分离。然后确定一个合适的级别,可以在该级别将函数重构为多个函数 - 每个函数专注于特定的子任务。
这是一个例子。看一下这个函数analyze_and_report_sales:
# fn. to analyze sales data, calculate sales metrics, and write it to a file
def analyze_and_report_sales(data, report_filename):
total_sales = sum(item['price'] * item['quantity'] for item in data)
average_sales = total_sales / len(data)
with open(report_filename, 'w') as report_file:
report_file.write(f"Total Sales: {total_sales}\n")
report_file.write(f"Average Sales: {average_sales}\n")
return total_sales, average_sales
很容易看出它可以重构为两个函数:一个计算销售指标,另一个将销售指标写入文件,如下所示:
# refactored into two funcs: one to calculate metrics and another to write sales report
def calculate_sales_metrics(data):
total_sales = sum(item['price'] * item['quantity'] for item in data)
average_sales = total_sales / len(data)
return total_sales, average_sales
def write_sales_report(report_filename, total_sales, average_sales):
with open(report_filename, 'w') as report_file:
report_file.write(f"Total Sales: {total_sales}\n")
report_file.write(f"Average Sales: {average_sales}\n")
现在,可以更轻松地分别调试与销售指标计算和文件操作相关的任何问题。以下是示例函数调用:
data = [{'price': 100, 'quantity': 2}, {'price': 200, 'quantity': 1}]
total_sales, average_sales = calculate_sales_metrics(data)
write_sales_report('sales_report.txt', total_sales, average_sales)
应该能够在工作目录中看到包含销售指标的“sales_report.txt”文件。这是一个简单的入门示例,但它非常有用,尤其是在您处理更复杂的函数时。
2. 添加类型提示以提高可维护性
Python 是一种动态类型语言。因此,无需为创建的变量声明类型。但您可以添加类型提示来指定变量的预期数据类型。定义函数时,可以为参数和返回值添加预期数据类型。
由于 Python 不会在运行时强制类型,因此添加类型提示在运行时不会产生任何效果。但使用类型提示仍然有好处,尤其是在可维护性方面:
- 向 Python 函数添加类型提示可作为内联文档,并可以更好地了解函数的作用以及它使用和返回的值。
- 当向函数添加类型提示时,可以配置 IDE 以利用这些类型提示。因此,如果您尝试在一个或多个函数调用中传递无效类型的参数、实现返回值与预期类型不匹配的函数等,将收到有用的警告。因此,您可以提前将错误降到最低。
- 可以选择使用静态类型检查器(例如mypy)来尽早捕获错误,而不是让类型不匹配引入难以调试的细微错误。
以下是处理订单详情的函数:
# fn. to process orders
def process_orders(orders):
total_quantity = sum(order['quantity'] for order in orders)
total_value = sum(order['quantity'] * order['price'] for order in orders)
return {
'total_quantity': total_quantity,
'total_value': total_value
}
现在向函数添加类型提示,如下所示:
# modified with type hints
from typing import List, Dict
def process_orders(orders: List[Dict[str, float | int]]) -> Dict[str, float | int]:
total_quantity = sum(order['quantity'] for order in orders)
total_value = sum(order['quantity'] * order['price'] for order in orders)
return {
'total_quantity': total_quantity,
'total_value': total_value
}
通过修改后的版本,可以知道该函数接受一个字典列表。字典的键应全部为字符串,值可以是整数或浮点值。该函数还返回一个字典。现在进行一个示例函数调用:
# Sample data
orders = [
{'price': 100.0, 'quantity': 2},
{'price': 50.0, 'quantity': 5},
{'price': 150.0, 'quantity': 1}
]
# Sample function call
result = process_orders(orders)
print(result)
输出如下:
{'total_quantity': 8, 'total_value': 600.0}
在此示例中,类型提示可帮助我们更好地了解函数的工作原理。
3. 只接受你真正需要的参数
如果是初学者或刚刚开始从事第一个开发角色,那么在定义函数签名时考虑不同的参数非常重要。确保函数只接受实际需要的参数,可以让函数调用更简洁、更易于维护。另外,函数中的参数过多也会使维护变得困难。那么,如何定义具有适当数量参数的易于维护的函数呢?
如果发现编写的函数的参数数量越来越多,第一步是从函数中删除所有未使用的参数。如果即使执行此步骤后参数仍然太多,请返回提示 1:将任务分解为多个子任务,并将函数重构为多个较小的函数。这将有助于控制参数数量。
现在来看一个简单的例子。这里计算学生成绩的函数定义包含instructor从未使用过的参数:
# takes in an arg that's never used!
def process_student_grades(student_id, grades, course_name, instructor'):
average_grade = sum(grades) / len(grades)
return f"Student {student_id} achieved an average grade of {average_grade:.2f} in {course_name}."
可以像这样重写不带参数的函数instructor:
# better version!
def process_student_grades(student_id: int, grades: list, course_name: str) -> str:
average_grade = sum(grades) / len(grades)
return f"Student {student_id} achieved an average grade of {average_grade:.2f} in {course_name}."
# Usage
student_id = 12345
grades = [85, 90, 75, 88, 92]
course_name = "Mathematics"
result = process_student_grades(student_id, grades, course_name)
print(result)
这是函数调用的输出:
Student 12345 achieved an average grade of 86.00 in Mathematics.4. 强制使用关键字参数以最大程度地减少错误
实际上,大多数 Python 函数都接受多个参数。您可以将参数作为位置参数、关键字参数或两者的混合传递给 Python 函数。有些参数天生就是位置参数。但有时函数调用只包含位置参数可能会造成混淆。当函数接受多个相同数据类型的参数时尤其如此,有些参数是必需的,有些是可选的。
使用位置参数时,参数会按照它们在函数调用中出现的顺序传递给函数签名中的参数。因此,参数顺序的变化可能会引入细微的错误和类型错误。
将可选参数设为仅关键字通常很有帮助。这也使得添加可选参数变得更加容易 — 而不会破坏现有调用。
这是一个例子。该process_payment函数接受一个可选description字符串:
# example fn. for processing transaction
def process_payment(transaction_id: int, amount: float, currency: str, description: str = None):
print(f"Processing transaction {transaction_id}...")
print(f"Amount: {amount} {currency}")
if description:
print(f"Description: {description}")
假设想让可选参数成为description仅关键字参数。可以这样做:
# enforce keyword-only arguments to minimize errors
# make the optional `description` arg keyword-only
def process_payment(transaction_id: int, amount: float, currency: str, *, description: str = None):
print(f"Processing transaction {transaction_id}:")
print(f"Amount: {amount} {currency}")
if description:
print(f"Description: {description}")
现在来看一个示例函数调用:
process_payment(1234, 100.0, 'USD', description='Payment for services')输出如下:
Processing transaction 1234...
Amount: 100.0 USD
Description: Payment for services
现在尝试将所有参数作为位置传递:
# throws error as we try to pass in more positional args than allowed!
process_payment(5678, 150.0, 'EUR', 'Invoice payment')
将收到如下所示的错误:
Traceback (most recent call last):
File "/home/balapriya/better-fns/tip4.py", line 9, in
process_payment(1234, 150.0, 'EUR', 'Invoice payment')
TypeError: process_payment() takes 3 positional arguments but 4 were given
5. 不要从函数返回列表;改用生成器
编写生成序列(例如值列表)的 Python 函数很常见。但应尽可能避免从 Python 函数返回列表。相反,可以将它们重写为生成器函数。生成器使用惰性求值;因此它们会按需生成序列的元素,而不是提前计算所有值。举例来说,以下函数可生成达到一定上限的斐波那契数列:
# returns a list of Fibonacci numbers
def generate_fibonacci_numbers_list(limit):
fibonacci_numbers = [0, 1]
while fibonacci_numbers[-1] + fibonacci_numbers[-2] <= limit:
fibonacci_numbers.append(fibonacci_numbers[-1] + fibonacci_numbers[-2])
return fibonacci_numbers
这是一个递归实现,计算量很大,填充列表并返回列表似乎比必要的更冗长。以下是使用生成器的函数的改进版本:
# use generators instead
from typing import Generator
def generate_fibonacci_numbers(limit: int) -> Generator[int, None, None]:
a, b = 0, 1
while a <= limit:
yield a
a, b = b, a + b在这种情况下,该函数返回一个生成器对象,然后您可以循环遍历该对象以获取序列的元素:
limit = 100
fibonacci_numbers_generator = generate_fibonacci_numbers(limit)
for num in fibonacci_numbers_generator:
print(num)
输出如下:
0
1
1
2
3
5
8
13
21
34
55
89
如您所见,使用生成器可以提高效率,尤其是对于较大的输入大小。此外,您可以将多个生成器链接在一起,这样您就可以使用生成器创建高效的数据处理管道。
相关推荐
- windows补丁怎么更新(windows 补丁更新)
-
windowsserver系统补丁升级的方法,1.打开WindowsServer系统运行对话框;2.在对话框中输入“control”;3.系统控制面板窗口自动打开;4.点击“Systemand...
- 电脑打不开了怎么重装系统(电脑打不开怎么重装系统不用u盘)
-
1、在可用电脑上制作好U盘启动盘,将下载的电脑系统iso文件直接复制到U盘的GHO目录下;2、在开不了机的电脑上插入U盘,重启后不停按F12或F11或Esc等快捷键打开启动菜单,选择U盘选项回车,比如...
- 一键清理垃圾下载(一键清除垃圾软件下载)
-
手机弹出广告是因为手机上的软件自动推送广告,可以在手机设置里关闭应用的消息通知,方法如下:1、找到手机设置,点击进入2、找到应用和通知,点击进入3、点击通知管理,点击进入4、我们可以看到自己开启消息通...
- ghost下载中文版官网(ghost8.0下载)
-
如果你下载的ghostwin7文件如果是用于系统安装。是不是映像文件要符合以下要求:1,压缩包完好无损。2,减压后的映像文件后缀名为ISO或者GHO.3,文件要在硬盘根目录才便于识别。4,映像文件后缀...
- win10最新版本是多少2025(win10最新版本是20h2吗)
-
1、打开软件,选择需要安装的win10系统。(4g以上内存选择64位系统)2、接着我们耐心等待下载重装资源。3、资源下载完成后,等待环境部署完毕重启即可。4、进入到pe系统后,打开小白工具,选择安装的...
- u盘显示被写保护怎么处理
-
U盘被写保护可以通过以下方法解除:格式化U盘:检查U盘上是否有写保护按钮,如果有,将其拨下,然后对U盘进行格式化即可。分区格式为exFat异常:这时需要Win+R打开窗口,输入cmd并点击确定,然后在...
- 电脑硬盘坏了恢复数据成功率高吗
-
1.不能全部恢复。因为电脑硬盘数据丢失可能是硬件故障、病毒攻击、人为误操作等原因造成,而不同的原因造成的数据丢失程度不同,可恢复的数据也有所不同。2.但也有可能可以全部恢复。如果是硬件故障引起的数...
- 移动硬盘怎么分区合并(移动硬盘分区合并最简单三个步骤)
-
1、按下组合键“win+R”打开运行窗口。2、在其中的输入框中输入“diskmgmt.msc”,再点击“确定”。3、在弹出的窗口中就可以看到要合并的磁盘了。4、在磁盘上单击鼠标右键。在弹出的选项框中点...
- 电脑bios有什么用(电脑bios能干什么)
-
电脑BIOS(基本输入输出系统)是计算机启动时运行的固件,它负责初始化计算机硬件,并提供操作系统加载所需的基本功能。BIOS主要功能包括:检测和配置硬件设备、加载操作系统、管理电源和温度、提供系统启动...
- 华硕电脑蓝屏怎么修复(华硕蓝屏怎么办)
-
华硕电脑蓝屏恢复的方法如下:安全模式进入系统。重启电脑后,连续按下F8键直至出现启动选项界面,选择安全模式进入系统,若此时能够正常运行,说明问题可能是由于软件冲突引起的。检查驱动程序兼容性。过于陈旧或...
- win10教育版怎么改成专业版(win10最稳定三个版本)
-
一、首先,点击Windows10“开始”菜单,找到电脑应用列表,二、然后,在应用列表中找到“Windows系统”文件夹中找到“命令提示符”,点击打开。三、然后,系统跳转到“命令提示符”窗口。四、然后,...
- ps下载官网(ps官网免费下载)
-
要从Adobe官网下载AdobePhotoshop(PS),可以按照以下步骤进行:1.打开网页浏览器,进入Adobe官网的主页。网址是:https://www.adobe.com。2.在网页的顶...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
python入门到脱坑 输入与输出—str()函数
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
宝塔面板如何添加免费waf防火墙?(宝塔面板开启https)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
慕ke 前端工程师2024「完整」
-
失业程序员复习python笔记——条件与循环
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
