在Python中“准确而优雅”的声明函数(一)

点击关注我哦

一篇文章带你了解函数声明时的优雅操作

函数是所有程序的关键组成部分,正确的函数是一种编写可读且可维护的代码的实用方法。但是未正确声明函数,则将把代码将变得难以阅读,并且长期可维护性很低。

鉴于函数在编程中的重要性,本文试图指出一些Python程序员在声明函数时可能犯的最常见错误。通过了解这些陷阱,我们可以实施相应的最佳做法,这些最佳做法不仅可以提高代码的可读性,还可以使其更易于维护。

1. 函数命名不正确

给函数起名是一名程序员整个职业生涯中的一项艰巨任务。(也有一些人认为Python命名是一个非常困难的事情)这个挑战来自于我们对于函数名称的标准,它应该是唯一的,信息丰富的和一致的。

独特

这是非常简单的要求。 与任何Python对象一样,我们使用名称来标识函数。 如果我们用相同的名称声明函数,则您的Python IDE(集成开发环境,例如PyCharm,Visual Studio Code)将抱怨,或者后来者成为赢家。在下面示例中我们声明了两个名为say_hello的函数,正如您所看到的,当我们调用该函数时,我们声明的第二个函数被调用了:

>>> def say_hello():
...     print('say hello, first function')
...
>>> def say_hello():
...     print('say hello, second function')
... 
>>> say_hello()
say hello, second function

内容丰富

编写函数以执行某些定义的操作,因此函数的名称应反映其功能。如果这些名称不能明确告知我们这些功能,我们将很难理解别人的程序或我们上个月编写的代码。提供信息意味着对功能的预期目的是特定且准确的。请考虑以下示例:

>>> def do_something():
...     print("do something")
... 
>>> def say_hi():
...     print("say hi")
... 
>>> def process_numbers(number1, number2):
...     result = number1 * number2
...     return result
... 
>>> def multiply_numbers(number1, number2):
...     result = number1 * number2
...     return result

保持一致性

Python编程鼓励模态,这意味着我们要在某些模块中对相关的类和函数进行分组。 在模块内部以及模块之间,需要保持一致地命名函数。 在一致性方面,我们的意思是对特定种类的对象和操作使用相同的约定。 考虑以下简单的示例。 前三个功能都使用两个数字执行类似的操作,因此我使用相同的格式:动词+下划线+数字。 在自定义类Mask中,两个功能promotion_price和sale_price具有相似的名称结构,第一部分定义价格的类型,第二部分指示返回值的性质(即以浮点数表示的价格) 。

>>> def multiply_numbers(number1, number2):
...     return number1 * number2
... 
>>> def add_numbers(number1, number2):
...     return number1 + number2
... 
>>> def divide_numbers(number1, number2):
...     return number1 / number2
... 
>>> class Mask:
...     def __init__(self, price):
...         self.price = price
...
...     def promotion_price(self):
...         return self.price * 0.9
...
...     def sales_price(self):
...         return self.price * 0.75

2. 功能混合和长度过长

另一个常见的错误是特定功能承担了太多的混合功能-即使某些高级程序员,如果他们不连续重构程序,有时也会犯一个错误。就给定功能的功能而言,最佳实践是该功能仅具有一个定义明确的功能,可以通过其明智的名称轻松体现该功能。

具有混合功能的功能的另一个常见症状是它们往往过长,因此,如果出现任何错误,则很难理解功能并进行调试。让我们考虑以下假设示例。我们使用流行的熊猫图书馆处理我们在实验中收集的生理数据。对于每个主题,我们都有CSV格式的四个数据会话。我们可以编写一个名为process_physio_data的函数,其中包含所有三个数据处理步骤。但是,由于数据的复杂性,该功能将超过100行代码。

>>> import pandas as pd
>>> 
>>> def process_physio_data(subject_id):
...     # first step, read the related files
...     df0 = pd.read_csv(f'{subject_id}_v1.csv')
...     df1 = pd.read_csv(f'{subject_id}_v2.csv')
...     df2 = pd.read_csv(f'{subject_id}_v3.csv')
...     df3 = pd.read_csv(f'{subject_id}_v4.csv')
...     # the end of first step
...
...     # second, some clean up procedures
...     # 
...     # process these four DataFrames
...     # 50 lines of code here
...     # generate a big DataFrame
...     #
...     # the end of the second step
...     big_df = pd.DataFrame()
...
...     # third, some complex calculations
...     #
...     # process the big DataFrames
...     # 50 lines of code here
...     # generate a small DataFrame
...     #
...     # the end of the third step
...     small_df = pd.DataFrame()
...
...     return small_df

除了编写此冗长的函数,我们可以编写以下函数,以更好地显示处理这些数据所涉及的步骤。如下面的代码片段所示,我们创建了三个助手函数,分别负责这三个步骤。值得注意的是,这些功能中的每一个都只具有一项功能。更新的process_physio_data函数更薄更清晰,它的唯一功能是提供一个管道来处理生理数据。通过这些作业的重构,代码的整体可读性大大提高。

>>> import pandas as pd
>>> 
>>> # the helper function that reads the data
>>> def read_physio_data(subject_id):
...     df0 = pd.read_csv(f'{subject_id}_v1.csv')
...     df1 = pd.read_csv(f'{subject_id}_v2.csv')
...     df2 = pd.read_csv(f'{subject_id}_v3.csv')
...     df3 = pd.read_csv(f'{subject_id}_v4.csv')
...     return [df0, df1, df2, df3]
... 
>>> # the helper function that cleans up the data
>>> def clean_physio_data(dfs):
...     # all the 50 lines of code for data clean up
...     big_df = pd.DataFrame()
...     return big_df
... 
>>> # the helper function that calculates the data
>>> def calculate_physio_data(df):
...     # all the 50 lines of code for data calculation
...     small_df = pd.DataFrame()
...     return small_df
...
>>> # updated function
>>> def process_physio_data(subject_id):
...     # first step, reading
...     dfs = read_physio_data(subject_id)
...     # second step, cleaning
...     big_df = clean_physio_data(dfs)
...     # third step, calculation
...     small_df = calculate_physio_data(big_df)
...     
...     return small_df

3. 没有说明文档

这是程序员必须长期学习的一个普遍错误。从表面上看,即使您没有任何文档,您的代码仍然可以按预期运行,这看起来非常好。例如,当您在几个星期内连续进行单个项目时,您会确切地知道每个功能的用途。但是,当您需要重新访问代码以更新某些功能时,需要花多少时间弄清楚自己做了什么?我通过艰苦的方式学习了该课程,您可能也有过类似的经历。

在人们共享API的团队合作环境中或在制作开源库时,缺少文档可能是一个更大的问题。当我们使用其他人的功能(尤其是复杂的功能)时,我们不知道这些功能的具体操作。但是,我们只需要阅读文档以了解如何调用该函数以及期望的返回值是多少。您能想象您的工作所依赖的库或框架中是否没有任何文档?

我并不是说您应该为函数编写大量的文档字符串。我相信,如果您遵循命名标准以使函数名称保持唯一,信息丰富且一致,并且每个函数仅执行一项功能且具有适当的长度,则无需为函数编写过多的文档。但是,如果您是公司或开放源代码社区中的大团队的成员,则必须实现标准的文档约定,以便您自己或他人受益。

·  END  ·

HAPPY LIFE

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值