Python编程精进:关于函数设计

233 阅读3分钟

最近看到这段代码,你觉得有没有问题呢?

def process_data(data, flag1, flag2, flag3):
    if flag1:
        data = [item for item in data if item.isdigit()]
    if flag2:
        data = [item.upper() for item in data]
    if flag3:
        data = sorted(data)
    result = "Processed: " + ", ".join(data)
    return result

乍一看,这段代码似乎并不太糟糕,对吧?但我却不这么人为。

1. 太多标志

它使用了太多标志(flag1flag2flag3)来控制行为,这使得函数有点令人困惑。

每次我们调用函数时,都得弄清楚哪种标志组合会产生期望的结果。

让我用这个例子来解释:

res = process_data(my_data, True, False, True)

你明白True, False, True是什么意思吗?上面的函数也是如此。必须细看逻辑,再三调用调试之后才敢调用。

2. 缺乏单一职责

这个函数试图同时做很多事情,比如过滤数字、将文本转换为大写、排序数据以及将数据连接成字符串

我们可以为每个任务编写单独的函数。因为单一职责的函数更容易阅读、测试和重用。

3. 缺乏类型注解

在这个函数中,没有任何解释。数据是什么类型?是列表、字符串还是字典?

我们得猜。

4. 扩展困难

例如,如果我们想添加另一个操作,比如修剪空白或任何其他操作,那么我们就得添加另一个标志。

这会使函数变得更加混乱。

如何优化

我尝试以清晰简单的方式重写这个函数。

1. 使用描述性的函数名

而不是为每个任务使用标志,我创建了单独的函数,如下所示:

def filter_digits(data):
    return [item for item in data if item.isdigit()]

def to_uppercase(data):
    return [item.upper() for item in data]

def sort_data(data):
    return sorted(data)

def format_result(data):
    return "Processed: " + ", ".join(data)

现在每个函数只做一件事。

2. 创建一个管道

然后,我尝试将这些函数连接在一起,采用类似管道的方法。这使得它更加清晰。

def process_data_pipeline(data):
    data = filter_digits(data)
    data = to_uppercase(data)
    data = sort_data(data)
    return format_result(data)

3. 使其更加灵活

我们可以使用一个函数列表来动态应用。如果我们要跳过某些步骤,这将非常有用。

def process_data(data, steps):
    for step in steps:
        data = step(data)
    return data

steps = [filter_digits, to_uppercase, sort_data, format_result]
result = process_data(my_data, steps)

现在我们完全控制了步骤。我们可以包含或排除步骤,而无需使用标志。

4. 添加类型注解

我只是添加了类型注解,使代码更易读,减少错误。

from typing import List, Callable

def process_data(data: List[str], steps: List[Callable[[List[str]], List[str]]]) -> str:
    for step in steps:
        data = step(data)
    return data

为什么这么改

  • 每个函数都应该只有一个明确的目的。以增加函数的可读性。

  • 如果我们想在其他地方过滤数字,我们只需调用filter_digits。这有助于函数的可重用性。

  • 如果我们想测试这个函数,现在更容易了。测试单个函数比测试重载函数更容易。

  • 现在,它的可扩展性提高了。如果我们想添加新步骤,我们可以简单地编写一个新函数并将其附加到列表中。

总结

所以,下次你写函数时,问问自己这些问题:

它是不是试图做太多事情?如果别人要理解这段代码,他是否不需要详细的解释就能理解?

如果答案是**“不”**,那么你需要重构这段代码。

你是否也有类似心得,可以说来听听。