传递列表

您经常会发现将列表传递给函数很有用,无论它是名称列表、数字列表还是更复杂的对象(例如字典)。 当您将列表传递给函数时,该函数可以直接访问列表的内容。 让我们使用函数来更有效地处理列表。

假设我们有一个用户列表,想要向每个用户打印问候语。 以下示例将姓名列表发送到名为 greet_users() 的函数,该函数会分别问候列表中的每个人:

def greet_users(names):
    """Print a simple greeting to each user in the list."""
    for name in names:
        msg = f"Hello, {name.title()}!"
        print(msg)

usernames = ['hannah', 'ty', 'margot']
greet_users(usernames)

我们定义 greet_users() 所以它需要一个名称列表,并将其分配给参数名称。 该函数循环遍历它收到的列表并向每个用户打印问候语。 在函数之外,我们定义了一个用户列表,然后在函数调用中将用户名列表传递给 greet_users():

Hello, Hannah!
Hello, Ty!
Hello, Margot!

这是我们想要的输出。 每个用户都会看到个性化的问候语,您可以随时调用该函数来问候一组特定的用户。

在函数中修改列表

当您将列表传递给函数时,该函数可以修改列表。 对函数体内的列表所做的任何更改都是永久性的,即使在处理大量数据时也能让您高效地工作。 考虑一家创建用户提交的设计的 3D 打印模型的公司。 需要打印的设计被存储在一个列表中,打印后它们被移动到一个单独的列表中。 以下代码在不使用函数的情况下执行此操作:

# Start with some designs that need to be printed.
unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron']
completed_models = []

# Simulate printing each design, until none are left.
#  Move each design to completed_models after printing.
while unprinted_designs:
    current_design = unprinted_designs.pop()
    print(f"Printing model: {current_design}")
    completed_models.append(current_design)

# Display all completed models.
print("\nThe following models have been printed:")
for completed_model in completed_models:
    print(completed_model)

该程序以需要打印的设计列表和一个名为 completed_models 的空列表开始,每个设计在打印后将移至该列表。 只要设计保留在 unprinted_designs 中,while 循环就会模拟打印每个设计,方法是从列表末尾移除一个设计,将其存储在 current_design 中,并显示当前设计正在打印的消息。 然后它将设计添加到已完成模型的列表中。 当循环完成运行时,将显示已打印的设计列表:

Printing model: dodecahedron
Printing model: robot pendant
Printing model: phone case

The following models have been printed:
dodecahedron
robot pendant
phone case

我们可以通过编写两个函数来重组这段代码,每个函数执行一项特定的工作。 大多数代码不会改变; 我们只是在更仔细地构建它。 第一个函数将处理打印设计,第二个函数将总结已完成的打印:

def print_models(unprinted_designs, completed_models): (1)
    """
    Simulate printing each design, until none are left.
    Move each design to completed_models after printing.
    """
    while unprinted_designs:
        current_design = unprinted_designs.pop()
        print(f"Printing model: {current_design}")
        completed_models.append(current_design)

def show_completed_models(completed_models): (2)
    """Show all the models that were printed."""
    print("\nThe following models have been printed:")
    for completed_model in completed_models:
        print(completed_model)

unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron']
completed_models = []

print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)
1 我们使用两个参数定义函数 print_models():需要打印的设计列表和已完成模型列表。 鉴于这两个列表,该函数通过清空未打印设计列表并填充已完成模型列表来模拟打印每个设计。
2 然后,我们使用一个参数定义函数 show_completed_models():已完成模型的列表。 鉴于此列表,show_completed_models() 显示打印的每个模型的名称。

该程序与没有函数的版本具有相同的输出,但代码更有条理。 完成大部分工作的代码已移至两个单独的函数,这使得程序的主要部分更易于理解。 查看程序的主体并注意您可以轻松地了解正在发生的事情:

unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron']
completed_models = []

print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)

我们设置了一个未打印设计列表和一个将包含已完成模型的空列表。 然后,因为我们已经定义了两个函数,所以我们所要做的就是调用它们并向它们传递正确的参数。 我们调用 print_models() 并将它需要的两个列表传递给它; 正如预期的那样,print_models() 模拟打印设计。 然后我们调用 show_completed_models() 并将已完成模型的列表传递给它,以便它可以报告已打印的模型。 描述性的函数名称允许其他人阅读并理解这段代码,即使没有注释。

该程序比没有功能的版本更易于扩展和维护。 如果稍后需要打印更多设计,只需再次调用 print_models() 即可。 如果我们意识到打印代码需要修改,我们可以更改代码一次,我们的更改将在函数调用的任何地方发生。 这种技术比必须在程序的几个地方分别更新代码更有效。

这个例子还展示了每个函数都应该有一个特定工作的想法。 第一个函数打印每个设计,第二个函数显示完成的模型。 这比使用一个函数来完成两个工作更有益。 如果您正在编写一个函数并注意到该函数执行太多不同的任务,请尝试将代码拆分为两个函数。 请记住,您始终可以从另一个函数调用一个函数,这在将复杂任务拆分为一系列步骤时会很有帮助。

禁止函数修改列表

有时你会想要阻止一个函数修改一个列表。 例如,假设您从未打印设计的列表开始,并编写一个函数将它们移动到已完成模型的列表中,如上例所示。 您可能会决定,即使您已经打印了所有设计,您仍希望保留未打印设计的原始列表以供记录。 但是因为您将所有设计名称移出了 unprinted_designs,所以列表现在是空的,空列表是您拥有的唯一版本; 原来的不见了。 在这种情况下,您可以通过向函数传递列表的副本而不是原始列表来解决此问题。 该函数对列表所做的任何更改都只会影响副本,而不会影响原始列表。

您可以将列表的副本发送到这样的函数:

function_name(list_name[:])

切片符号 [:] 制作列表的副本以发送给函数。 如果我们不想清空 print_models.py 中的 unprinted_designs 列表,我们可以这样调用 print_models():

print_models(unprinted_designs[:], completed_models)

函数 print_models() 可以完成它的工作,因为它仍然接收所有未打印设计的名称。 但这次它使用原始未打印设计列表的副本,而不是实际的 unprinted_designs 列表。 列表 completed_models 将像以前一样填充打印模型的名称,但未打印设计的原始列表将不受该功能的影响。

即使您可以通过将列表的副本传递给您的函数来保留列表的内容,您也应该将原始列表传递给函数,除非您有特定的理由传递副本。 函数与现有列表一起工作效率更高,因为这避免了使用制作单独副本所需的时间和内存。 在处理大型列表时尤其如此。

自己试试
8-9.Messages

制作一个包含一系列短文本消息的列表。 将列表传递给名为 show_messages() 的函数,该函数打印每条文本消息。

8-10.Sending Messages

从练习 8-9 的程序副本开始。 编写一个名为 send_messages() 的函数来打印每条文本消息,并在打印时将每条消息移动到一个名为 sent_messages 的新列表中。 调用该函数后,打印两个列表以确保消息已正确移动。

8-11.Archived Messages

从练习 8-10 开始。 使用消息列表的副本调用函数 send_messages()。 调用该函数后,打印您的两个列表以显示原始列表已保留其消息。