百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

5个简单而有效的Python技巧,用于编写更好、更可维护的函数

off999 2024-10-23 12:40 34 浏览 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

如您所见,使用生成器可以提高效率,尤其是对于较大的输入大小。此外,您可以将多个生成器链接在一起,这样您就可以使用生成器创建高效的数据处理管道。

相关推荐

hdd硬盘和ssd(ssd硬盘和hdd硬盘是什么意思)

HDD硬盘和SSD硬盘是两种不同类型的电脑存储设备,它们有着以下区别:1.工作原理:HDD硬盘使用机械旋转的磁盘和读写磁头来存储和读取数据,而SSD硬盘则使用闪存存储数据,类似于USB闪存盘。2....

电脑免费软件下载大全(电脑上免费的下载软件)

正常情况下,如果我们想要在自己的电脑上面下载一个不要钱的单机游戏,那么我们是可以直接在我们的软件管理中心进行一个下载的,这个时候我们只需要通过一个权限就能够正常的下载,当然我们也是可以在一些小游戏的软...

mpp文件转换excel(mpp转换成pdf)

要将Excel表格转换为MPP格式,您可以按照以下步骤操作:1.打开Excel表格并确保数据按照项目的不同阶段或任务进行组织。2.将Excel表格中的数据复制到一个新的MicrosoftProj...

win7旗舰版开机密码忘记按f2

方法如下:开始-控制面板-用户帐户;在打开的更改用户帐户界面点击要更改的帐户;然后点击帐户左面的更改密码按钮;在打开的页面上,输入一次当前使用的密码,输入2次要更改的新密码然后保存退出就可以了...

笔记本无音频输出设备(笔记本无音频输出设备)

1、没有声卡驱动,解决方法就是找到笔记本的官网,下载电脑声卡的驱动安装即可。2、没有外界的音频播放设备,解决方法就是买一个外界的音频播放设备插到电脑主机的音频接口上即可。笔记本电脑显示未安装任何音频输...

iso文件能用手机打开吗(iso文件能用手机打开吗安全吗)

一般的压缩软件就可以打开的,比如,好压软件,这个打开只是解压形式的,如果你说的是运行iso文件,这个没有,况且安卓系统也不支持iso运行ISO文件一般用于光盘镜像文件的存储,如果想要在手机上运行ISO...

win7系统卡顿怎么优化(win7很慢很卡怎么优化)

1、首先打开安全卫士,进入安全卫士首页,单击软件窗口右下角的“更多”图标,打开扩展应用程序。2、单击选择“我的工具”。3、在我的工具菜单里面找到“人工服务”单击打开人工服务。4、在人工服务对话框有很多...

如何查看c盘微信聊天记录(如何查看c盘微信聊天记录内存大小)

微信群中的消息只要没删除基本都能保存,想要找微信群中几个多月前的消息可以直接根据日期来查找聊天记录。操作如下:1、打开想要查找记录的微信群,点击右上角人形图标;2、点击查找聊天内容;3、选择按日...

office2016家庭版激活密钥(office家庭版激活码2019)

走淘宝吧,因为零售版的密钥只能用一次。大概几块钱就能激活2016。如果你不在乎钱的话可以向我一样,订阅一个office365.实在不行可以和几个人一起买一个家庭版的365.出现这个情况,找微软申诉是没...

移动硬盘驱动器下载安装(移动硬盘驱动器下载安装教程)

1、右键单击您的桌面,选择“新建文件夹”,并命名该文件夹(例如“usb驱动程序”);2、然后到本站下载驱动程序;3、将其解压缩至在您的桌面上刚刚创建的usb驱动程序文件夹;4、单击开始菜单,然后选择设...

电脑硬盘格式化工具(电脑 格式化硬盘)

硬盘格式化工具很多,PQMACGIG8.0(中文就叫硬盘分区魔法师)是比较好的一个,这个是在WINDOWS下比叫好用,(个人感觉)FDISK也是比较好的一个,这个一般用在DOS下分区格式化WIN...

photoshop是一款什么软件(ps指的是什么软件)

这个说法是错误的,ps软件“即:photoshop”是由美国著名的“adobe阿多比”公司出品的专业的图像处理软件,它不是由微软公司出品的软件。众所周知的是,微软公司以设计视窗操作系统名满全球,它出...

ipad越狱的好处与坏处(ipad越狱好不好)

  好处一:  1、重命名、重组应用程序  如果你看着Sparrow(iOS最优秀邮件客户端)这个名字不爽,越狱之后就可以改成“Email”,如果你觉得“豆瓣电台”这个名字不给力,那就改成“中央人民广...

win7光盘重装系统步骤图解(win7光盘如何重装系统)

1.确认您的电脑支持从光盘启动。如果支持,可以直接将Windows7安装光盘插入电脑的光驱中。 2.打开电脑,按下F2、F10、F12或Delete等键进入BIOS设置界面。 ...

电脑已联网却无法上网(电脑已经联网了但是不能上网)

电脑连上网后,仍可能存在无法上网的情况,这可能是由多种原因造成的。以下是一些可能的原因和解决方法:1.浏览器问题:有时候,浏览器可能会出现故障,导致无法正常访问网络。您可以尝试清除浏览器的缓存和co...

取消回复欢迎 发表评论: