文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

编写高效Python函数的五个技巧,真心建议你牢记,并当作此后编码的准绳!

2024-11-29 19:16

关注

以下是文章的整体概览:

首先,强调遵循单一职责原则,确保每个函数只执行一个任务。接下来,讨论了类型提示(type hints)为增强代码清晰度和长期可维护性带来的好处。

接着文章探讨了仅关键字参数(keyword-only)这一Python功能,它可以通过强制使用参数名称来最小化出错的几率。另一个建议是严格控制参数数量,仅使用函数功能所必要的参数,这样做可以减少复杂性和潜在的 bug。

最后,文章倡导使用生成器(Generator)这种内存高效利用的技术来返回可迭代数据,而不是一次性构造并返回整个列表。

无论你是新手还是经验丰富的开发者,都建议从现在开始应用这5个技巧,并在日常工作中不断实践,相信你可以在未来的编程生涯中编写出更高效、更具可读性和可维护性的函数,从而产生更高质量的代码,从而提高你的生产力。

1. 你的函数应该只做一件事

“你的函数应该只做一件事”原则是整洁代码和高效编程的核心原则。这一原则也被称为单一职责原则(Single Responsibility Principle, SRP),它的宗旨是建议一个函数应该只有一个职责或任务。这会让你的代码更易于阅读、测试、调试和维护。以下是应用此原则的一些优点:

假设你正在开发一个Python程序,处理一个数字列表,需要实现:

def filter_negative_numbers(numbers):
    """Filters out negative numbers from the list."""
    return [num for num in numbers if num >= 0]

def square_numbers(numbers):
    """Return a list of squared numbers."""
    return [num ** 2 for num in numbers]

def sum_numbers(numbers):
    """Return the sum of numbers."""
    return sum(numbers)

def data_processing(numbers):
    """Process the list of nubers: filter, square, and sum."""
    positive_numbers = filter_negative_numbers(numbers)
    squared_numbers = square_numbers(positive_numbers)
    total = sum_numbers(squared_numbers)
    return total

if __name__ == '__main__':
    numbers = [-2, -3, 4, -1, -2, 1, 5, -3]
    result = data_processing(numbers)
    print(result)   # Output: 42

2. 增加类型提示以提高可读性和可维护性

类型提示(Type hints)是Python中一项非常棒的功能,允许你指定变量、函数参数和返回值的预期类型。该特征在 PEP 484 中引入,类型提示不会影响程序运行时的行为,而是提供了一种对代码执行静态类型检查的方式。它们提高了代码的可读性,并帮助开发人员理解预期的输入和输出类型,使代码更易于维护。添加类型提示可以改善以下几点:

在单一职责原则中我们提供了一个没有类型提示的示例,接下来我们将提供一个具有类型提示的版本,功能一样:

from typing import List

def filter_positive_numbers(numbers: List[int]) -> List[int]:
    """Filters out negative numbers from the list."""
    return [num for num in numbers if num >= 0]

def square_positive_numbers(numbers: List[int]) -> List[int]:
    """Return a list of squared numbers."""
    return [num ** 2 for num in numbers]

def sum_numbers(numbers: List[int]) -> int:
    """Return the sum of numbers."""
    return sum(numbers)

def data_processing(numbers: List[int]) -> int:
    """Process the list of nubers: filter, square, and sum."""
    positive_numbers = filter_positive_numbers(numbers)
    squared_numbers = square_positive_numbers(positive_numbers)
    total = sum_numbers(squared_numbers)
    return total

if __name__ == '__main__':
    numbers = [-2, -3, 4, -1, -2, 1, 5, -3]
    result = data_processing(numbers)
    print(result)   # Output: 42

比较这两段代码,我们可以发现:

没有类型提示:阅读函数定义时,不太清楚期望什么类型的参数或函数返回什么类型。

有类型提示:函数签名明确指出它们使用整数列表,并返回整数列表或单个整数。

3. 强制使用仅关键字参数以最小化出错几率

强制使用“仅关键字参数”(keyword-only)是Python中的一种技术,该技术表明,调用函数时某些参数必须通过名称指定。

这是通过在函数定义中使用特殊语法来完成的,该语法可以防止这些参数以位置方式传递。这种方法可以显著提高代码的清晰度并减少错误。

在Python函数中强制使用“仅关键字参数”可以大大增强代码的清晰度和正确性。关键字参数只能使用参数名称指定。这种强制执行有助于:

下面是一个邮件发送的示例。send_email 函数接受一个可选的 cc 字符串:

def send_email(recipient: str, subject: str, body: str, cc: str = None):
    print(f"Sending email to {recipient}...")
    print(f"Subject: {subject}")
    print(f"Body: {body}")
    if cc:
        print(f"CC: {cc}")
        
if __name__ == '__main__':
    send_email('jackzhang@example.com', '编写高效Python函数的5个技巧', 
               '本文将向大家分享5个让你编写高效Python函数的技巧...', 'cc@example.com')

假设您想将可选的 cc 参数设置为关键字参数。可以这样做:

# Make the optional 'cc' argument keyword-only
def send_email(recipient: str, subject: str, body: str, *, cc: str = None):
    print(f"Sending email to {recipient}...")
    print(f"Subject: {subject}")
    print(f"Body: {body}")
    if cc:
        print(f"CC: {cc}")
        
if __name__ == '__main__':
    send_email('jackzhang@example.com', '编写高效Python函数的5个技巧', 
               '本文将向大家分享5个让你编写高效Python函数的技巧...', cc='cc@example.com')

只需要在可选参数 cc 前使用 * 号将前后隔开即可将其后的参数变成仅关键字参数。如果你想了解更多关于关键字参数和位置参数的细节,请阅读我的另一篇文章(Python效率秘籍:使用“*” 和“/” 让你的函数参数看起来更整洁)。

现在我们再次调用函数时可选参数 cc 就必须用定义时的名称进行值传递:

send_email('jackzhang@example.com', '编写高效Python函数的5个技巧', 
           '本文将向大家分享5个让你编写高效Python函数的技巧...', cc='cc@example.com')
Sending email to jackzhang@example.com...
Subject: 编写高效Python函数的5个技巧
Body: 本文将向大家分享5个让你编写高效Python函数的技巧...
CC: cc@example.com

如果我们尝试像之前一样所有参数都采用位置参数传递:

send_email('jackzhang@example.com', '编写高效Python函数的5个技巧', 
           '本文将向大家分享5个让你编写高效Python函数的技巧...', 'cc@example.com')

你将会收到下面这样的报错信息:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[6], line 10
      7         print(f"CC: {cc}")
      9 if __name__ == '__main__':
---> 10     send_email('jackzhang@example.com', '编写高效Python函数的5个技巧', 
     11                '本文将向大家分享5个让你编写高效Python函数的技巧...', 'cc@example.com')

TypeError: send_email() takes 3 positional arguments but 4 were given

4. 你的函数应该只使用必要参数

在定义函数时,严格限制参数数量,将其限制为函数操作所必要的参数至关重要。不必要的参数会常常会导致混淆,使函数调用臃肿,并且会让维护变得更复杂。以下是你应该只使用必要参数的几个原因:

以下是一个冗余参数的函数示例:

# example function for processing an order including unnecessary arguments
def process_order(order_id: int, customer_id: int, customer_name: str,
                  amount: float, discount: float = 0.0):
    print(f"Processing order {order_id} for customer {customer_id} - {customer_name}")
    total_amount = amount * (1 - discount)
    print(f"Total amount after discount: {total_amount}")
    
if __name__ == '__main__':
    process_order(666, 888, 'Jack Zhang', 1000.0, 0.05)
Processing order 666 for customer 888 - Jack Zhang
Total amount after discount: 950.0

在这个例子中,函数 process_order 同时接受 customer_id 和 customer_name 参数,如果所有必需的信息都可以从 order_id 中获得,这两个参数可能是不必要的。

现在,让我们只使用必要的参数,重构该函数:

# example function for processing an order with only necessary arguments
def process_order(order_id: int, amount: float, discount: float = 0.0):
    print(f"Processing order {order_id}")
    total_amount = amount * (1 - discount)
    print(f"Total amount after discount: {total_amount}")
    
if __name__ == '__main__':
    process_order(666, 1000.0, 0.05)

5. 使用生成器返回列表

生成器(Generator)是Python中一种特殊的可迭代对象,它允许你逐个迭代序列值,而不需要一次性将整个序列存储在内存中。生成器通过函数和 yield 关键字进行定义,该关键字允许函数返回一个值并暂停其状态,在下次请求值时恢复。这使得生成器成为处理大型数据集或数据流的高效方式。

以下是你应该使用生成器的几个原因:

以下是一个不使用生成器返回列表的函数示例:

from typing import List

def get_squares(n: int) -> List[int]:
    """Return a list of squares from 0 to n-1."""
    squares = []
    for i in range(n):
        squares.append(i * i)
    return squares

if __name__ == '__main__':
    squares_list = get_squares(10)
    print(squares_list) 
    # Output: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

在这个例子中,函数 get_squares 在返回之前会生成并存储所有数值(0~n-1)的平方数。现在,我们改用生成器实现相同的功能:

from typing import List

def get_squares(n: int) -> List[int]:
    """Yield squares from 0 to n-1."""
    for i in range(n):
        yield i * i
        
if __name__ == '__main__':
    squares_gen = get_squares(10)
    print(list(squares_gen))

在Python中使用生成器返回列表可以在内存效率、性能和惰性求值方面带来显著优势。通过按需生成值,你可以更高效地处理大型数据集,编写更简洁、更易维护的代码。

两种方法的比较清楚地显示了使用生成器的好处,特别是对于大型或计算密集型序列的优势尤其显著。

来源:数据派探险家内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯