Code Monkey home page Code Monkey logo

learn-ai-assisted-python-programming's People

Contributors

cssmagic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

learn-ai-assisted-python-programming's Issues

[译] [205] 我们的第一个编程练习

2.5 Our first programming problem

2.5 我们的第一个编程练习

The goal of this next section is twofold: 1) for you to see the workflow of interacting with Copilot and 2) for you to gain an appreciation of how powerful Copilot can be by seeing it solve a fairly complicated task fairly easily.

本节有两个主要目标:(1) 展现与 Copilot 交互的整个工作流程;(2) 通过观察 Copilot 如何轻松解决一个相当复杂的任务,让你感受到 Copilot 的强大能力。

In our next chapter, we’ll talk through the workflow with Copilot in more detail, but generally speaking, you’ll use the following steps when authoring code with Copilot:

在下一章中,我们将更深入地探讨与 Copilot 协作的工作流。但一般来说,当你借助 Copilot 编写代码时,大体遵从以下几步:

  1. Write a prompt to Copilot using comments (#) or docstrings (""").
  2. Let Copilot generate code for you.
  3. Check to see if the code is correct, by reading through it and by testing
    1. If it works, move to Step #1 for the next thing you’d like it to do.
    2. If it doesn’t work, delete the code from Copilot and go back to Step #1 and modify the prompt (and see suggestions in table 2.1).
  1. 通过注释(#)或文档字符串(""")向 Copilot 提供提示词。
  2. 让 Copilot 自动生成代码。
  3. 通过阅读和测试代码来确认其是否正确
    1. 如果代码运行正常,继续执行步骤 #1,进行下一个任务。
    2. 如果代码运行不正常,删除 Copilot 生成的代码,返回步骤 #1 并修改提示词(参见表 2.1 中的建议)。

Because you’ve just started working with Copilot, we’re wary of showing you such a large example, but we feel you’ll really value seeing how powerful Copilot can be now that you have it installed. As such, we want you to follow along as best as you can to get a feel for working with Copilot on your own, but if you get stuck, feel free to just read along and save working along with Copilot in VSCode for the next chapter. Later chapters will explain the process of working with Copilot in more detail.

由于你刚开始尝试 Copilot,我们对展示一个这么大的例子感到谨慎,但我们认为,让你见证 Copilot 的强大能力,特别是在你已经安装了它之后,将极具价值。**因此,我们希望你尽可能地跟着操作,以获得使用 Copilot 的实践体验,但如果你遇到难题,也可以选择仅仅阅读,把实践操作留到下一章进行。**后续章节将会详细解释如何与 Copilot 合作的过程。

Also, Copilot will generate a lot of code in this section and we don’t expect you to understand the code until much later in the book. We provide the code solely so you can see what Copilot gave us, but do NOT feel as though you need to try to understand the code in this chapter.

此外,本部分 Copilot 将生成许多代码,我们并不期待你在本书后面的内容中立刻理解这些代码。我们提供这些代码,仅仅是为了让你看到 Copilot 生成的内容,但不要认为你需要在本章理解这些代码。

To get started, let’s create a new file. If you aren’t already in VSCode, go ahead and start it. Then create a new Python file and save it as nfl_stats.

开始之前,请创建一个新文件。如果你目前还没有使用 VSCode,请先启动它。然后创建一个新的 Python 文件,并将其命名为 nfl_stats

Showcasing Copilot’s value in a data processing Task

展现 Copilot 在数据处理任务上的能力

We want to start with some basic data processing as this is something that many of you have likely done in your personal or professional lives. To find a dataset, we went to a great website called Kaggle [4] which has tons of datasets freely available for use. Many of them include important data like health statistics for different countries, or information to help track the spread of disease, and so on. We’re not going to use those because we’d like to have something lighter for our first program. Since both of us are American Football fans, we felt we should play with the NFL offensive stats database.

我们决定从一些基础的数据处理任务开始,这可能是许多人在个人或职业生活中已经尝试过的。在寻找合适的数据集时,我们使用了一个名为 Kaggle [4] 的优秀网站,该网站免费提供了丰富的数据集。这些数据集涵盖了众多领域,比如不同国家的健康统计数据或是疾病传播的追踪信息等。但我们选择不使用这类数据,因为我们希望我们的第一个程序项目更加轻松有趣。由于我们都是橄榄球的热忱粉丝,我们决定挑选 NFL 进攻数据统计库来进行我们的数据处理实践。

Let’s get started by downloading the dataset from:

我们先从以下链接下载数据集:

www.kaggle.com/datasets/dtrade84/nfl-offensive-stats-2019-2022

To download the dataset, you will have to sign up for a Kaggle account. If you don’t want to create the account, it’s okay to just read through this section without using VSCode and Copilot to generate the code yourself. Once downloaded, you may need to extract the zip file using the default zip extractor on your computer. Copy the dataset file from that zip file into your current folder in VSCode where you have your code (the folder you have open in Explorer). (If you are on a Mac and the file is saved as a .numbers file, you will need to use “File->Export To” to then save the file as a CSV in your current working directory.) That dataset has NFL information from 2019 to 2022.

为了下载数据集,你将需要注册一个 Kaggle 账户。如果你不希望注册账户,只是阅读这一部分而不亲手使用 VSCode 和 Copilot 生成代码也是可以的。下载完成后,你可能需要使用电脑自带的压缩文件解压缩工具来解压 zip 文件。从该 zip 文件中将数据集文件复制到你的 VSCode 当前文件夹,即你在资源管理器中打开的那个包含你代码的文件夹。(如果你是 Mac 用户,并且文件保存为 .numbers 格式,你将需要使用“文件->导出至”然后将文件作为 CSV 保存在你当前的工作目录。)该数据集含有 2019 到 2022 年的 NFL 信息。

Figure 2.4 The first few columns and rows of the nfl_offensive_stats.csv dataset.

图 2.4 nfl_offensive_stats.csv 数据集的前几行和前几列。

The nfl_offensive_stats.csv file is something known as a comma separated value text file (see figure 2.4 for a portion of the file). This is a pretty standard format for storing data. It has a header row at the top that explains what’s in every column. The way that we (or a computer) know the boundaries between columns is to use the commas between cells. Also notice that each row is placed on its own line.

nfl_offensive_stats.csv 文件是一种被称为逗号分隔值文本文件的东西(参见图 2.4 查看文件的一部分)。这是一种用于存储数据的非常标准的格式。它顶部有一个标题行,解释了每一列中的内容。我们(或计算机)知道列之间边界的方式是通过单元格之间的逗号。同时注意,每一行都放置在自己的行上。

Good news: Python has a bunch of tools for reading in CSV files.

好消息是 Python 有一大堆工具可以读取 CSV 文件。

Step 1: How many passing yards did Aaron Rodgers throw in 2019-2022

第 1 步:2019 至 2022 年,阿隆·罗杰斯共投掷了多少传球码数

Let’s start by exploring what is stored in this file. To preview what is in the file, you can look at the Kaggle webpage for these stats under “Detail”, you can open it in VSCode, or you can just open it up in spreadsheet software like Microsoft Excel. (If you open it with Excel, be sure not to save the file. We need to leave the file in a .csv format.) Whichever way you choose to open it, here’s the start of the header (top) row (also shown in figure 2.4):

我们首先探查这个文件包含了哪些数据。为了预览文件内容,你既可以在 Kaggle 网页上查看这些统计信息的“详细信息”,也可以在 VS Code 中打开它,或者直接在 Microsoft Excel 等电子表格软件中查看。(如果使用 Excel 打开,请务必不要保存文件,因为我们需要维持文件的 .csv 格式。)不论你采用何种方式打开它,这是表头(最顶端一行)的起始部分(也在图 2.4 中展示):

game_id,player_id,position ,player,team,pass_cmp,pass_att,pass_yds,...

There are a bunch more columns after these, but these have all we need to perform our first task. We know now that there’s a column for players and a column for passing yards. Aaron Rodgers is a player that gets passing yards in each game that he plays. But how many passing yards does he have in total, over all of the games that he played? This isn’t so easy to answer by directly looking at the file. So what we want the computer to do is to make this easier for us!

在这些之后,表中还有很多其他列,但这些信息已经足以让我们完成首个任务。我们现在知道,表中有一列是为了记录球员姓名,另一列记录的是传球码数。阿隆·罗杰斯在他参加的每一场比赛中都会有传球码数的增加。但是,在他参与的所有比赛中,他总共获得了多少传球码?直接从文件中找出答案并不简单。因此,我们需要计算机帮助我们简化这个问题的解答过程!

We want it to sum up all the passing yards (pass_yds) for rows (games) where Aaron Rodgers is the player. For now, we’re going to just ask for all the yards in the database even though it covers multiple seasons. We could change this later if we’d like. This problem might be a good problem to give to programmers learning to program in their 4th week of a standard college-level introductory programming course, but we have Copilot! So instead of learning how to write this code from scratch, we’re just going to ask Copilot to generate it for us. To make that happen, we are going to be quite specific in our request to make sure Copilot knows what we are asking for.

我们期望它能统计所有行(即比赛)里,标记为阿隆·罗杰斯这名球员的传球码数(pass_yds)的总和。目前,我们打算查询数据库中记录的所有传球码数,尽管这涉及多个赛季。未来如果我们想要,还可以对此进行调整。对于正处于标准大学水平入门编程课程第四周的编程学习者来说,这可能是一个合适的问题,但我们有 Copilot!因此,我们无需从头开始学习如何编写代码,而是直接让 Copilot 为我们完成代码生成。为了确保 Copilot 理解我们的需求,我们将在提出请求时保持极高的明确性。

Specifically, we’re only going to ask it to perform small amounts of work and then re-prompt it to perform the next step. Later we’ll discuss how to write good prompts, but for now, just go ahead and use what we’ve written below by placing this text at the top of your new file:

具体而言,我们会要求它完成一小部分工作,然后再提示它进行下一步操作。我们稍后会探讨如何撰写有效的提示词,但目前,你只需要将以下文本复制并粘贴到新文件的开头即可:

"""
open the csv file called "nfl_offensive_stats.csv" and read in
the csv data from the file
"""
"""
打开名为 `nfl_offensive_stats.csv` 的 csv 文件,并从文件中读取 csv 数据
"""

The """ at the top and the bottom are surrounding something called a docstring. Docstrings are an alternative way of commenting (similar to text starting with #). They are commonly used for describing functions (see Chapter 3 for details on functions), but we use them in this example to avoid Copilot continually generating comments (see the “Comments only” problem in table 2.1). Given this prompt, Copilot should start generating code. For us, it produced this block of code:

顶部和底部的 """ 符号围绕的是一种称为文档字符串(docstring)的东西。文档字符串是一种注释方式(类似于以 # 开始的文本)。它们常用于描述函数(函数的详情请见第三章),但在本例中,我们使用它们以避免 Copilot 持续生成注释(参见表 2.1 中的“仅注释”问题)。有了这样的提示,Copilot 就应该开始生成代码了。对我们而言,它生成了以下代码块:

import csv
with open('nfl_offensive_stats.csv', 'r') as f:
    reader = csv.reader(f)
    nfl_data = list(reader)

First, for the purpose of reading this book, we want to remind you that the prompt is displayed differently than what Copilot produces. This is intentional so you can tell what we wrote (and you should write) and what Copilot wrote.

首先,为了便于本书的阅读,我们想提醒你,提示词的展示形式与 Copilot 生成的内容有所不同。这是故意为之,以便你能清楚地区分出我们所编写的内容(及你应编写的内容)与 Copilot 生成的内容。

Second, the code produced by Copilot is quite reasonable. We don’t expect you to be able to understand the code at this point in the book, but you can likely see the name of the file we wanted opened and some code about opening the file and reading in the file. Later in the book, we’ll learn how to read through the code. For now, just keep following along.

其次,Copilot 生成的代码相当合理。我们并不期望你在目前阅读本书的阶段就能完全理解代码,但你可能已经看到了我们希望打开的文件名,以及一些有关打开和读取文件的代码。在本书的后续内容中,我们将引导你学习如何阅读这些代码。现阶段,请你继续按照书中的指引前进。

Now that we have the data from the file, we’re going to give it a new prompt to ask it to sum all the passing yards for Aaron Rodgers in this dataset.

现在我们已经从文件里提取出了数据,下一步我们会提出一个新的提示词,目的是让它计算数据集中 Aaron Rodgers 的全部传球码数总和。

Because the computer doesn’t know what football is or specifics like that Aaron Rodgers is a quarterback, our prompt is going to be quite specific. We’ll teach you how to write prompts like this over the course of the book. Here is the new prompt:

鉴于计算机不了解橄榄球或者具体到 Aaron Rodgers 是一名四分卫的信息,我们的提示词需要极为明确。在本书的学习过程中,我们将引导你学会如何撰写这样的提示词。以下是新的提示词:

"""
In the data we just read in, the fourth column is the player
and the 8th column is the passing yards. Get the sum of
yards from column 8 where the 4th column value is
"Aaron Rodgers"
"""
"""
在我们刚刚导入的数据里,第四列标示的是球员名称,而第八列是传球码数。
请求出在第四列值为“Aaron Rodgers”情况下,第八列码数的总和。
"""

Notice how we tell the computer which columns are for players, and which are for passing yards. That’s to tell the computer how to interpret the data. Also, notice how we say specifically that we only want to sum the yards in the case that the player’s name is Aaron Rodgers. Again, we’ll teach you how to write prompts like this as we move forward in the book. Given this prompt, Copilot then produced the code below.

请注意我们如何指示计算机哪些列是球员名称,哪些列是传球码数。这样做是为了让计算机知道如何对数据进行解析。此外,注意到我们特别强调只在球员名为 Aaron Rodgers 的情况下对码数进行求和。我们会在书中继续教你如何撰写这类提示词。根据这个提示词,Copilot 接着生成了下面的代码。

passing_yards = 0
for row in nfl_data:
   if row[3] == 'Aaron Rodgers':
       passing_yards += int(row[7])
print(passing_yards)

Reminder: Copilot is non-deterministic

Remember from Chapter 1 that Copilot is nondeterministic so what Copilot gives you may not match what it gives us. This is going to be a challenge for the rest of the book: what do you do if you get a wrong result when we get a right result? We’re actually fairly confident that Copilot will give you a correct answer here, but if you get a wrong answer from Copilot, go ahead and just read the remainder of this section rather than working along with Copilot in VSCode. We will absolutely give you all the tools you need to fix the code when Copilot gives you a wrong answer, but that skill will be taught over the remainder of the book, so we don’t want you to get stuck on this now. When we run this code (recall how to “Run Code” from figure 2.1), we get this result: 13852

提醒:Copilot 具有非确定性特性

回想一下第一章的内容,Copilot 是具有非确定性的,这意味着 Copilot 给你的输出可能与给我们的输出不一致。这将成为本书剩余部分的挑战之一:当我们获得正确结果而你得到错误结果时,你该怎么办?实际上,我们对 Copilot 在此给出正确答案充满信心,但如果你从 Copilot 那里得到错误答案,请直接阅读本节其余内容,而不是在 VS Code 中继续尝试。我们将确保你拥有修正 Copilot 错误答案所需的所有工具,但这些技能会在书的后续部分介绍,因此我们不希望你现在就遇到障碍。当我们运行这段代码时(回想一下如何从图 2.1 中 “运行代码”),我们得到的结果是:13852。

Which is the correct answer. (We double-checked the answer, but if you are familiar with football, you can likely use estimates to see if the figure seems reasonable. Quarterbacks throw for 3,000-5,500 yards per season, and this is three seasons worth of data, so 13,852 seems like the right ballpark for a high performing quarterback.) What’s particularly interesting is that we planned on giving Copilot a third prompt to ask it to print the result, but Copilot guessed that’s what we’d want to do so it did so on its own.

这便是正确的答案。(我们已对此答案进行了复核,但如果你对橄榄球略有认识,你可能通过估算判断这个数值是否合理。四分卫每赛季的传球码数一般在 3,000 至 5,500 码之间,而这是三个赛季的数据,因此对于一位表现卓越的四分卫而言,13,852 码似乎是在合理范围内。)尤其有趣的是,我们原计划给 Copilot 提出第三个提示词,请求其打印结果,但 Copilot 似乎已经猜到了我们的意图并自行执行了这一步。

What do we want you to take from this example (and the rest of the chapter)?

我们期望你从这个示例(及本章的其余内容)中学到什么?

  1. Copilot is a powerful tool. We didn’t write any code, but we were able to get Copilot to generate the code needed to perform a basic analysis of the data. For readers who have used spreadsheets, you can probably think of a way to do this using spreadsheet applications like Excel, but it likely wouldn’t be as easy as writing code like this. Even if you haven’t used spreadsheets before, you’ve got to admit that it’s amazing that writing some basic, human-readable prompts can produce correct code and output like this.

  2. Copilot 是一个功能强大的工具。 我们没有亲手编写过任何代码,却能够利用 Copilot 生成完成数据基本分析所需的代码。对于那些有使用电子表格经验的读者,你可能会考虑通过 Excel 等电子表格工具来做这件事,但很可能不会像编写这样的代码那样简便。即便你以前没有使用过电子表格,也必须认可,仅通过撰写一些基本、易于人类阅读的提示词,就能够产出正确的代码和输出,这非常令人赞叹。

  3. Breaking problems into small tasks is important. For this example, we tried writing this code with just a single large prompt (not shown) or by breaking it into two smaller tasks. The larger prompt was almost identical text, just as a single prompt. What we found was that Copilot would usually give us the right answer with the larger prompt but would sometimes make mistakes. This was especially true in the next example we’ll show you. However, breaking the problem into smaller tasks significantly increased the likelihood of Copilot generating the right code. We’ll see how to break down larger problems into smaller tasks throughout the remainder of this book as this is one of the most important skills you’ll need. In fact, the next chapter helps you start understanding what are reasonable tasks to give to Copilot.

  4. 将问题分解成小任务是很重要的。 在这个例子中,我们尝试了使用一个较大的单一提示词(未展示)来编写这段代码,或者将其分解成两个较小的任务。较大的提示词几乎与单个提示的文本完全相同。我们发现,使用较大的提示词时,Copilot 通常会给出正确的答案,但有时会犯错。这在我们将要展示的下一个例子中尤其明显。然而,将问题分解成较小的任务显著增加了 Copilot 生成正确代码的可能性。在本书的剩余部分,我们将看到如何将较大的问题分解成小任务,因为这是你将需要的最重要的技能之一。实际上,下一章将帮助你开始理解,哪些任务是适合交给 Copilot 的。

  5. We still need to understand code to some degree. This is true for several reasons. One is that writing good prompts requires you to have a basic understanding of what computers know and what they don’t. We can’t just give a prompt to Copilot that says, “Give me the number of passing yards for Aaron Rodgers.” Copilot likely wouldn’t be able to figure out where the data is stored, the format of the data, which columns correspond to players and passing yards, or that Aaron Rodgers is a player. We had to spell that out to Copilot for it to be successful. Another reason has to do with determining whether code from Copilot is reasonable. When the two of us read the response from Copilot, we know how to read code so we can determine if the code produced by Copilot is reasonable. You’ll need to be able to do this to some degree, which is why Chapters 4 and 5 are dedicated to reading code.

  6. 我们仍需要在一定程度上理解代码。 这背后有几个原因。首先,要写出好的提示词,你需要对计算机知道什么和不知道什么有一个基本的理解。我们不能只给 Copilot 一个提示说,“给我 Aaron Rodgers 的传球码数。” Copilot 可能不会明白数据存储在哪里,数据的格式,哪些列是指球员和传球码数,或者 Aaron Rodgers 是一位球员。我们需要明确指出这些信息,Copilot 才能成功执行。另一个原因是关于判断 Copilot 生成的代码是否合理。当我们阅读 Copilot 的反馈时,我们能够读懂代码,从而判断 Copilot 生成的代码是否合理。你也需要具备这种判断能力,这也是第四章和第五章专门用于学习如何阅读代码的原因。

  7. Testing is important. When programmers talk about testing, they’re referring to the practice of making sure that their code works correctly, even in possibly unexpected circumstances. We didn’t spend much time on this piece other than checking if Copilot’s answer is plausible using estimates on just one data set, but in general we’ll need to spend more time on testing as this is a critical part of the code writing process. It likely goes without saying, but errors in code range from embarrassing (if you tell your hard-core NFL fan friend the wrong number of passing yards for a player) to dangerous (if software in a car behaves incorrectly) to costly (if businesses make decisions on wrong analyses). Concerningly, even after you’ve learned how to read code, we have first-hand experience to tell you that even if the code looks correct, it might not be! To address this, we have to test every piece of code created by Copilot to ensure it does what it should. We’ll learn how to rigorously test Copilot’s code in later chapters.

  8. 测试极为重要。 当程序员提到测试时,他们指的是确保代码即便在可能出现的意外情况下也能正确执行的实践。我们在这一部分并没有花太多时间,仅通过对单一数据集进行估算来判断 Copilot 的答案是否合理,但总体而言,我们需要在测试上投入更多的时间,因为这是编码过程中至关重要的一环。虽然可能不需要特别强调,但代码错误的后果从尴尬(例如,向你那位狂热的 NFL 粉丝朋友报告错误的球员传球码数),到危险(如汽车软件行为不当),再到代价高昂(如企业基于错误的分析做出决策)。值得担忧的是,即使你已经学会了如何阅读代码,我们有直接的经验告诉你,即使代码看似正确,也可能并非如此!因此,我们必须对 Copilot 生成的每个代码段进行测试,以确保其按照预期工作。在后面的章节中,我们将学习如何对 Copilot 生成的代码进行严格的测试。

To showcase the power of Copilot, we’re going to continue this example. Please feel free to follow along writing the prompts and running the code in Copilot or just reading along.

为了充分展现 Copilot 的能力,我们接下来将继续这个示例。你可以选择跟随我们一起编写提示词并在 Copilot 中运行代码,或者只是进行阅读。

Step 2: How well did all the quarterbacks do over that time period?

第 2 步:在这段时间里,所有四分卫的整体表现如何?

Knowing how well Aaron Rodgers did is interesting. But a next and more meaningful step would be to compare his stats to other quarterbacks over that time period. We really only want to compare against other quarterbacks because they are the players whose job is to throw the ball. Sure, a running back might throw the ball once a season, and it’s often fun when they do, but it’s not really their job. To include only the quarterbacks, we need to go back to the data for a moment. The third column is for “position” and “QB” stands for Quarterback. As such, let’s delete all the code Copilot gave us (everything after the end of our initial comments) and start over. We won’t show each step like we did above, instead, here is the result of the entire interaction with Copilot with our prompts highlighted as separate from the code it gave us.

了解阿隆·罗杰斯(Aaron Rodgers)的表现固然有趣,但下一个更有意义的步骤将是将他的数据与同期其他四分卫进行比较。我们真正只想与其他四分卫比较,因为他们是负责传球的球员。当然,一个跑卫可能一个赛季传一次球,而且他们这么做时通常很有趣,但这并不真正是他们的工作。为了仅包括四分卫,我们需要重新查看一下数据。第三列是“位置”,“QB”代表四分卫。因此,让我们删除 Copilot 给我们的所有代码(即我们最初评论结束后的所有内容)并重新开始。我们不会像上面那样展示每个步骤,而是这里展示与 Copilot 互动的整个结果,我们的提示作为与其给出的代码分开的部分高亮显示。

Listing 2.1 Copilot’s code to analyze the top Quarterbacks

列表 2.1 Copilot 分析顶级四分卫的代码

"""
open the csv file called "nfl_offensive_stats.csv" and
read in the csv data from the file
"""
# import the csv module
import csv
# open the csv file
with open('nfl_offensive_stats.csv', 'r') as f:
    # read the csv data
    data = list(csv.reader(f))
"""
the 3rd column in data is player position, the fourth column
is the player, and the 8th column is the passing yards.
For each player whose position in column 3 is "QB",
determine the sum of yards from column 8
"""
# create a dictionary to hold the player name and passing yards
passing_yards = {}
# loop through the data
for row in data:
    # check if the player is a quarterback
    if row[2] == 'QB':
        # check if the player is already in the dictionary
        if row[3] in passing_yards:
            # add the passing yards to the existing value
            passing_yards[row[3]] += int(row[7])
        else:
            # add the player to the dictionary
            passing_yards[row[3]] = int(row[7])
"""
print the sum of the passing yards sorted by sum
of passing yards in descending order
"""
for player in sorted(passing_yards, key=passing_yards.get, reverse=True):
    print(player, passing_yards[player])

Notice that we gave Copilot three prompts. The first was to handle the input data, the second was to process the data, and the third was to output the response. This cycle of input data, process data, provide output is exceptionally common in programming tasks.

注意,我们向 Copilot 提出了三段提示词。第一段提示词针对输入数据的处理,第二段涉及数据的加工处理,而第三段则关于输出的响应。这种 “输入数据、加工数据、输出响应” 的流程在编程任务里非常典型。

Looking at the results from Copilot, we have to point out that we’ve taught programming for years and this is pretty impressive. We might ask students to solve something like this on a final exam in our college-level classes and we suspect less than half the class would do it correctly. Without diving into too many details, Copilot chose a good way of storing the data by using a dictionary (not a normal dictionary like an English dictionary, but a way of storing data in Python) which is a good choice here and used a clever way of sorting the data to help in displaying the results.

观察 Copilot 的输出结果,我们必须指出,身为多年编程教育者,这成果确实令人赞叹。在我们大学水平的课程中,我们可能会在期末考试上布置类似的题目,但我们估计不到半数的学生能正确答题。Copilot 选择了使用字典(这里说的字典不是像英语字典那种普通字典,而是一种在 Python 中用于数据存储的方式)来存储数据,这是个明智的选择;,并且它还巧妙地采用了排序方法来辅助结果的展示。

Thinking of the results, here are the first 5 lines from the output if you run the code:

我们来看看结果。如果执行代码,以下是输出的前五行内容:

Patrick Mahomes 16132
Tom Brady 15876
Aaron Rodgers 13852
Josh Allen 13758
Derek Carr 13271

If you follow football, these results should not be a surprise to you. Just to see how well Copilot can adapt to our wishes, let’s try to make a minor change.

如果你是橄榄球迷,这个结果对你而言不应该是什么意外。为了测试 Copilot 对我们需求的适应能力,我们来尝试做一个小修改。

Suppose that because Tom Brady is already recognized as one of the best QBs of all time, you would rather omit him from this comparison.

考虑到汤姆·布雷迪(Tom Brady)已被广泛认为是史上最伟大的四分卫之一,你可能更愿意在这次比较中忽略他。

To make this change, we’re just going to modify the prompt at the bottom. Go to the point in the code where it says:

为实现这个调整,我们将仅仅修改底部的提示。请找到代码中以下位置:

"""
print the sum of the passing yards sorted by sum
of passing yards in descending order
"""
for player in sorted(passing_yards, key=passing_yards.get, reverse=True):
    print(player, passing_yards[player])

Delete the code, just leaving the comment, and add another line to the docstring like this:

删除代码,仅留下注释,并在文档字符串中按如下方式添加另一行:

"""
print the sum of the passing yards sorted by sum
of passing yards in descending order
Do not include Tom Brady because he wins too much
"""
"""
打印传球码数的总和,按照传球码数的总和进行降序排序
不包括汤姆·布雷迪(Tom Brady),因为他获胜次数过多
"""

Copilot then suggested to us:

随后,Copilot 给我们的建议是:

for player in sorted(passing_yards, key=passing_yards.get, reverse=True):
    if player != "Tom Brady":
        print(player, passing_yards[player])

That’s exactly what we’d like to see changed in the code. (Thanks, Tom Brady, for being a good sport in this example.) The code excluded all data for Tom Brady at the point of printing the results. When we save the file and run it again, the first 5 lines are now:

这正是我们想在代码中进行的更改。(感谢汤姆·布雷迪(Tom Brady)在这个例子中展现出的良好体育精神。)代码在输出结果的环节中排除了所有关于汤姆·布雷迪的数据。当我们保存并重新运行文件时,现在的前五行如下:

Patrick Mahomes 16132
Aaron Rodgers 13852
Josh Allen 13758
Derek Carr 13271
Matt Ryan 13015

Step 3: Let’s Plot these stats, so we can compare them better

步骤 3:绘制这些数据的统计图,便于我们更好地进行比较

Let’s really drive home our key point that Copilot is a powerful tool by asking it to go even one step farther. The printout of all the quarterback stats is likely a useful analysis for some purposes. But a visual plot might be a better way of presenting this information. Can we ask Copilot to plot it?

我们通过要求 Copilot 进行更进一步的操作,从而实质上突显出我们的核心论点:Copilot 是一款强大的工具。对于某些特定目的而言,打印所有四分卫的统计数据可能是一项有益的分析。但是,采用可视化图表可能是展现这些信息的更好途径。我们可以请求 Copilot 来进行图表绘制吗?

Suppose we just care about the top performing quarterbacks, and so we decide to set an arbitrary limit of 4,000 yards as the minimum number of yards a quarterback needs to throw during this time window to be included in our plot. Granted, asking Copilot to plot the data is likely to introduce some new challenges because it will involve going beyond straight text, but let’s give it a try.

假定我们仅对顶尖表现的四分卫感兴趣,所以我们决定设定一个临界值,即在这个时间框架内四分卫需要投掷的最少码数为4000码,这样才能将他们纳入我们的图表分析。确实,要求 Copilot 进行数据绘图可能会带来一些新挑战,因为这需要进行非纯文本的操作,但我们可以尝试一下。

At the end of the code we just wrote, add the following comment:

在我们刚编写的代码的末尾添加以下注释:

"""
plot the players by their number of passing yards only for
players with more than 4000 passing yards
"""
"""
仅针对传球码数超过4000码的球员,绘制其传球码数图表
"""

When we gave Copilot this prompt, here is the code it gave us:

我们向 Copilot 提出这段提示词后,它给出的代码如下:

Listing 2.2 Code given by Copilot to plot our data about quarterbacks.

列表 2.2 Copilot 提供的绘制四分卫数据的代码。

import matplotlib.pyplot as plt
import numpy as np
# create a list of players
players = []
# create a list of passing yards
yards = []
# for each player in the dictionary
for player in passing_yards:
   # if the player has more than 4000 passing yards
   if passing_yards[player] > 4000:
       # add the player to the list of players
       players.append(player)
       # add the passing yards to the list of passing yards
       yards.append(passing_yards[player])
# create a numpy array of the passing yards
yards = np.array(yards)
# create a numpy array of the players
players = np.array(players)
# sort the players by the passing yards
print(np.argsort(yards))
players = players[np.argsort(yards)]
# sort the passing yards
yards = np.sort(yards)
# create a figure
plt.figure()
# create a bar chart of the players and their passing yards
plt.bar(players, yards)
# rotate the x axis labels
plt.xticks(rotation=90)
# show the plot
plt.show()

We gave Copilot this prompt a number of times and it would consistently produce a reasonable graph, but the code and graph varied each time. This is just one representative result, and in this result, the code is notably more advanced than the code you saw earlier this chapter. In fact, we need a more in depth understanding of the code to be able to walk through it in any real detail so we’re just going to skip any attempt to read through or interpret the code at this point in the book. We can tell you that, at a high-level, it properly imported a Python module designed to make plots (called matplotlib), did some fairly clever data manipulation in the middle using a Python module called numpy, and even had the sense to rotate player names so that they could print well as an x-axis label.

我们多次向 Copilot 提供这段提示词,它每次都能产生一个合理的图形,但是代码和图形每次都有所变化。这只是其中一个具有代表性的结果,在这个结果中,代码比本章早些时候展示的代码要复杂得多。事实上,我们需要更深层次地理解代码才能详细地分析它,因此我们在本书的这个阶段将跳过任何尝试去阅读或解释代码。可以高层次地告诉你,它正确地导入了一个用于绘图的 Python 模块(名为 matplotlib),在中部利用名为 numpy` 的 Python 模块进行了一些相当巧妙的数据操作,并且甚至还考虑到了将球员名字旋转,使其能够作为 x 轴标签清晰地打印。

If you go to run this code, you might hit a snag, however. Because Copilot learned from code in GitHub, it doesn’t know what Python modules are installed on your personal machine. The programmers who wrote the code that Copilot learned from likely had matplotlib installed, and matplotlib is the right module to use here, but matplotlib is not a module installed by default in Python. If you don’t have it installed, you’ll get an error when you try to run the code about not finding the “matplotlib” module.

但是,当你尝试执行这段代码时,可能会遇到一个问题。因为 Copilot 根据 GitHub 上的代码进行学习,它无法知道你的个人设备上安装了哪些 Python 模块。Copilot 学习的代码的编写者很可能安装了 matplotlib,而且在这种情况下使用 matplotlib 是正确的选择,但是 matplotlib 并不是 Python 默认安装的模块。如果你尚未安装它,在尝试运行代码时,你将遇到一个错误,提示无法找到 matplotlib 模块。

Python modules

Python modules expand the capability of the programming language. There are many modules in Python and they can help you do anything from data analysis to creating websites to writing video games. You can recognize when code wants to use a Python module by the import statement in the code. Python doesn’t automatically install all the modules for you because you likely won’t use most of them. When you want to use a module then, you’ll need to install the package containing the module yourself.

Python 模块

Python 模块扩充了这门编程语言的功能范畴。Python 中存在大量模块,它们能帮助你完成各式各样的任务,包括数据分析、网站创建和视频游戏开发等。通过代码中的 import 语句,你可以知道代码希望使用 Python 模块。Python 并不会自动安装所有模块,因为很可能你不会用到它们的大部分。因此,当你希望使用某个模块时,你需要自己安装含有该模块的包。

To fix this error, you’ll need to install matplotlib. The good news is that Python has made it really easy to install new packages. Go to the “Terminal” at the bottom right of VSCode and type:

要修复这个错误,你需要安装 matplotlib。好消息是在 Python 安装新的包已经非常简单了。在 VS Code 右下角的 “终端” 里输入:

pip install matplotlib

Note

For some operating systems you may need to use pip3 rather than pip. On Windows machines, we recommend using pip if you followed our installation instructions. On Mac or Linux machines, we recommend using pip3.

注意

对于某些操作系统,你可能需要使用 pip3 而不是 pip。在 Windows 设备上,如果你是按照我们的安装说明进行操作的,那建议你使用 pip。而在 Mac 或 Linux 设备上,我们建议使用 pip3

When you run this command, you’ll see that a bunch of modules are installed, including numpy (the next module this code wants to use). (matplotlib requires Python modules of its own, so it installs all the modules you need to use matplotlib in addition to matplotlib itself.) When you try to run the code again, you’ll get a plot like this.

运行这个命令后,你会看到一大堆模块被安装了,其中包括 numpy(这段代码接下来也会用到它)。(matplotlib 自己也依赖别的 Python 模块,因此除了 matplotlib 本身,它还会安装使用 matplotlib 所需的所有模块。)当你尝试再次运行代码时,将得到如下图表。

Figure 2.5 The plot produced by the code in listing 2.2.

图 2.5 代码清单 2.2 所生成的图表。

In this bar graph, we see the y-axis is the number of passing yards and the x-axis is the player’s name. The players are sorted from the fewest yards (with a minimum of 4000) to most yards. Admittedly, it’s not perfect as it is missing a y-axis label and the names on the x-axis are cut off at the bottom, but this is pretty impressive given all we gave Copilot was a pretty short prompt. We could keep adding prompts to see if we can format the graph better, but we’ve already achieved the primary goals for this section which was to show you how powerful Copilot is at helping us write code and to get a feel for how to interact with Copilot.

在这个柱状图中,我们看到 y 轴是传球码数,x 轴是球员的名字。球员按照传球码数从最少(最少为 4000 码)到最多进行排序。诚然,这个图表并不完美,因为它缺少 y 轴标签,而 x 轴上的名字在底部被截断了,但考虑到我们给 Copilot 的提示词非常简短,这已经相当令人赞叹了。我们可以继续添加提示词,看看是否能更好地格式化图表,但我们已经完成了本节的主要目标,即向你展示 Copilot 在帮助我们编写代码方面的强大能力,并让你找到与 Copilot 互动的感觉。

Indeed, in this chapter, we’ve accomplished a great deal! If you’ve finished setting up your programming environment and followed along the example with us, you should be proud. You’ve taken a huge step toward writing software! Beyond the details of setting up your environment, we’ve written software to solve our first problem. Moreover, you’ve observed the process of writing software with Copilot that starts with writing good prompts to help Copilot give us the code we want. In the examples in this chapter, Copilot gave us the code we wanted without us needing to change the prompt or debug the code to figure out why it's not working properly. That was a nice way to showcase the power of using an AI Assistant to program, but you will often find yourself having to test the code, change the prompts, and sometimes try to understand why the code is wrong. This is the AI Assistant programming process that we’ll learn more about in upcoming chapters.

不得不说,我们在这一章取得了很大进展!**如果你已经完成了编程环境的设置,并跟着我们的示例进行了操作,你应该感到自豪。你已经在编写软件的道路上迈进了重要的一步!**不仅仅是完成了细碎的环境配置,我们还写出程序并完成了我们的第一个练习。此外,你还观察到了使用 Copilot 编写软件的过程,这个过程始于撰写良好的提示词,让 Copilot 生成我们想要的代码。在本章的示例中,Copilot 给出了我们想要的代码,而我们无需更改提示词,也无需调试代码来排查代码出错的原因。这个例子很好地展示了 AI 编程助手的强大能力,但你往往还是会发现自己需要测试代码、更改提示词,有时还需要尝试理解代码为何出错。在接下来的章节中,我们会更多地了解的与 AI 编程助手的协作过程。

[译] [010] 欢迎加入

Welcome

欢迎加入

Thank you for purchasing the MEAP for Learn AI-Assisted Python Programming.

感谢您购买《AI 辅助编程》这本书的预览版。

We’re at the start of a new era in programming. AI coding assistants are here—-ChatGPT, Copilot, and others—-and they’re transforming how people program. The two of us have spent our careers researching and enhancing student learning of programming. We have never been more excited about the possibilities that AI coding assistants bring for the beginner or would-be programmer.

我们即将迈入编程的新时代。AI 编程助手已经登场,像 ChatGPT、GitHub Copilot 等工具正在改变编程的方式。在我们二人的职业生涯中,一直致力于研究和提升学生在编程领域的学习效率。当我们看到 AI 编程助手为初学者和未来程序员们所带来的无限可能,我们感到无比兴奋。

In this book, we fully embrace these tools and use them to offer an alternative way to learn how to write software. We believe that new learners of Python shouldn’t be spending time learning low-level details that are now mostly solved by the AI coding assistants. Instead, we believe that these tools empower you to write larger and more powerful software faster.

在这本书中,我们完全拥抱这些工具,并借助它们提出了一种全新的编程学习方法。我们认为,Python 新手不该深究那些 AI 编程助手已经处理得很好的底层细节;与此相反,你应该充分享受这些工具的赋能,以更快的速度编写更复杂、更强大的软件。

This book is for those who are interested in learning how to write software with Python or for the many people who tried and failed (we get it!) to learn how to write Python code in the traditional way. No prior programming skills are required, but we expect you to be comfortable with installing software and managing files.

这本书适合那些有兴趣学习 Python 编程的人士,以及太多太多尝试过传统学习方法但失败了的人们(我们懂的!)。虽然并不需要具备任何编程技能,但我们还是希望你可以熟练地进行软件安装和文件管理。

We’ve organized the book to get you started writing software with Copilot right from the start. Then we teach you the essential skills that you need to program with Copilot effectively: code reading, problem decomposition, and testing. After that, we’ll bring all the skills together to write small software projects in meaningful domains.

我们组织了这本书,让你从一开始就能够开始使用 Copilot 编写软件。然后,我们教你与 Copilot 有效编程所需的基本技能:代码阅读、问题分解和测试。之后,我们将所有技能汇集起来,编写在有意义的领域中的小型软件项目。

我们精心安排了本书的结构,确保你一开始就能运用 Copilot 编写代码。接下来,我们将传授 Copilot 高效编程的核心技能:阅读代码、分析问题、进行测试。最后,我们会将这些技能结合起来,在具有实际意义的领域中完成小型软件项目的编写。

At the same time, we need to caution you: it’s early. These tools seem to be changing on a daily basis. We don’t know what programming will look like when the dust settles. The two of us are exploring new territory with few established answers and we are excited to start this journey with you.

与此同时,我们还必须提醒您:目前还处于早期阶段。这些工具似乎每天都在变化。我们无法预测,当尘埃落定后,编程将会演变成何种面貌。我们二人正在探索这片鲜有定论的新领域,我们很高兴能与您一起开始这段旅程。

If you have any questions, comments, or suggestions, please share them in Manning’s liveBook Discussion forum for our book.

如果您对这本书有任何疑问、意见或建议,请在 Manning 的开放出版论区分享。

-—Leo Porter and Daniel Zingaro

——利奥·波特(Leo Porter)与丹尼尔·津加罗(Daniel Zingaro)

[译] [204] 应对常见的 Copilot 难题

2.4 Addressing common Copilot challenges

2.4 应对常见的 Copilot 难题

It may seem early in your experience with Copilot to start talking about common challenges with Copilot, but you may have already run into this when writing your first program and you’ll certainly encounter some of these working through our next example and in the next chapter, so we wanted to give these to you now.

虽然你刚开始使用 Copilot,现在就讨论 Copilot 的常见挑战似乎有些早,但你在编写第一个程序时可能已经碰到了这些问题,在接下来的示例和下一章中肯定还会遇到,因此我们想提前给你一些建议。

In our time working with Copilot, we’ve run into a few common challenges. These challenges will likely decrease with time as Copilot improves, but they were still issues at the time of writing. Although the challenges in table 2.1 are not exhaustive of what you might encounter, we hope our tips on how to address these common challenges will help you get up and running quickly. We’ll keep a running list at our book’s website so please feel free to reach out to us on the forums if you feel we’ve missed something!

在我们与 Copilot 的合作过程中,我们遇到了一些常见的挑战。虽然随着 Copilot 的持续改善,这些挑战可能会逐渐减少,但在本书撰写时,这些问题仍然存在。表 2.1 中列出的挑战并不是你可能遇到的所有问题的详尽列表,但我们希望我们提出的如何应对这些常见挑战的建议能帮助你迅速开始工作。我们会在本书的网站上维护一个更新列表,所以如果你觉得我们忽略了某些内容,请随时通过论坛联系我们!

Table 2.1 Common Challenges working with Copilot
表 2.1 在使用 Copilot 过程中的常见难题

Challenge

Description

Remedies

Comments only

If you give Copilot a prompt using the comment symbol (#), when you start a newline, it wants to just give you more comments rather than code. For example:

# output "Hello Copilot" to the screen

# print "Hello world" to the screen

We’ve seen Copilot generate line after line of comments, sometimes repeating itself! When this happens, suggestion 3 (use docstrings) is sometimes the most effective.

1. Add a newline (Enter) between your comment and Copilot’s suggestion to help it switch from comments to code.

2. If a newline doesn’t work, you can type a letter or two of code (no comment symbol). A couple letters from a keyword in your prompt usually works. For example:

# output "Hello Copilot" to the screen

pr

A couple letters from a keyword typically causes Copilot to give a code suggestion.

3. Switch from using # comments to docstring comments like this:

"""

output "Hello Copilot" to the screen

"""

4. Use Ctrl–Enter to see if Copilot will give you suggestions that are code rather than comments

Wrong code

Sometimes Copilot just gives you obviously wrong code from the start. (You’ll learn throughout this book how to identify incorrect code!)

In addition, sometimes Copilot seems to get stuck down wrong paths. For example, it might seem to be trying to solve a different problem than what you’ve asked it to solve. (Suggestion 3, in particular, can help with getting Copilot to go down a new path.)

Much of this book is about how to address this problem, but here are some quick tips to get Copilot to help:

1. Change your prompt to see if you can better describe what you need.

2. Try using Ctrl–Enter to find a suggestion from Copilot that is the correct code.

3. Close the VS Code program, wait a little bit, and restart it. This can help clear the Copilot cache to get new suggestions.

4. Try breaking down the problem into smaller steps (see chapter 7 for more details).

5. Debug the code (see chapter 8).

6. Try asking ChatGPT for the code and feed its suggestions into VS Code. A different large language model (LLM) can sometimes give suggestions that help the other LLM to get unstuck.

Copilot gives you

# YOUR CODE HERE

We’ve had Copilot seem to tell us to write our own code by generating this (or similar text) after a prompt:

# YOUR CODE HERE

We believe this is happening when we ask Copilot to solve a problem that has been given by an instructor to students to solve in the past. Why? Well, when we write our assignments for our students, we (as instructors) often write some code and then tell our students to write the rest by writing

# YOUR CODE HERE

where we want students to write their code. Students tend to leave that comment in their solution code, which means Copilot was trained to think this comment is an important part of the solution (it’s not). Often, we’re able to solve this problem by finding reasonable solutions in the Copilot suggestions with Ctrl–Enter, but please see the solutions for Wrong Code if that doesn’t work.

Missing modules

Copilot gives you code, but it won’t work because there are modules missing. (Modules are additional libraries that can be added to Python to give prebuilt functionality.)

See section 2.5, under “Modules” for how to install new modules on your machine.

难题

描述

解决方法

仅限注释

如果你使用注释符号(#)给 Copilot 提供提示词,当你开始新的一行时,它只会给你更多的注释而不是代码。例如:

# 在屏幕上输出 “Hello Copilot”

# 在屏幕上打印 “Hello world”

我们见过 Copilot 生成一行又一行的注释,有时候还会重复!当发生这种情况时,建议 3(使用文档字符串)有时是最有效的。

1. 在你的注释和 Copilot 的建议之间添加一个新行(Enter),以帮助它从注释切换到代码。

2. 如果新行不起作用,你可以输入一两个代码字母(不使用注释符号)。你提示词中的一个关键字的几个字母通常有效。例如:

# 在屏幕上输出 “Hello Copilot”

pr

关键字的几个字母通常会使 Copilot 给出代码建议。

3. 从使用 # 注释切换到使用文档字符串注释,像这样:

"""

在屏幕上输出 “Hello Copilot”

"""

4. 使用 Ctrl–Enter 看看 Copilot 是否会给出代码而非注释的建议

错误的代码

有时 Copilot 一开始就给出了明显错误的代码。(在本书中,你将学习如何识别错误的代码!)

此外,有时 Copilot 似乎会陷入错误的路径。例如,它可能试图解决一个与你要求解决的问题不同的问题。(特别是建议 3,可以帮助 Copilot 走上新的路径。)

本书的很多内容都是关于如何解决这个问题的,但这里有一些快速提示可以让 Copilot 帮到你:

1. 改变你的提示词,看看你是否能更好地描述你需要的内容。

2. 尝试使用 Ctrl–Enter 找到 Copilot 的正确代码建议。

3. 关闭 VS Code 程序,稍等一会儿,然后重启它。这可以帮助清除 Copilot 缓存以获取新的建议。

4. 尝试将问题分解成更小的步骤(更多细节见第 7 章)。

5. 调试代码(见第 8 章)。

6. 尝试向 ChatGPT 请求代码,并将其建议输入 VS Code。不同的大型语言模型(LLM)有时可以提供帮助,让另一个 LLM 从困境中解脱出来。

Copilot 给出

# YOUR CODE HERE

我们有时会让 Copilot 在提示词后生成这样(或类似的文本)来告诉我们写自己的代码:

# YOUR CODE HERE

我们认为,当我们让 Copilot 解决过去教师给学生出的问题时,这种情况就会发生。为什么呢?因为当我们为我们的学生编写作业时,我们(作为教师)经常会写一些代码,然后通过写

# YOUR CODE HERE

来告诉我们的学生在哪里编写他们的代码。学生们往往会在他们的解决方案代码中保留那个评论,这意味着 Copilot 被训练认为这个评论是解决方案的重要部分(实际上不是)。通常,我们能够通过在 Copilot 建议中使用 Ctrl–Enter 找到合理的解决方案,但如果那不起作用,请参见“错误的代码”的解决方案。

缺少模块

Copilot 给出了代码,但由于缺少模块而无法工作。(模块是可以添加到 Python 中的额外库,用以提供预构建的功能。)

请参阅第 2.5 节的“模块”,了解如何在你的机器上安装新模块。

[译] [1005] 本章小结

Summary

本章小结

  • Games have a common program flow that includes game setup and gameplay.
  • Randomness is an important ingredient of many games.
  • We can add randomness to our Python games by using functions in the random module.
  • We can implement games with Copilot using the same workflow that we’ve used throughout the book, with problem decomposition playing a key role.
  • 所有游戏都遵循一种共通的程序流程,包括游戏的设置和玩法。
  • 随机性是众多游戏不可或缺的一部分。
  • 我们可以通过使用 random 模块中的函数,在我们的 Python 游戏中添加随机性。
  • 我们可以利用本书中一贯采用的工作流程,通过 Copilot 来实现游戏的开发,问题分解在此过程中扮演关键角色。

[译] [203] 在 VS Code 中使用 Copilot

2.3 Working with Copilot in Visual Studio Code

2.3 在 VS Code 中使用 Copilot


本节译文略。


Now that you have your system set up, let’s get acquainted with the VSCode interface shown in figure 2.1. (You may need to click on the “Explorer” icon in the middle/top left to have this same view.) The following regions are identified in figure 2.1:

  • Activity Bar - on the far left is the activity bar where we can open file folders or install extensions (as you did to install the Github Copilot extension in the previous section).

  • Side Bar – shows what is presently open in the Activity Bar. In figure 2.1, the Activity Bar has the “Explorer” selected and hence the Side Bar is showing the files in the present folder.

  • Editor Pane(s) - these are the primary areas we will be using to create our software. The editor in the Editor Pane is similar to any other text editor in that you can write text, edit text, copy and paste using the clipboard, and so on. What is special about it, however, is that it is designed to work well with code. As we’ll see in the next example, you will be primarily working in this window by asking Copilot to generate code and then testing that code.

  • Output and Terminal Panel – this is the area of the interface for seeing the output of your code or any errors that have occurred. It has the tabs “Problems”, “Output”, “Debug Console”, and “Terminal.” We will primarily be using the “Problems” tab where we can see potential errors in our code and the “Terminal” tab that allows us to interact with Python and will be where we see the output of our code.

We highlighted the Copilot logo in the bottom right of figure 2.1 as you should be seeing this symbol (or similar) if you setup Copilot properly in the previous section.

Figure 2.1 The VSCode Interface [3].

2.3.1 Set up your working folder

In the top of the Activity Bar on the left in VSCode you will find the “Explorer” as the top icon. After you click on the “Explorer”, it should say “No Folder Open.” Click on the button to “Open Folder” and select a folder in your computer (or make a new one—we like the folder name fun_with_Copilot). Once you’ve opened this folder, your workspace will be the folder you opened which means you should have your code and any data files, like the one we’ll use later this chapter, in that folder.

File not found or file missing errors

If you ever receive an error that says you are missing a file, these are the kind of errors that can be really annoying when writing software. It could be that you just didn’t put the file in your working folder, this happens. That’s an easy fix by copying or moving the file into the correct folder. However, sometimes, you’ll look in the folder and the file will be there, but when you run your code in VSCode, Python can’t seem to find it. If this happens to you (it happened to us when writing the book!), be sure to have the folder with the code and the desired file open using Explorer in VSCode (as shown in the Side Bar in figure 2.1).

2.3.2 Check to see if your setup is working properly

Let’s check to see if we’ve set up everything properly and that Copilot is working. To do this, start by creating a new file to hold our program. You do this by going to “File -> New File” (figure 2.2), then selecting “Python File” (figure 2.3).

Figure 2.2 How to create a new file in VSCode.

Figure 2.3 Select to create the New File as a Python File.

After creating it, we like to make sure that we’ve saved the file. Go to “File -> Save As” and let’s just name this file first_Copilot_program.

Next, in the text editor, type:

# output "Hello Copilot" to the screen

The prompts and code we write will be in bold font (as above) to help distinguish between what we write and the code and comments Copilot may give us. The “#” sign at the start is important (and you should include it in what you typed). It means that what you wrote is a comment (depending on your VSCode color palette, it’ll be a different color than the code we’re about to produce). Comments are not code: the computer executes code and does not execute comments. Comments are used by programmers to provide a human-readable summary of what the code did to help other software engineers read the code. Today, its purpose has expanded to also work as a way to prompt Copilot. After writing a comment (and sometimes even while writing comments), Copilot will attempt to give us suggestions. You can think of this as a much more sophisticated autocomplete, like when you type “New York T” in your search engine, and it autocompletes with “New York Times.”

To trigger Copilot to start giving us code (or more comments), press Enter at the end of the line, and you’ll be at the start of a new line. Pause for a moment, and you should see something appear. Until accepted, Copilot’s suggestions are in light gray italics. If you do not get a suggestion yet, you may need to hit enter a second time to trigger Copilot to suggest the code.

Here’s what happened for us:

# output "Hello Copilot" to the screen
print("Hello Copilot")

If you still do not see a suggestion from Copilot, try pressing <CTRL>+Enter (hold “CTRL” while pressing “Enter”). When you press <Ctrl>+Enter, a new window on the right-hand side of the screen should appear. The window will be to the right of your editor window with the program and will be called “GitHub Copilot”. If that window does not appear, there may be something wrong with your setup, and we encourage you to go to the Appendix to double-check that you followed all the steps correctly or go to our book website to find (or ask for) help.

If you saw the suggestion, like above, from Copilot, then you need to just press <Tab> to accept Copilot’s suggestion. Once you do this, the suggestion that was previously in light gray italics should now be in a standard font like below:

# output "Hello Copilot" to the screen
print("Hello Copilot")

If you are seeing different code than this, it’s because of something we mentioned in the introduction: Copilot is non-deterministic, so you may see different code than us. We mention this because sometimes Copilot makes a minor mistake with the code here and may give you code similar to this:

print "Hello Copilot"

You might think this slight difference (no parentheses around “Hello Copilot”) wouldn’t matter, but it does. Before Python 3, this was the correct syntax for a print statement and when Python 3 was introduced, it switched to the code with parentheses. Since we’re running Python 3, you need to have the parentheses for the code to work. You might ask why Copilot gets this wrong, but the issue is Copilot was trained on Python code before Python 3.

If this seems annoying, we agree. But it’s a hint of the frustration novice programmers went through before Copilot. Most of what Copilot suggests is syntactically correct. But if you were a novice writing the code from scratch, missing parentheses or a missing colon somewhere might cost you a lot of time.

Now that we have the correct code:

# output "Hello Copilot" to the screen
print("Hello Copilot")

which, as you might guess, prints “Hello Copilot” to the screen, we should test it. First, you’ll want to save your file by going to File->Save.

Be sure to save your file before you run it. We’re embarrassed to admit the amount of time we’ve spent trying to fix code that was actually correct but hadn’t been saved.

To run your program, go to the top right corner of the text editor and press the “Run Code” icon as shown in figure 2.1. After pressing the icon, in the Terminal section at the bottom, you should see something like this:

> & C:/Users/YOURNAME/AppData/Local/Programs/Python/Python311/Python.exe c:/Users/YOURNAME/Copilot-book/first_Copilot_program.py

Hello Copilot

The top line starting with > is the command for the computer to run your code and all it says is to run your first_Copilot_program.py using Python. The second line is the output from running the command, and it says “Hello Copilot” which is what we’d hoped to see.

Congratulations! You’ve written your first program! We now know that your programming environment is set up correctly, and we can move onto our first programming task. But before we do, we’d like to go over some tips for how to deal with some common issues we’ve encountered when working with Copilot, so you have these tips available to you when working through the next example.

[译] [307] 本章小结

Summary

本章小结

  • Problem decomposition involves breaking a large problem into smaller tasks.
  • We use functions to perform problem decomposition in our programs.
  • Each function must solve one small, well-defined task.
  • Functions reduce duplication, make it easier to test our code, and reduce the likelihood of bugs.
  • Unit testing involves checking that the function does what we expect on a variety of different inputs.
  • A function header or signature is the first line of code of the function.
  • Parameters are used to provide information to functions.
  • The function header indicates the name of the function and names of its parameters.
  • We use return to output a value from a function.
  • A docstring uses the names of each function parameter to describe the purpose of the function.
  • To ask Copilot to write a function, we provide it the function header and docstring.
  • We get a function to do its work by calling it with values (also called arguments) for its parameters.
  • A variable is a name that refers to a value.
  • A helper function is a small function written to make it easier to write a bigger function.
  • A leaf function doesn’t call any other function to do its job.
  • To test whether a function is correct, we call it with different types of inputs.
  • Every Python value has a type, such as a number, text (string), true/false value (bool), or collection of values (list or dictionary).
  • Prompt engineering involves modifying our prompt for Copilot to influence the code that we get back.
  • We need to ensure that we import any module (such as string) our code is using.
  • 问题分解是指将大问题拆解为更小的任务。
  • 我们利用函数在我们的程序中执行问题分解。
  • 每个函数都应解决一个小而具体定义的任务。
  • 函数帮助减少代码重复,使得测试我们的代码变得更容易,并且减少了出现错误的可能性。
  • 单元测试涉及到检查函数在各种不同输入上的表现是否符合我们的预期。
  • 函数的头部或签名是该函数的首行代码。
  • 参数被用于向函数传递信息。
  • 函数头部显示了函数的名称和它的参数名称。
  • 我们使用 return 来从函数返回一个值。
  • 文档字符串利用函数参数的名称来说明函数的用途。
  • 向 Copilot 请求编写函数时,我们需要提供函数的头部和文档字符串。
  • 通过为其参数提供值(亦称为实参)来调用函数,从而使函数执行其功能。
  • 变量是引用值的名字。
  • 辅助函数是一个小型函数,旨在简化编写更大函数的过程。
  • 叶子函数不会调用其他任何函数来完成其任务。
  • 测试函数是否正确的一个方法是,用不同类型的输入去调用它。
  • 每个 Python 的值都有类型,比如数字、文本(字符串)、真/假(布尔值)、或值的集合(列表或字典)。
  • 提示工程指的是调整我们对 Copilot 的提示词,从而影响我们收到的代码。
  • 我们需要确保我们的代码使用的任何模块(例如 string)都被导入。

[译] [502] 本章小结

Summary

本章小结

  • A loop is used to repeat code as many times as needed.
  • We use a for loop when we know how many iterations the loop will do; we use a while loop when we don’t know how many iterations a loop will do.
  • Python uses indentation to determine which lines of code go together.
  • A dictionary is a mapping from keys (such as words) to values (such as their frequencies).
  • We need to open a file before we can read from it.
  • Once a file is open, we can use methods or a loop to read its lines.
  • Some modules (such as csv and zipfile) come with Python and can be used by importing them.
  • Other modules, such as matplotlib, need to be installed first before they can be imported and used.
  • 循环用于根据需要重复执行代码多次。
  • 当我们明确知道循环将执行的迭代次数时,使用 for 循环;而在不确定循环将执行多少次迭代时,则采用 while 循环。
  • Python 通过缩进来判断哪些代码行应该组合在一起。
  • 字典是从键(例如单词)到值(例如它们的频率)的映射。
  • 我们必须先打开一个文件,才能开始从中读取数据。
  • 文件打开后,我们可以使用方法或循环来逐行读取。
  • 一些模块(如 csv 和 zipfile)是 Python 自带的,可以直接通过导入来使用。
  • 其他模块,例如 matplotlib,需先安装之后才能导入和使用。

[译] [607] 本章小结

Summary

本章小结

  • Testing is a critical skill when writing software using Copilot.
  • Closed-box and open-box testing are different approaches to ensuring the code is correct. In closed-box testing, we come up with test cases based on what we know about the problem; in open-box testing, we additionally examine the code itself.
  • Doctest is a convenient way to test your code by adding test cases to the docstring description of a function.
  • Creating artificial files is an effective way to test code that uses files.
  • 在利用 Copilot 进行软件开发时,测试技能至关重要。
  • 黑盒测试与白盒测试代表了确保代码准确性的两种不同策略。黑盒测试依据我们对问题的认识来设计测试案例;而在白盒测试中,我们会进一步审视代码本身。
  • Doctest 提供了一种方便的方法,可以通过在函数的文档字符串说明中加入测试案例来对代码进行测试。
  • 创造虚拟文件是测试涉及文件操作代码的一个高效策略。

[译] [302] 函数的益处

3.2 Benefits of Functions

3.2 函数的益处

We already mentioned that functions are critical in performing problem decomposition. Beyond problem decomposition, functions are valuable in software for a number of other reasons, including:

我们已经谈到,函数在进行问题分解方面发挥着关键作用。其实,函数在软件开发中之所以宝贵,还源于其他诸多方面的原因,包括:

  • Cognitive Load. You may have heard of cognitive load [1] before. It’s the amount of information your brain can handle at any given time and still be effective. If you are given four random words and asked to repeat them back, you might be able to do that. If you are given the same task with 20 words, most of us would fail as it’s too much information to handle at once. Similarly, if you’ve ever been on a road trip with your family and are trying to optimize the travel time, combined with stops for the kids, lunch breaks, bathroom stops, gas station stops, good locations for hotels, and so on, you might have felt your head swimming to manage all those constraints at once. That point when you can’t handle it all at once is when you’ve exceeded your own brain’s processing power. Programmers have the same problem. If they are trying to do too much at once or solve too complex a problem in one piece of code, they struggle to do it correctly. Functions are designed to help programmers avoid doing too much work at once.

  • 认知负荷。你可能听说过“认知负荷”[1]。它指的是大脑在同一时间内能够有效处理的信息量。例如,给你四个随机单词让你重复,你可能没问题;但如果是20个单词,大多数人会感到难以一次性记住。同样,在家庭自驾游中,当你试图同时考虑旅行时间、孩子的休息点、午餐、如厕、加油以及酒店位置等因素时,你可能会感到难以应对。这就是你的认知负荷达到极限的时刻。程序员在编程时也会遇到同样的问题。如果他们试图一次完成太多任务或解决过于复杂的问题,很容易出错。因此,函数的设计就是为了帮助程序员避免一次性承担过多工作。

  • Avoid Repetition. Programmers (and, we’d argue, humans in general) aren’t very excited about solving the same problem over and over. If I write a function that can correctly compute the area of a circle once, I don’t need to write that code ever again. That means if I have two sections of my code that need to compute the area of a circle, I’d write one function that computes the area of the circle and then I’d have my code call that function in each of those two places.

  • 避免重复。 程序员(我们认为,一般人也是如此)并不喜欢一遍又一遍地解决同一个问题。如果我编写了一个函数,它能正确计算圆的面积,那么我就不需要再次编写那段代码了。这意味着,如果我的代码中有两处需要计算圆的面积,我会编写一个计算圆面积的函数,然后在这两端代码中分别调用这个函数。

  • Improve Testing. It's a lot harder to test a section of code that does multiple things compared to code that does one thing. Programmers at companies use a variety of testing techniques, but a key technique is known as unit testing. Every function takes some input and produces some output. For a function that computes the area of a circle, for example, the input would be the circle’s radius and the output would be its area. Unit tests give a function an input and then compare that input to the desired result. For the area-of-a-circle function, we might test it by giving it varying inputs (e.g., some small positive numbers, some large positive numbers, and 0) and compare the result of the function against the values we know to be correct. If the answers from the function match what we expect, we have a higher degree of confidence that the code is correct. If the code produces a mistake, we won’t have much code to check to find and fix the problem. But if a function does more than one task, it vastly complicates the testing process as you need to test each task and the interaction of those tasks.

  • 提升测试效率。 相较于执行单一功能的代码,测试同时执行多个任务的代码要复杂得多。程序员们在公司中运用多种测试方法,但其中关键的一种称为“单元测试”。每个函数接收一些输入并产生一些输出。例如,计算圆面积的函数,其输入就是圆的半径,输出则是面积。单元测试向函数提供输入,然后将这些输入产生的输出与预期结果进行比较。对于计算圆面积的函数,我们可以给它多种输入(比如一些小的正数、一些大的正数和0),并将其结果与我们已知的正确值进行对比。如果函数的输出与我们的预期匹配,我们就对代码的正确性更有信心。如果代码出现错误,我们也不用检查太多代码来找出并解决问题。但如果一个函数执行多个任务,这将使测试过程变得复杂,因为你需要测试每个任务以及它们之间的交互。

  • Improve Reliability. When we write code as experienced software engineers, we know we make mistakes. We also know Copilot makes mistakes. If you imagine you are an amazing programmer and each line of code you write is 95% likely to be correct, how many lines of code do you think you can write before at least one of those lines is likely to be incorrect? The answer ends up being only 14. We think 95% correctness per line is probably a high bar for even experienced programmers and is likely a higher bar than what Copilot produces. By keeping the tasks small, things solvable in 12-20 lines of code, we reduce the likelihood that there’s an error in the code. If combined with good testing as noted above, we can feel even more confident that the code is correct. Lastly, nothing is worse than code that has multiple mistakes that interact together, and the likelihood of multiple mistakes grows the more code you write. Both of us have been on multi-hour debugging expeditions because our code had more than one mistake and, for both of us, we became a lot better at frequent testing of short pieces of code as a result! Improve Code Readability. In this book, we’re going to mostly use Copilot to write code from scratch. But that’s not the only way to use Copilot. If you have a larger piece of software that you or your coworkers are all editing and using, Copilot can jump in to help write code for that, too. It’s in everyone’s interest to understand the code, whether most of it is written by humans or by Copilot. That way, we can find bugs more easily, determine what code to start modifying when we want to add new features, and understand at a high level what would be easy or difficult to achieve with our overall program design. Having tasks broken down into functions helps us understand what each part of the code is doing so we can gain a better understanding of how it all works together. It also helps divide up the work and responsibility for ensuring the code is correct.

  • 提升可靠性。 作为资深软件工程师,我们在编码时难免会犯错。Copilot 同样不是完美无缺。设想你是一位杰出的程序员,你写出的每一行代码都有 95% 的把握是正确的,那么你觉得你能连续写多少行代码,才可能首次出现错误?答案是仅仅 14 行。即便是对经验丰富的程序员而言,每行代码 95% 的正确率标准可能也过高,更何况 Copilot。通过限制任务的规模,控制在 12 至 20 行代码之内解决问题,我们就能减少出错的几率。结合前文提到的有效测试,我们对代码正确性的信心将更加充足。最后,最糟糕的情况莫过于代码中存在多个相互影响的错误,而代码量越大,出错的概率也随之增加。我们都曾因为代码中的多个错误而陷入长时间的调试困境,这使得我们更加擅长于对小块代码进行频繁测试!增强代码可读性。本书将主要采用 Copilot 来全新编写代码,但这并非 Copilot 唯一的使用场景。当你和同事们共同编辑和使用一个大型软件项目时,Copilot 也能助一臂之力。无论是人类还是 Copilot 编写的代码,理解它对每个人都至关重要。这有助于我们更轻松地发现并修复错误,确定何时开始调整代码以添加新功能,并在宏观层面上把握实现我们整体程序设计的难易程度。将任务划分为函数,有助于我们理解代码的各个部分各自承担的职责,从而更好地理解它们是如何协同作用的。这也有助于我们分配工作和责任,确保代码的准确性。

These benefits are huge for programmers. Programming languages haven’t always had functions. But even before they did, programmers did their best to use other features to mimic functions. They were ugly hacks (Google “goto statements” if you’re interested), and all programmers are happy that we have proper functions now.

这些益处对于程序员来说极为重要。编程语言并不是从一开始就内置了函数功能。但在函数功能出现之前,程序员们已经尽力使用其他特性来模拟函数的效果。那些替代方案往往显得笨拙(如果你感兴趣,不妨在Google中搜索“goto语句”了解一下),现在我们有了正规的函数,所有程序员都为之庆幸。

You may be asking, “I see how these advantages matter to humans, but how do they impact Copilot?” In general, we believe all the principles that apply to humans apply to Copilot, albeit for sometimes different reasons. Copilot may not have cognitive load, but it’s going to do better when we ask it to solve problems similar to what’s been done by humans before; since humans write functions to solve tasks, Copilot will mimic that and write functions as well. Once we’ve written and tested a function, whether by hand or by Copilot, we don’t want to write it again. Knowing how to test if your program is working properly is just as essential for code produced by humans as it is by Copilot. Copilot is as likely to make mistakes when it generates code, so we want to catch them quickly, just as we do with human-written code. And even if you only work on your own code and never have anyone else read it, as programmers who have had to go back to edit code we wrote years ago, let us tell you that it is important for your code to be readable, even if the only person reading it is you.

您可能会好奇,“我理解这些优势对人类极为重要,但它们对 Copilot 有何影响?”总体而言,我们相信,所有适用于人类的原则也同样适用于 Copilot,尽管背后的原理可能有所不同。Copilot 固然没有认知负荷,但当我们要求它处理与人类解决过的类似问题时,它的表现会更加出色;既然人类通过编写函数来完成任务,Copilot 也会仿效这一做法,同样编写函数。一旦我们通过手工或 Copilot 编写并测试了一个函数,我们就不希望再次重写它。了解如何验证程序是否运行正常,对于人类编写的代码和 Copilot 生成的代码来说,都是至关重要的。Copilot 在生成代码时同样可能犯错,因此我们希望能够迅速发现并修正这些错误,就像我们对待人类编写的代码一样。即便你只是在独立编写和维护自己的代码,从未让他人阅读过,但作为曾不得不回顾和修改多年前自己编写的代码的程序员,我们要告诉你,保持代码的可读性非常重要,哪怕唯一的读者只有你自己。

[译] [500] 第五章 理解 Python 代码(下)

5 Reading Python Code: Part 2

第五章 理解 Python 代码(下)

This chapter covers

本章内容概要

  • Repeating code the required number of times using loops
  • Using indentation to tell Python which code goes together
  • Building dictionaries to store pairs of associated values
  • Setting up files to read and process data
  • Adding modules to extend Python into new domains
  • Asking Copilot to explain code
  • 使用循环实现代码的指定次数重复
  • 用缩进标识哪些代码是一组
  • 建立字典来保存一对一关联的值
  • 设置文件读取和处理数据
  • 添加模块,使 Python 拓展到新的领域
  • 要求 Copilot 对代码进行解释

In Chapter 4, we explored five Python features that you’re going to see all the time as you continue in your programming journey: functions, variables, conditionals (if statements), strings, and lists. You need to know those features to read code, and we explained why being able to read code is important whether or not we’re using Copilot.

在第四章中,我们探索了在编程之旅中频繁出现的五个 Python 特性:函数、变量、条件(if 语句)、字符串和列表。理解这些功能对阅读代码非常重要,我们还解释了无论是否使用 Copilot,阅读代码的能力都相当重要。

We’re going to continue in this chapter with five more Python features, which will round out our top 10. As in Chapter 4, we’ll do this through a combination of our own explanations, explanations from Copilot, and experimenting at the Python prompt.

本章,我们将继续深入另外五个 Python 功能,使我们的重点功能总数达到十个。与第 4 章的做法一样,我们将通过自己的讲解、Copilot 的解释和在 Python 提示符下进行实验的方式来实现这一目标。


本章目录

  • 5.1 Top 10 programming features you need to know: Part 2
    • 5.1.1 #6. Loops
    • 5.1.2 #7. Indentation
    • 5.1.3 #8. Dictionaries
    • 5.1.4 #9. Files
    • 5.1.5 #10. Modules
  • 5.1 你必须掌握的十大编程特性:第二部分
    • 5.1.1 #6. 循环
    • 5.1.2 #7. 缩进
    • 5.1.3 #8. 字典
    • 5.1.4 #9. 文件
    • 5.1.5 #10. 模块

本章正文略。如需阅读,请购买正版。


[译] [906] 本章小结

Summary

本章小结

  • Programmers often make tools to automate tedious tasks.
  • It’s often necessary to use a Python module to help us write our tool.
  • We can use Copilot chat to determine which Python modules we should be using.
  • It’s helpful to converse with Copilot to understand the pros and cons of various Python modules that may be available to us.
  • There are Python modules for working with the clipboard, working with .pdf files and other file formats, copying files, and more.
  • 程序员常常开发工具来自动化那些重复而繁琐的任务。
  • 通常,我们需要使用 Python 模块来帮助我们构建这些工具。
  • 我们可以利用 Copilot 聊天来决定我们应当使用哪些 Python 模块。
  • 与 Copilot 对话,理解我们可以使用的各种 Python 模块的优势与劣势,这一点非常有帮助。
  • 存在许多 Python 模块,用于处理剪贴板、处理 .pdf 文件以及其他文件格式、复制文件等。

[译] [304] 一个函数的合理任务应该是什么样的?

3.4 What’s a reasonable task for a function?

3.4 一个函数的合理任务应该是什么样的?

There’s no clear rule for what makes a good function, but there are some intuitions and recommendations we can share. Make no mistake, though: identifying good functions is a skill that takes time and practice. To help you with this, in this section we’ll outline our recommendations and provide you with some good and bad examples to help build that intuition. Then, in Section 3.5, we’ll show you a bunch of examples of how to write good functions.

好的函数并没有固定的标准,但我们能提供一些直观的建议和指导。需要明确的是,辨别优秀的函数是一种需要时间积累和实践锻炼的能力。在本节中,我们会简要介绍我们的建议,并给出一些好与坏的实例,以助于培养你们的这种直觉。接下来,在第三章的第五节中,我们会提供一系列编写优质函数的实例。

3.4.1 Attributes of good functions

3.4.1 良好函数的特征

Here are some guidelines that we believe will help you see what makes a good function:

下面提供的这些指导原则,我们相信将有助于你洞察怎样设计出一个良好的函数:

  • One clear task to perform. If they are leaf functions, this might be something like, “compute the volume of a sphere”, “find the largest number in a list”, or “check to see if a list contains a specific value.” Non-leaf functions can achieve more broad goals, like “update the game graphics” or “collect and sanitize input from the user”. Non-leaf functions should still have a particular goal in mind but are designed knowing they will likely call other functions to achieve their goal.

  • 执行一项明确的任务。 以叶子函数为例,这可能是“计算一个球体的体积”、“从列表中找出最大的数字”或是“检查一个列表是否包含某个特定值”。对于非叶子函数,它们可能承担更为宽泛的目标,例如“刷新游戏的视觉效果”或“搜集并净化用户输入”。尽管非叶子函数有明确的目标,但其设计时已考虑到可能会调用其他函数来共同完成任务。

  • Clearly defined behavior. The task “find the largest number in a list” is clearly defined. If I gave you a list of numbers and asked you for the largest number, you know what you should do. In contrast, the task “find the best word in the list” is poorly defined as stated. You need more information – what's the “best” word? Is it the longest, the one that uses the fewest vowels, the one that doesn’t share any of the same letters as “Leo” or “Dan”? You get the point; subjective tasks aren’t great for computers. Instead, we could write the function “find the word in the list that has the most characters” as what is expected is well defined. Often programmers can’t put all the particulars of a function just in the name, so they fill in the details in the docstring to clarify its use. If you find yourself having to write more than a few sentences to describe the functions behavior, the task is probably too much for a single function.

  • 定义明确的行为。 “在列表中寻找最大的数字”这个任务定义得很清楚。假如我给你一组数字,要你找出其中最大的一个,你会知道该如何着手。而“找出列表中最佳的单词”这一任务,表述得就过于模糊。你需要额外的信息——什么样的单词是“最佳”的?是最长的,还是使用元音最少的,或是与“Leo”和“Dan”没有任何相同字母的?这就说明了问题;对于计算机而言,主观性的任务并不合适。因此,我们可以设计一个函数,名为“找出列表中字符数最多的单词”,这样的预期结果就非常明确。程序员们往往无法仅通过函数名来涵盖函数的所有细节,所以他们会在函数的文档字符串中补充详细信息,以便阐明其具体用途。如果你发现自己需要写很多句话来描述一个函数的行为,那么这个任务很可能超出了一个函数所能承载的范围。

  • Short in number of lines of code. We’ve heard different rules over the years for the length of functions, informed by different company style guidelines. The lengths we’ve heard vary from 12 lines to 20 lines of Python code as the maximum number of lines. In these rules, the number of lines is being used as a proxy for code complexity and it’s not a bad general rule of thumb. As programmers ourselves, we both apply similar rules to our code to ensure the complexity doesn’t get out of hand. With Copilot, we can use this as a guide as well. If you ask Copilot for a function and it gives you back 50 lines of code, this probably isn’t a good function name or task and, as we discussed earlier, that many lines of code are likely to have errors anyway.

  • 代码行数宜简短。 多年来,我们了解到不同公司的风格指南对函数长度有着不同的规则。我们听到单个 Python 函数的最大行数限制,从 12 到 20 不等。这些规则中,把行数作为衡量代码复杂度的一个参考指标,是一个不错的通用原则。我们作为程序员,在编写代码时也会遵循类似的准则,以确保复杂度不会过高。在使用 Copilot 时,我们同样可以参考这一准则。如果你要求 Copilot 生成一个函数,而它返回了 50 行代码,这很可能意味着函数命名或任务描述不够好;而且如我们之前所讨论的,这么多行代码很可能本身就存在错误。

  • General value over specific use. A function that returns the number of values in a list that are greater than 1 might be a specific need for a part of your program, but there’s a way to make this better. The function should be rewritten to return the number of values in the list that are greater than another parameter. The new function would work for your use case (give the function 1 for the 2nd parameter) and for any other value than 1. We strive to have functions be as simple, but as powerful as possible.

  • 追求通用性而非定制化。 设计一个函数,如果它仅返回列表中大于 1 的元素数量,这可能只是满足了某个程序的某个具体需求。然而,我们可以让它变得更好:重构该函数,使其能够返回列表中大于某个给定参数的元素数量。这样的新函数不仅能够适应你的特定场景(只需将第二个参数设为 1),还能适应任何其他数值。我们的目标是让函数在保持简洁的同时,发挥最大的功效。

  • Clear input and output. You generally don’t want a lot of parameters. That doesn’t mean you can’t have a lot of input though. A single parameter could be a list of items (we’ll talk more about lists soon). It does mean that you want to find ways to keep the number of inputs to a minimum. You can only return one thing, but again, you can return a list so you aren’t as limited as it may appear. But if you find yourself writing a function that sometimes returns a list, sometimes returns a single value, and sometimes returns nothing, that’s probably not a good function.

  • 清晰的输入与输出。 通常情况下,你并不希望函数有太多的参数。这并不是说你不能接受大量的输入数据。例如,一个参数可以是一个项目列表(我们之后会详细讨论列表)。这样做的目的是为了尽量减少输入的数量。虽然函数只能返回一个结果,但由于可以返回列表,所以你实际上并不像看起来那样受限。但如果发现自己编写的函数有时返回列表,有时又返回单个值,有时则不返回任何东西,那么这个函数很可能设计得不够合理。

3.4.2 Examples of good (and bad) leaf functions

3.4.2 一些正面例子(和反面例子)

Here are examples of good leaf functions:

以下是关于叶子函数的一些正面例子:

  • Compute the volume of a sphere —- given the sphere’s radius, return its volume.

  • “计算球体体积” —- 提供球的半径,便能得出其体积。

  • Find the largest number in a list —- given a list, return the largest value.

  • “找出列表中的最大数” —— 给定一个列表,返回其中的最大值。

  • Check to see if a list contains a specific value —- given a list and a value, return true if the list contains the value and false if it does not.

  • “检查列表中是否存在某个值” —— 提供一个列表和一个特定的值,若列表中含有该值,函数返回 true;若不含有,返回 false

  • Print the state of the checkers game —- given a 2D list representing the game board, output the game board to the screen in text.

  • “打印跳棋游戏状态” —— 给定一个二维列表代表游戏棋盘,在屏幕上以文本形式输出棋盘状态。

  • Insert a value in a list -— given a list, a new value, and a location in the list, return a new list that is the old list with the new value inserted at the desired location.

  • “在列表中插入一个值” —— 给定一个列表、一个新值以及列表中的位置,返回一个新的列表,新列表是旧列表在指定位置插入新值后的版本。

Here are examples of bad leaf functions and our reasons for why they are bad:

以下是一些反面例子,我们也解释了它们不好的原因:

  • Request a user’s tax information and return the amount they owe this year. -- Perhaps in some countries this wouldn’t be too bad, but we can’t imagine this as a single function in either the United States or Canada given the complexity of the tax rules!

  • “请求用户的税务信息并返回他们今年应缴的税款” —— 或许在某些国家,这个场景尚可接受;但如果在美国或加拿大,考虑到税收制度的复杂性,我们很难将其设想为一个简单的功能。

  • Identify the largest value in the list and remove that value from the list. -- This might not seem so bad, but it’s really doing two things. The first is to find the largest value in the list. The second is to remove a value from the list. We’d recommend two leaf functions, one that finds the largest and one that removes the value from the list. However, this might make a good non-leaf function if your program needs to perform this task frequently.

  • “识别列表中的最大值并将其从列表中删除” —— 这种做法乍一看似乎没什么不妥,但其实它同时完成了两个任务。首先是寻找列表中的最大数值,其次是将该数值从列表中剔除。我们推荐将其拆分为两个简单的叶子函数,一个负责查找最大值,另一个负责删除指定值。当然,如果你的程序频繁需要进行这一操作,将其作为一个复合函数也是一个不错的选择。

  • (Thinking of our dataset from Chapter 2) Return the names of the quarterbacks with more than 4000 yards of passing in the dataset. -- This has too much specificity. Without a doubt, the number 4000 should be a parameter. But it’d likely be better to make a function that takes as input the position (quarterback, running back), the statistic (passing yards, games played), and the cutoff that we care about (4000, 8000), all as parameters. This new function would provide far more capability than the original, allowing a user to call the function to determine not only the names of particular quarterbacks who threw for more than 4000 yards, but also running backs who had more than 12 rushing touchdowns.

  • (回想第二章提到的数据集)“列出传球码数超过 4000 码的四分卫姓名” —— 此函数过于专门化。数字 4000 无疑应作为一个参数。不过,更优的做法是构建一个函数,它将接收诸如位置(四分卫、跑卫)、统计数据(传球码数、比赛次数)以及我们所关注的界限值(4000、8000)等参数。这样的新函数将大大扩展其应用范围,用户不仅可以利用它来查询那些传球码数超过 4000 码的四分卫,还能找出超过12次冲球达阵的跑卫。

  • Determine the best movie of all time. -- This is too vague. Best movie by what definition? What movies should it consider? A better version of this might be a function that determines the highest rated movie by users given at least a minimum number of ratings. This function would likely be part of a larger program where the function would have, as input, data from a movie database (say IMDB) and minimum number of user ratings. The output of the function would be the highest rated movie that has at least as many ratings as specified.

  • “选出史上最佳影片” -- 这个任务描述过于宽泛。最佳影片是根据什么标准来评定的呢?需要考虑哪些电影作品?一个更佳的方案是设计一个函数,它能够在满足最低评分人数要求的前提下,找出平均评分最高的电影。这个函数可能是某个更庞大程序的组成部分,其输入数据来自电影资料库(例如 IMDB)以及一个用户评分的最小阈值。输出结果则是达到规定评分人数且评分最高的那一部。

  • Play Call of Duty. -- This might be the main function in the large code base for the game Call of Duty, but it is definitely not a leaf function.

  • “玩《使命召唤》” -- 这可能是《使命召唤》游戏的大型代码库中 main 主函数,但它肯定不是一个叶子函数。

[译] [101] 我们如何与计算机对话

1.1 How we talk to computers

1.1 我们如何与计算机对话

Would you be happy if we started by asking you to read and understand this?

如果我们在开头要求你阅读并理解的是以下内容,你的感受会怎么样?

section .text
global _start
_start:
    mov ecx, 10
    mov eax, '0'
    l1:
    mov [num], eax
    mov eax, 4
    mov ebx, 1
    push ecx
    mov ecx, num
    mov edx, 1
    int 0x80
    mov eax, [num]
    inc eax
    pop ecx
    loop l1
    mov eax, 1
    int 0x80
section .bss
    num resb 1

That monstrosity prints out the numbers from 0 to 9. It's written using code in assembly language, a low-level programming language. Low-level programming languages, as you can see, are very far from languages that humans can easily read and write. They’re designed for computers, not humans.

这段 “天书” 能够打印出 0~9 的数字。它是使用汇编语言编写的,是一种低级编程语言。正如你所见,低级编程语言与我们能够轻松读写的语言相去甚远。它们主要是为计算机而设计的,并非面向人类。

No one wants to write programs like that. But, especially in the past, it was sometimes necessary. Programmers could use it to do exactly what they wanted the computer to do, down to individual instructions. This level of control was needed in order to squeeze every bit of performance out of underpowered computers. For example, the most speed-critical pieces of 1990s computer games such as Doom and Quake were written in assembly language like the code above. It simply wouldn't have been possible to make those games otherwise.

没人愿意写这种程序。但在早期,这是不得已而为之。程序员能借此控制每一条指令,让计算机精确实现他们的意图。这种控制层次是必须的,为的是从计算能力不足的电脑中挤出每一点性能。90 年代电子游戏中对速度要求极高的部分,比如《毁灭战士(Doom)》和《雷神之锤(Quake)》,都是采用上面那种汇编语言编写的。否则,根本无法制作出这些游戏。

Making it a little easier

让难度降低一点儿

Okay, no more of that. Let's move on. Would you be happier with this?

好的,汇编到此为止。我们继续。以下这段代码感觉如何?

for num in range(0, 9):
    print(num)

This, by contrast, is code in the Python language and is what many programmers use these days. Unlike assembly language, which is a low-level language, Python is considered a high-level language because it's much closer to natural language. Even though you don't know about Python code yet, you might be able to guess what this program is trying to do. The first line looks like it's doing something with the range of numbers from 0 to 9. The second line is printing something. It may not be too hard for you to believe that this program, just like the assembly language monstrosity, is supposed to print the numbers from 0 to 9. Unfortunately, something is wrong with it.

对上面不同,这段代码是用 Python 语言编写的,目前许多程序员都在使用它。不同于汇编语言这样的低级语言,Python 被视为高级语言,因为它与自然语言的距离更近。即便你对 Python 代码尚无了解,你或许也能猜出这段程序的目的。第一行似乎在处理 0~9 的数字范围。第二行在进行打印操作。不难理解,这段程序的目的与上面那段汇编语言天书一样,是要打印出 0~9 的数字。不过很可惜,程序中存在错误。

While this code is closer to English, it isn’t English. It's a programming language that, like assembly language, has specific rules. As in the code above, misunderstanding the details of those rules can result in a broken program.

尽管这种代码更接近英语,但它并非英语。它还是一种编程语言,就像汇编语言一样,有着特定的规则。正如上面的代码所示,误解这些规则的细节可能会导致程序错误。

The holy grail of communicating with a computer is to do so in a natural language such as English. We've been talking to computers using various programming languages over the past 70 years not because we want to, but because we have to. Computers were simply not powerful enough for the vagaries and idiosyncrasies of a language like English. Our programming languages improved—-from symbol soup assembly language to Python, for example—-but they are still computer languages, not natural languages.

与计算机沟通的终极目标是能够使用诸如英语这样的自然语言。在过去的 70 年中,我们之所以使用各种编程语言与计算机对话,并非出于喜好,而是迫于无奈。计算机的计算能力不足以应对英语等自然语言的复杂性和特异性。我们的编程语言虽然一直在演进——比如从天书一般的汇编语言发展到了 Python——但它们依旧是计算机语言,并非自然语言。

This is changing.

然而,这种情况正在发生变化。

Making it a lot easier

让难度降低一大截

Using an AI assistant, we can now ask for what we want in English and have the computer code written for us in response. To get a correct Python program that does actually print the numbers from 0 to 9, we can ask our AI assistant (Copilot) in normal English language like this:

有了 AI 助手,我们现在可以用英文明确提出我们的需求,并让计算机相应地编写代码。如果我们希望得到一段正确 Python 程序,打印出 0~9 数字,我们可以用日常英语向我们的 AI 助手(Copilot)发出指令:

# Output the numbers from 0 to 9
# 打印出 0~9 的数字

Copilot might respond to this prompt by generating something like this:

对此,Copilot 可能会根据这一指令生成如下代码:

for i in range(10):
    print(i)

Unlike the example we showed you before, this piece of Python code actually works!

不同于我们在上面看到的那个示例,这段 Python 代码确实能够正常运行!

AI coding assistants can be used to help people write code. In this book, we will learn how to use Copilot to write code for us. We will ask for what we want in English, and we will get the code back in Python.

AI 编程助手能够帮助人们编写代码。在本书中,我们会探索如何利用 Copilot 来编写代码。我们只需用英语描述我们的需求,便能收到用 Python 编写的代码作为回应。

More than that, we'll be able to use Copilot as a seamless part of our workflow. Without tools like Copilot, programmers routinely have two windows open: the one where they write code and the other where they ask on Google how to write code. This second window would have Google search results, Python documentation, or forums of programmers talking about how to write code to solve that particular problem. They're often pasting code from these results into their code, then tweaking it slightly for their context, trying alternatives, and so on. This has become a way of life for programmers—but you can imagine the inefficiency here. By some estimates, up to 35% of programmer’s time may be spent searching for code [1], and much of the code that is found is not readily usable. This will be much improved with Copilot helping us write our code.

更重要的是,我们将能够把 Copilot 无缝集成到我们的工作流程中。在没有 Copilot 这类工具的情况下,程序员通常需要同时打开两个窗口:一个用于编写代码,另一个用于在 Google 上查询编码方法。这第二个窗口可能充斥着 Google 搜索结果、Python 文档或是程序员论坛上讨论如何解决特定编码问题的帖子。他们经常需要从这些结果中复制代码到自己的项目中,随后进行适当的调整以符合自己的应用场景,尝试不同的解决方案等等。这已经成为了程序员生活的一部分——但你可以想象这种工作方式的效率有多低。据估计,程序员可能有多达 35% 的时间花在搜索代码上[1],而且找到的代码并不是立即可用的。有了 Copilot 协助我们编写代码,这种情况将得到显著改善。

[译] [100] 第一章 走近 AI 辅助编程

Introducing AI-Assisted Programming with Copilot

第一章 走近 AI 辅助编程

This chapter covers

本章内容概要

  • AI-assisted computer programming
  • How AI-assistants change how new programmers learn how to program
  • Why programming is never going to be the same
  • How AI assistants like Copilot work
  • How Copilot can solve many classic introductory programming problems
  • What else you can do with an AI coding assistant
  • Possible perils of AI assisted programming
  • AI 辅助的计算机编程
  • AI 助手如何改变新手程序员学习编程的方式
  • 为什么编程方式将发生永久性的变革
  • 像 Copilot 这样的 AI 助手是如何工作的
  • Copilot 为何能够解决众多经典的入门级编程问题
  • 利用 AI 编程助手还能做哪些事情
  • AI 辅助编程可能带来的风险

In this chapter, we’ll talk about how humans communicate with computers. We’ll introduce you to your AI Assistant, Copilot, an amazing tool that uses Artificial Intelligence (AI) to help people write software. More importantly, we’ll show you how Copilot can help you learn how to program. We’re not expecting that you’ve written any programs before. If you have, please don’t skip this chapter, even if you already know a little bit about programming.

在本章中,我们将探讨人类如何与计算机进行交流。我们会向您介绍您的 AI 助手 Copilot,一个利用人工智能(AI)帮助人们编写软件的神奇工具。更为重要的是,我们将展示 Copilot 如何帮助您学习编程。我们并不假设您之前有编程经验。不过,即便您有所了解,也请不要跳过本章。

Everyone needs to know why writing programs is different now that we have AI assistants like ChatGPT and Copilot, and that the skills we need to be effective programmers change. As we’ll see, we also need to be vigilant, because sometimes tools like ChatGPT and Copilot lie.

现如今,随着像 ChatGPT 和 Copilot 这样的 AI 助手的出现,编程方式已经发生了根本性的变化。这也意味着我们作为程序员所需掌握的技能也随之改变。同时我们也会看到,由于 ChatGPT 和 Copilot 这类工具有时可能会提供误导信息,我们还需保持警惕。

[译] [900] 第九章 繁琐事务自动化

9 Automating Tedious Tasks

第九章 繁琐事务自动化

This chapter covers

本章内容概要

  • Understanding why programmers write tools
  • Determining which modules we need to write a given tool
  • Automating cleaning up emails that have > > > symbols
  • Automating manipulating PDF files
  • Organizing your and your partner’s phone pictures in the same place
  • 探讨程序员编写工具的动机
  • 确定编写特定工具需要哪些模块
  • 自动清除含有 > > > 符号的电子邮件
  • 对 PDF 文件进行自动化操作
  • 把你及你伙伴的手机照片统一整理至同一位置

Suppose that you’re responsible for creating 100 reports, one for each of 100 people. Perhaps you’re a teacher and need to send a report to each of your students. Perhaps you work for HR and need to send an annual assessment report to each employee. Regardless of your role, you have the problem of having to create these reports, and you decided to prepare your reports as .pdf files. You need a customized cover page for each report, too, and those cover pages are designed by one of your colleagues (a graphic design artist).

假设你需要制作 100 份报告,每个人分得一份。可能你是教师,需要向每位学生发送一份报告;或者你在人力资源部门,需要向每位员工发送年度评估报告。不论你担任何种角色,你都将面临制作这些报告的挑战,并且你决定以 .pdf 格式来准备这些报告。每份报告还需一个个性化的封面,这些封面由你的一位同事——一位平面设计艺术家——设计。

You and your colleague work independently, and finally, the job is done. Or wait, not so fast. Because now you have to put each cover page at the beginning of each of your reports.

你和你的同事分头工作,终于,任务完成了。但是,别急。因为现在你必须把每个封面页添加到每份报告的最前面。

At this point, a non-programmer might grit their teeth and start on the job, manually merging the cover page with the first report, the second cover page with the second report, and so on. That could take hours. Not knowing that there may be another way, a non-programmer may just power ahead until the job is done.

此刻,非程序员可能只能硬着头皮开始工作,手动将每个封面与相应的报告一一合并,从第一个封面和报告开始,如此下去。这样的工作可能需要耗费数小时。由于不知道还有其他解决办法,非程序员可能只会坚持到底,直至任务完成。

But you’re a programmer now. And most programmers, the two of us included, would never power ahead with manual work like this.

然而,你现在是一名程序员。包括我们自己在内的绝大多数程序员,都不会选择坚持这种手动重复的工作。

...

...


本章目录

  • 9.1 Why programmers make tools
  • 9.2 How to use Copilot to write tools
  • 9.3 Example 1: Cleaning up email text
    • 9.3.1 Conversing with Copilot
    • 9.3.2 Writing the tool to clean up email
  • 9.4 Example 2: Adding cover pages to PDF files
    • 9.4.1 Conversing with Copilot
    • 9.4.2 Writing the tool
  • 9.5 Example 3: Merging phone picture libraries
    • 9.5.1 Conversing with Copilot
    • 9.5.2 Top-down design
    • 9.5.3 Writing the tool
  • 9.1 程序员为什么创造工具
  • 9.2 如何使用 Copilot 编写工具
  • 9.3 示例一:电子邮件文本的清理
    • 9.3.1 与 Copilot 对话
    • 9.3.2 制作工具
  • 9.4 示例二:给 PDF 文件增加封面页
    • 9.4.1 与 Copilot 对话
    • 9.4.2 制作工具
  • 9.5 示例三:合并手机照片库
    • 9.5.1 与 Copilot 对话
    • 9.5.2 自顶向下的设计
    • 9.5.3 制作工具

本章正文略。如需阅读,请购买正版。


[译] [105] 使用 Copilot 的风险和挑战

1.5 Risks and challenges of using Copilot

1.5 使用 Copilot 的风险和挑战

Now that we're all pumped up about getting Copilot to write code for us, we need to talk about the dangers inherent in using AI Assistants. See references [2] and [3] for elaboration on some of these points.

既然我们都对让 Copilot 帮我们编码感到非常兴奋,接下来我们必须讨论一下使用 AI 助手所固有的危险。有关这些观点的更多详细信息,请参见参考资料 [2] 和 [3]。

Copyright. As we discussed above, Copilot is trained on human-written code. More specifically, it was trained using millions of GitHub repositories containing open-source code. One worry is that Copilot will “steal” that code and give it to us. In our experience, Copilot doesn't often suggest a large chunk of someone else’s code, but that possibility is there. Even if the code that Copilot gives us is a melding and transformation of various bits of other people's code, there may still be licensing issues. For example, who owns the code produced by Copilot? There is currently no consensus on the answer.

版权问题。正如我们之前讨论的,Copilot 是基于人类编写的代码进行训练的。具体来说,它通过使用包含开源代码的数百万个 GitHub 仓库进行训练。存在一个担忧,即 Copilot 可能会“盗用”这些代码并提供给我们。根据我们的经验,Copilot 很少直接建议使用别人的大量代码,但这种可能性存在。即便 Copilot 提供给我们的代码是多个其他人代码的融合和转换,也可能存在许可问题。例如,谁拥有 Copilot 生成的代码的所有权?对于这个问题,目前还没有一个共识。

The Copilot team is adding features to help; for example, Copilot will be able to tell you whether the code that it produced is similar to already-existing code and what the license is on that code [4]. Learning and experimenting on your own is great, and we encourage that—but take the necessary care if you do intend to use this code for purposes beyond your home. We’re a bit vague here, and that’s intentional: it may take some time for laws to catch up to this new technology. It’s best to play it safe while these debates are had within society.

Copilot 团队正在引入新的功能来提供帮助;例如,Copilot 将能告诉你其生成的代码与已有代码的相似度,以及那些代码所适用的许可证类型 [4]。自我学习和实验是极好的,我们非常鼓励这种做法——但如果你计划将这些代码应用于个人家庭之外的场景,请务必采取必要的谨慎措施。我们这里的表述有意保持了一定的模糊性,这是因为法律可能需要时间来适应这一新技术。在社会就这些议题进行讨论之际,采取安全的做法总是最佳选择。

Education. As instructors of introductory programming courses ourselves, we have seen first-hand how well Copilot does on the types of assignments we have historically given our students. In one study [5], Copilot was asked to solve 166 common introductory programming tasks. And how well did it do? On its first attempt, it solved almost 50% of these problems. Give Copilot a little more information, and that number goes up to 80%. You have already seen for yourself how Copilot solves a standard introductory programming problem. Education needs to change in light of tools like Copilot, and instructors are currently discussing how these changes may look. Will students be allowed to use Copilot, and in what ways? How can Copilot help students learn? And what will programming assignments look like now?

教育。作为初级编程课程的讲师,我们直接见证了 Copilot 在我们以往布置给学生的作业类型上的表现。在一项研究 [5] 中,Copilot 被要求解决 166 项常见的初学者编程任务。它的表现如何?在首次尝试中,它解决了近 50% 的问题。如果给 Copilot 提供更多信息,这一数字可以提升至 80%。你已经亲身体验了 Copilot 如何解决标准的初学者编程问题。考虑到像 Copilot 这样的工具,教育领域需要变革,教师们目前正在讨论这些变化可能的形态。学生们能否被允许使用 Copilot,以及如何使用?Copilot 如何帮助学生学习?编程作业将会是怎样的新面貌?

Code quality. We need to be careful not to trust Copilot, especially with sensitive code or code that needs to be secure. Code written for medical devices, for example, or code that handles sensitive user data must always be thoroughly understood. It's tempting to ask Copilot for code, marvel at the code that it produces, and accept that code without scrutiny. But that code might be plain wrong. In this book, we will be working on code that will not be deployed at large, so while we will focus on getting correct code, we will not worry about the implications of using this code for broader purposes. In this book, we start building the foundations that you will need to independently determine whether code is correct.

代码质量。我们必须保持警惕,不能盲目依赖 Copilot,特别是处理敏感或需要保障安全的代码时更是如此。例如,用于医疗设备的代码或处理敏感用户数据的代码,必须始终被充分理解。虽然向 Copilot 请求代码,对其生成的代码感到惊叹,并在未经仔细审核的情况下接受这些代码可能非常诱人,但这些代码有可能完全错误。在这本书中,我们将处理的代码不会进行大规模部署,因此我们虽然会专注于获取正确的代码,但不会过多考虑使用这些代码的更广泛影响。在本书中,我们将开始构建独立判断代码正确性所需的基础。

Code security. As with code quality, code security is absolutely not assured when we get code from Copilot. For example, if we were working with user data, getting code from Copilot is not enough. We would need to perform security audits and have expertise to determine that the code is secure. Again, though, we will not be using code from Copilot in real-world scenarios.

代码安全。 如同代码质量一样,从 Copilot 获得的代码并不能保证其安全性。例如,在处理用户数据时,仅仅使用 Copilot 提供的代码是远远不够的。我们需要进行安全审计,并需具备专业知识以确保代码的安全。然而,我们不会在现实世界的应用场景中使用来自 Copilot 的代码。

Therefore, we will not be focusing on security concerns.

因此,我们不会将重点放在安全问题上。

Not an expert. One of the markers of being an expert is awareness of what one knows and, equally importantly, what one doesn't. Experts are also often able to state how confident they are in their response; and, if they are not confident enough, they will learn further until they know that they know. Copilot, and LLMs more generally, do not do this. You ask them a question, and they answer, plain as that. They will confabulate if necessary. They will mix bits of truth with bits of garbage into a plausible sounding but overall nonsensical response. For example, we have seen LLMs fabricate obituaries for people who are alive, which doesn’t make any sense, yet the “obituaries” do contain elements of truth about people’s lives. When asked why an abacus can perform math faster than a computer, we have seen LLMs come up with responses—something about abacuses being mechanical and therefore necessarily the fastest. There is ongoing work in this area for LLMs to be able to say, "sorry, no, I don't know this," but we are not there yet. They don't know what they don't know and that means they need supervision.

不是专家。专家的一个显著特征是对自己所知及所不知有清晰的自觉,他们还能准确表达对自己答案的信心程度;若感到不够自信,便会进一步学习,直到确信自己掌握了知识。然而,Copilot 及更广泛的大型语言模型(LLM)并不具备这样的能力。当你向它们提出问题时,它们会直接回答,必要时甚至会编造答案。它们能将一些真实信息与错误信息混合,拼接出听起来合理但实际上完全没有意义的答案。例如,我们已经见证了 LLM 为仍然健在的人编写讣告,虽然这毫无道理,但这些“讣告”却确实含有关于人们生活的一些真实元素。被问及为何算盘能比电脑更快进行数学计算时,我们看到 LLM 给出了解释——某种关于算盘是机械设备,因此自然更快的论断。这一领域的研究正在进行中,目标是让 LLM 能够说出“对不起,我不知道”,但目前还未达到这一步。它们不知道自己不知道什么,这表明它们需要人工监督。

Bias. LLMs will reproduce the same biases present in the data on which they were trained. If you ask Copilot to generate a list of names, it will generate primarily English names. If you ask for a graph, it may produce a graph that doesn’t consider perceptual differences among humans. And if you ask for code, it may produce code in a style reminiscent of how dominant groups write code. (After all, the dominant groups wrote most of the code in the world, and Copilot is trained on that code.) Computer science and software engineering have long suffered with a lack of diversity. We cannot afford to stifle diversity further, and indeed we need to reverse the trend. We need to let more people in and allow them to express themselves in their own ways. How this will be handled with tools like Copilot is currently being worked out and is of crucial importance for the future of programming. However, we believe Copilot has the potential to improve diversity by lowering barriers for entry into the field.

偏见挑战。LLM 会重现其训练数据中存在的偏见。例如,当你请求 Copilot 生成姓名列表时,它通常会生成一些英文名;如果你要求绘制图表,得到的图表可能未充分考虑到人类之间的视觉感知差异;而要求编写代码时,它输出的代码风格很可能反映了主流群体的编码习惯。(事实上,由于主流群体编写了世界上的大部分代码,Copilot 的训练资料也以这些代码为基础。)长期以来,计算机科学和软件工程领域一直面临多样性不足的问题。我们不能再允许多样性进一步受损,更应努力扭转目前的趋势。我们需要欢迎更多人加入,让他们能以自己的方式自由表达。如何利用像 Copilot 这样的工具应对这一挑战,目前正在积极探索中,这对编程的未来极为关键。尽管如此,我们相信 Copilot 有通过降低入门门槛来增进行业多样性的巨大潜力。

[译] [1000] 第十章 开发小游戏

10 Making Some Games

第十章 开发小游戏

This chapter covers

本章内容概要

  • Adding randomness to our programs
  • Designing and programming a code-breaking logic game
  • Designing and programming a press-your-luck dice game
  • 在我们的程序中加入随机性
  • 设计并编程一个破解代码的逻辑游戏
  • 设计并编程一个冒险掷骰子游戏

There are many reasons why people learn to program. Some people want to automate tedious tasks as we did in the previous chapter. Some people want to work with artificial intelligence as we did in Chapter 7. Other people want to make interactive websites, Android or iOS apps, or Alexa skills. There’s an endless amount of stuff that programmers can make.

人们学习编程的理由千差万别。一些人希望能够像我们在上一章展示的那样,自动化那些重复性的任务。有些人则是想要像在第七章中那样,与人工智能进行合作。还有人愿意制作交互式网站、Android 或 iOS 应用程序,或是 Alexa 技能。程序员能够创造的东西实在是无穷无尽。

Another popular reason to learn programming is to create games. For that reason, we thought we’d end our Copilot programming journey with you by designing two small computer games. The first is a code-breaking game where you use clues to identify the computer’s secret number. The second is a two-player dice game where each player needs to balance risk and luck to reach the required number of points before the other player does. Instead of using graphics and animation, these games use text. We’ve made this decision to help us stay focused on the game logic, rather than the way that the game is represented or the way that the player interacts with the games. Along the way, we offer some next steps if you are interested in taking your game-making abilities further. And don’t worry, your current skills are a great start to that!

创造游戏是学习编程的另一个广受欢迎的原因。因此,我们打算通过设计两款小型计算机游戏,为你的 Copilot 编程之旅画上圆满的句号。第一款游戏是破解密码,通过线索揭开计算机隐藏的秘密数字。第二款是双人骰子游戏,玩家需要在对方之前,巧妙地权衡风险和运气以达到既定分数。选择文本而非图形和动画来展现这些游戏,是为了让我们能集中精力探讨游戏逻辑,而非游戏的视觉表现或是玩家的互动方式。如果你对提升游戏制作技能感兴趣,我们也会提供一些可能的后续步骤。不过,别担心,你目前所拥有的技能已经为此打下了良好的基础!


本章目录

  • 10.1 Game programs
  • 10.2 Adding randomness
  • 10.3 Example 1: Bulls and Cows
    • 10.3.1 How the game works
    • 10.3.2 Top-down design
    • 10.3.3 Parameters and return types
    • 10.3.4 Implementing our functions
    • 10.3.5 Adding a graphical interface for Bulls and Cows
  • 10.4 Example 2: Bogart
    • 10.4.1 How the game works
    • 10.4.2 Top-down design
    • 10.4.3 Implementing our functions
  • 10.1 游戏程序设计
  • 10.2 加入随机性
  • 10.3 示例一:公牛与奶牛游戏
    • 10.3.1 游戏的运行机制
    • 10.3.2 采用自顶向下的设计策略
    • 10.3.3 参数及返回值的类型
    • 10.3.4 实施我们的功能
    • 10.3.5 为公牛与奶牛游戏增加图形化界面
  • 10.4 示例二:Bogart 游戏
    • 10.4.1 游戏的运行机制
    • 10.4.2 采用自顶向下的设计策略
    • 10.4.3 实施我们的功能

本章正文略。如需阅读,请购买正版。


[译] [103] Copilot 如何改变我们学习编程的方式

1.3 How Copilot changes how we learn to program

1.3 Copilot 如何改变我们学习编程的方式

To illustrate how learning to program changes in the presence of an AI Assistant, we want to show you how powerful Copilot is at solving a programming task typically given to students in the middle of a programming course or in a book on how to program.

为了阐明 AI 辅助下学习编程方式的变化,我们将展示 Copilot 解决典型编程任务的强大功能,这类任务通常会在编程课程中期或编程入门书籍中给出。

When learning how to program in the past, learners often spent most of their time learning how to work with the syntax and basic structure of programs. Being able to write a program from scratch (line-by-line), like the one we’ll show you below, is seen as the culmination of all of their learning. Before we dive into the problem, we need to be clear: many new programmers cannot write the code to solve this problem despite the fact most programming instructors (and book authors) think that new programmers should be able to do so. Here are the instructions for one version of this problem [6].

在过去,学习编程的过程中,学习者往往需要花费大量时间掌握程序的语法和基础结构。能够从零开始(逐行)编写程序,如下面即将向你展示的示例,被认为是学习过程的顶峰。然而,在我们深入探讨这个问题之前,有一点需要明确:虽然多数编程导师和书籍作者认为新手程序员应该有能力做到这一点,但实际上许多新手程序员却无法编写代码解决此类问题。以下是该问题一个版本的指示 [6]。

“Write a program that will read the amount of rainfall for each day. A negative value of rainfall should be rejected since this is invalid and inadmissible. The program should print out the number of valid recorded days, the number of rainy days, the rainfall over the period, and the maximum amount of rain that fell on any one day. Use a sentinel value of 9999 to terminate the program.”

“编写一个程序,读取每天的降雨量。降雨量的负值应该被拒绝,因为这是无效且不可接受的。程序应该打印出有效记录天数、下雨天数、该时期的降雨总量以及任意一天降下的最大雨量。使用 9999 作为哨兵值来终止程序。”

If you have tried to learn to program before, this kind of problem statement may be familiar to you, and you may have struggled to solve such a problem.

如果你以前尝试过学习编程,这种类型的问题描述可能对你并不陌生,你可能在解决此类问题时遭遇过挑战。

But now, if we give exactly this prompt to Copilot, we get the following Python code as a result. (Remember from the Introduction that Copilot is nondeterministic, so if we ask it again, we might get different code!)

但现在,如果我们将这个确切的提示词交给 Copilot,我们得到了以下的 Python 代码作为结果。(请记得引言中提到的,Copilot 是非确定性的,所以如果我们再次提问,可能会得到不同的代码!)

Listing 1.1 Copilot code that solves the classic rainfall problem.

清单 1.1 Copilot 解决经典降雨问题的代码。

rainfall = 0
total_rainfall = 0
num_rainy_days = 0
num_valid_days = 0
max_rainfall = 0
rainfall = int(input("Enter the amount of rainfall for the day: "))
while rainfall != 9999:
    if rainfall < 0:
        print("Invalid rainfall amount. Please try again.")
        rainfall = int(input("Enter the amount of rainfall for the day: ")
    else:
        num_valid_days += 1
        total_rainfall += rainfall
        if rainfall > 0:
            num_rainy_days += 1
        if rainfall > max_rainfall:
            max_rainfall = rainfall
        rainfall = int(input("Enter the amount of rainfall for the day: ")
print("Number of valid days:", num_valid_days)
print("Number of rainy days:", num_rainy_days)
print("Total rainfall over the period:", total_rainfall)
print("Maximum amount of rain that fell on any one day:", max_rainfall)

We’re not expecting you to have any intuition right now that this code is good. But it is. As Computer Science Professors, we’d grade this code very highly.

我们目前并不指望你能直觉上判断这段代码的优劣。但事实上,它是非常好的。作为计算机科学的教授,我们会给这段代码打上很高的分数。

People learning to program used to spend weeks or months to get to a point where they could write programs like this. Now Copilot can offer code immediately. As we’ll see in the rest of the book, we still need to work to verify that this code is correct because Copilot can make mistakes. However, we don’t need to write it from scratch anymore. We believe this successful interaction with Copilot signals the end of the way that we have historically taught and learned programming.

以往,学习编程的人们可能需要花费几周甚至几个月的时间才能编写出类似这样的程序。如今,Copilot 能够立即提供代码。正如我们将在本书其余部分看到的,我们仍需努力以验证这段代码的正确性,因为 Copilot 有可能犯错。但我们不再需要从零开始编写代码。我们相信,这次与 Copilot 的成功互动预示着我们过去教学和学习编程方式的终结。

You, as someone interested in learning how to program, simply don’t need to struggle with syntax, control flow, and the host of other Python concepts needed to write code like this as you had to in the past. Sure, we are going to learn about those concepts in this book, but not so that you can demonstrate your understanding by writing code from scratch that Copilot can produce easily. No, we’ll learn those concepts only inasmuch as they help us solve meaningful problems and interact productively with Copilot. Instead, you get to learn how to write larger, more meaningful software faster, because of how an AI-Assistant fundamentally changes the skills needed to learn to program.

作为一个有志于学习编程的人,你不必再像以往那样,为了掌握语法、控制流程以及编写类似代码所需的诸多 Python 概念而苦苦挣扎。诚然,我们会在本书中讲解这些概念,但目的不是让你通过从零开始编写 Copilot 能轻而易举输出的代码来展示你的理解。不,我们学习这些概念,仅仅是为了它们能帮助我们解决实际问题,并与 Copilot 产生有成效的互动。反过来,你将因为 AI 助手从根本上改变了学习编程所需的技能,而学会如何更快地编写规模更大、意义更深远的软件。

[译] [800] 第八章 调试代码并且更深入地理解代码

8 Debugging and Better Understanding Your Code

第八章 调试代码并且更深入地理解代码

This chapter covers

本章内容概要

  • Determining the source of a bug

  • Fixing errors with Copilot techniques

  • Debugging code with Copilot

  • Using VS Code debugger to see how code is functioning

  • 查明 bug 的根源

  • 通过 Copilot 技巧修正错误

  • 借助 Copilot 对代码进行调试

  • 使用 VS Code 的调试器观察代码如何运作

There will be a point in every programmer’s career when their code isn’t doing what they want it to do. This has likely happened to you already, and, rest assured, it is a normal part of learning to program. How do we fix the code? Sometimes, changing the prompt or better decomposing the problem like you learned in earlier chapters is sufficient to fix the problem. But what do you do when you just can’t get Copilot to give you different or better code, and you can’t seem to figure out why the code you’ve been given doesn’t work properly?

在每位程序员的职业道路上,都会遇到代码不按他们所愿执行的时刻。这很可能已经发生在你身上,而且,请放心,这是学习编程过程中的一部分。我们如何修正代码?有时,改变提示词或者如前几章所学,更精确地分解问题就足以解决问题。但是,当你无法让 Copilot 提供不同或更好的代码,又或者无法搞清楚为什么你得到的代码不能正常工作时,你应该怎么办?

This chapter serves two purposes. The primary goal is to learn how to find errors (called bugs) in the code and fix them. To find those bugs, you’ll need to fulfill the second goal, which is gaining a deeper understanding of how your code works while you are running it.

本章节的目标有二。首要任务是学习如何发现代码中的错误(俗称 bug)并予以修复。而为了找到这些错误,你还需实现第二个目标,那就是在代码执行时深入理解其运作原理。

The good news is that having an error in your code is such a common occurrence for programmers that programming environments, like Visual Studio Code (VS Code), have tools to help us uncover what is going wrong. We’ll learn how to use that tool, called a debugger, in this chapter as well.

值得庆幸的是,代码出现错误对编程人员来说极为常见,以至于像 Visual Studio Code(VS Code)这种编程环境都配备了工具来帮助我们找出问题所在。本章我们将学习如何使用这种称为调试器的工具。

...

...


本章目录

  • 8.1 What causes errors (bugs)?
  • 8.2 How to find the bug
    • 8.2.1 Using print statements to learn about the code behavior
    • 8.2.2 Using VS Code’s debugger to learn about the code behavior
  • 8.3 How to fix a bug (once found)
    • 8.3.1 Asking Copilot to fix your bug via chat
    • 8.3.2 Giving Copilot a new prompt for the whole function
    • 8.3.3 Giving Copilot a targeted prompt for part of a function
    • 8.3.4 Modifying the code to fix the bug yourself
  • 8.4 Modifying our workflow in light of our new skills
  • 8.5 Applying our debugging skills to a new problem
  • 8.6 Using the debugger to better understand code
  • 8.7 A caution about debugging
  • 8.1 导致错误(bug)的原因是什么?
  • 8.2 如何定位 bug
    • 8.2.1 使用 print 语句来了解代码的行为
    • 8.2.2 使用 VS Code 的调试工具来了解代码的行为
  • 8.3 找到 bug 后如何修复
    • 8.3.1 通过对话让 Copilot 帮你修复 bug
    • 8.3.2 为整个函数给 Copilot 一个新的提示
    • 8.3.3 为函数的一部分给 Copilot 一个具体的提示
    • 8.3.4 自己修改代码修复 bug
  • 8.4 根据新技能调整我们的工作流
  • 8.5 将调试技巧应用到新问题上
  • 8.6 使用调试器更深入地理解代码
  • 8.7 调试的注意事项

本章正文略。如需阅读,请购买正版。


[译] [301] 初识函数

3.1 Functions

3.1 初识函数

Before we can learn about the details of writing a function, we need some insight into their purpose in software. Functions are small tasks that help accomplish larger tasks, which in turn help solve larger tasks, and so forth. We think you already have a lot of intuition about breaking apart large tasks into smaller tasks that you’ll be able to bring to bear in this example:

在我们深入探讨编写函数的具体细节之前,首先需要洞悉它们在软件开发中的重要性。函数就像是一系列小任务,它们汇聚起来共同完成更宏大的任务,进而解决更为复杂的问题。我们认为,你已经具备了将复杂任务拆解为简单子任务的直觉,这将在以下示例中得到应用:

Suppose that you’ve found a word search puzzle in the newspaper that you’d like to solve (See figure 3.1 for an example puzzle). In these kinds of puzzles, you’re looking for each of the words in the word list. The words can be found going from left to right, right to left, up to down, or down to up.

设想你偶然发现了一份报纸上的单词搜索游戏,正打算挑战一番(详见图 3.1,那里有一个游戏示例)。这类游戏中,你的任务是找出单词列表中的每一个单词。这些单词可能横向从左至右、从右至左,或者纵向从上至下、从下至上地隐藏着。

Figure 3.1 Example Wordsearch puzzle

图 3.1 单词搜索游戏示例

At a high level, your task is “find all of the words in the word search”. Unfortunately, that description of the task isn’t helpful on its own. It doesn’t tell us what steps we need to carry out to solve the problem.

在更宏观的层面,你的任务是“在单词搜索中找出所有单词”。然而,这样的任务描述实际上并不具备指导性。它并未指明我们应该如何具体操作以解决这一问题。

Try working on the problem right now for a couple minutes. How did you start? How did you break down the overall task to make it more achievable?

尝试现在就花几分钟来处理这个问题。你是从哪里着手的?你是如何将整个任务分解,以便更容易完成的?

One thing you might do is say, “OK, finding every word is a big task, but a smaller task is just finding the first word (CAT). Let me work on that first!” This is an example of taking a large task and breaking it into smaller tasks. To solve the entire puzzle, then, you could repeat that smaller task for each word that you need to find.

你可能会这样想:“好吧,找出所有单词确实是个庞大的挑战,但首先找出第一个单词(比如'CAT')则简单多了。我先从这个开始吧!”这是将庞大任务拆解为小任务的一个实际例子。为了完成整个字谜游戏,你只需对每个需要寻找的单词重复这个简化后的任务。

Now, how would we find an individual word, such as CAT? Even this task can be broken down further to make it easier to accomplish. For example, we could break it into four tasks: search for CAT from left to right, search for CAT from right to left, search for CAT from top to bottom, and search for CAT bottom to top. Not only are we making simpler and simpler tasks, but we’re also organizing our work into logical pieces. And, most importantly, as we’ll see throughout the chapter, it’s these simpler tasks whose code we’re going to ask Copilot to write and ultimately assemble into our complete programs.

那么,我们该如何寻找一个特定的单词,例如 "CAT"?其实,这个任务同样可以被进一步细化,以便我们更容易地完成。比如,我们可以将其拆分为四个子任务:从左至右查找 "CAT",从右至左查找 "CAT",从上至下查找 "CAT",以及从下至上查找 "CAT"。我们这样做,不仅使得任务变得简单,而且也让我们的工作更有条理。更为关键的是,正如我们在本章中将不断看到的,正是这些简化后的任务,我们将委托给 Copilot 来编写代码,最终整合成我们完整的程序。

Taking a large problem and dividing it into smaller tasks is called problem decomposition and is one of the most important skills in software design. We’ve dedicated an entire later chapter to it. For now, what is essential is knowing what’s too big of a task to ask Copilot. Asking it to make a new video game that’s a combination of Wordscapes meets Wordle is not going to work at all. However, you can get Copilot to write a function that’s important to solve a larger problem; for example, you might have a function that checks to see if the word provided by the player is present in a list of valid words.

将一个复杂问题拆分成若干小任务,这个过程称为问题分解,它是软件设计中极其关键的技能之一。我们将在后续章节中详细探讨这一主题。目前,你需要了解的是,对于Copilot来说,哪些任务太过庞大。譬如,要求它开发一款融合了Wordscapes和Wordle特点的全新视频游戏是行不通的。但是,你可以指导Copilot编写一些关键的函数,以帮助解决更广泛的问题。例如,你可以设计一个函数,用以验证玩家输入的单词是否包含在有效单词的列表中。

Copilot can solve that problem well and that function would help Copilot get closer to being able to solve the larger problem.

Copilot 能够有效处理这个难题,而这个函数将助 Copilot 一臂之力,使其更接近解决更复杂问题的目标。

3.1.1 The components of a function

3.1.1 函数的组成部分

The origin of the name “function” goes back to math where functions define the output of something based on an input. For example, if f(x) = x2 we can say that when x is 6, f(x) is 36. As programming functions also have expected output for a particular input, the name is quite apt for programming as well.

函数(function)这一术语源自数学,它描述了基于输入定义输出的数学概念。举个例子,f(x) = x^2 表示当 x 等于 6 时,f(x) 的结果为 36。同样,在编程中,函数也是根据给定的输入来产生预期的输出,因此“函数”这一称呼在编程领域同样适用。

As software engineers, we also like to think of functions as promises or contracts. If there is a function called “larger” and we’re told that it takes two numbers and gives us the larger of the two, we have faith that when we give the function the numbers 2 and 5, it will return the answer of 5. We don’t need to see how that function works to be able to use it, any more than we need to know how the mechanics of a car works to be able to use the brake pedal. Press the brake, the car slows down. Give the function two numbers, it gives us back the larger of the two.

作为软件工程师,我们倾向于将函数视作一种承诺或契约。假设存在一个名为“larger”的函数,它接受两个数字并返回其中较大的一个。我们确信,当我们向这个函数提供数字2和5时,它会返回5作为结果。我们在使用这个函数时,并不必要了解其内部工作原理,这与我们使用汽车刹车踏板而无需了解汽车机械构造是同样的道理。按下刹车,汽车便会减速。向函数传递两个数字,它便会返回较大的那个。

Every function in Python has a function header (also called a signature), which is the first line of code of the function. Given their ubiquitous nature, we’ll want to be able to read and write headers. The function header describes the name of the function and its inputs. In some other languages, it sometimes includes some information about what the output looks like, but in Python you have to find that elsewhere in the code.

在 Python 中,每个函数都拥有一个函数头,亦称为签名,它构成了函数代码的第一行。鉴于函数头的普遍性,我们自然需要学会如何阅读和书写它们。函数头详细说明了函数的名称和所需的输入参数。而在其他一些编程语言中,函数头有时还会包含输出结果的相关信息,但在 Python 中,这些信息需要在代码的其他部分寻找。

In Chapter 2, we wrote # comments to tell Copilot what to do. We can continue to use that approach if we want Copilot to generate a function. For example, we can use comments to ask Copilot to write a function that tells us which of two numbers is biggest:

在第二章,我们通过编写 # 注释来指导 Copilot 如何行动。若我们期望 Copilot 帮我们创建一个函数,这种方法仍然适用。举个例子,我们可以在注释中要求 Copilot 编写一个函数,用以比较两个数字并找出较大的那个:

# write a function that returns the larger of two numbers
# input is two numbers
# output is the larger of the two numbers
def larger(num1, num2):
    if num1 > num2:
        return num1
    else:
        return num2
# 编写一个比较两个数字大小并返回较大者的函数
# 输入为两个数字
# 输出为这两个数字中较大的一个
def larger(num1, num2):
    if num1 > num2:
        return num1
    else:
        return num2

As with the code in the last chapter, we just wrote the comments to prompt Copilot to give us the code. The function header has three main components: the keyword that tells Python that this is a function, the name of the function, and the inputs to the function. The word def denotes that this is creating (defining) a function. After def is the name of the function; that name should describe the behavior of the function as well as possible. The name of this function is larger. If it’s hard to name a function because it does a bunch of different things, that’s usually a clue that it’s too big a task for a single function, but more on that later.

与上一章的代码类似,我们刚才写下的注释是为了引导 Copilot 为我们生成代码。函数头包含三个核心部分:一个关键字,用来告诉 Python 这是一个函数;函数的名称;以及函数的输入参数。def 是创建(定义)函数的关键字。def 后面的是函数的名称,这个名字应该尽可能准确地描述函数的功能。这个函数的名字叫做 larger。如果给函数命名感觉困难,因为它涉及到多种不同的操作,这通常意味着这个任务对于一个函数来说太复杂了,我们稍后会进一步讨论这个问题。

What appears in the parentheses are the parameters. Parameters are how you provide information to a function that it needs to run. A function can have any number of parameters and some functions have no parameters. This function has two parameters named num1 and num2; there are two parameters because it needs to know the two numbers it’s comparing.

括号内的内容即为参数。参数是我们向函数传递执行所需信息的途径。函数可以包含任意数量的参数,某些函数也可能不包含任何参数。本例中的函数包含两个参数,分别命名为 num1num2;之所以有两个参数,是因为函数需要明确它所比较的两个数值。

There can be only one output of a function; the keyword to look for when determining what the function is returning is return. Whatever follows the word return is the output of the function. In this code, either num1 or num2 will be returned. Functions are not required to return anything (e.g., a function that prints a list to the screen has no reason to return anything) so if you don’t see a return statement, it isn’t necessarily a problem as the function may be doing something else (interacting with the user, for example) rather than returning something. Functions also either have to return something or not return something, they can’t return something in some cases and nothing in other cases.

函数的输出是唯一的;要了解函数返回的内容,关键是要找到 return 关键字。return 后面的部分即为函数的输出结果。在这段代码示例中,num1num2 将作为返回值。函数并不强制要求必须返回结果(例如,一个打印列表到屏幕的函数就无需返回任何内容),因此即使没有看到 return 语句,也不一定意味着有问题,函数可能在执行其他操作(例如与用户交互)。函数要么返回值,要么不返回,不能时而返回时而不返回。

Although we had Copilot generate this function using # comments, this approach is actually a lot of work for Copilot. It first has to get the header right, including figuring out how many parameters you need. Then it has to get the actual code of the function right.

尽管我们通过 # 注释让 Copilot 为我们生成了这个函数,但这种做法对 Copilot 来说确实颇为繁琐。它首先得精确地编写出函数头,这包括确定所需的参数数量。接着,它还得确保函数的代码正确无误。

There’s an alternate way to prompt Copilot to write the code for a function that may help it generate code more accurately and may help us better understand exactly what we want our function to do. It involves writing a docstring, and we’ll use docstrings to write functions for the majority of the book.

有一种不同的方式可以引导 Copilot 编写函数代码,这不仅能帮助它更精确地生成代码,还能让我们更清楚地了解我们期望函数实现的功能。这种方法就是编写文档字符串,我们将在本书中广泛使用文档字符串来定义函数。

Docstrings explain function behavior

Docstrings are how Python functions are described by programmers. They follow the function header and begin and end with three quotation marks.

Docstrings 解释函数的行为

Docstrings 是程序员用来描述 Python 函数的行为的。它们紧跟在函数头之后,以三个引号开始和结束。

By writing the header and docstring, you’ll make it easier for Copilot to generate the right code. In the header, you will be the one deciding on the name of the function and will provide the names of each parameter that you want the function to use. After the function header, you’ll provide a docstring which tells Copilot what the function does. Then, just as before, Copilot will generate the code for the function. Because we gave Copilot the function header, it will be able to learn from the header and is less likely to make mistakes.

编写函数头和文档字符串,能让 Copilot 更精准地编写代码。在函数头部分,你需要确定函数的名称,并指定你希望函数使用的各个参数的名称。紧接着函数头,编写一个文档字符串来向 Copilot 阐述函数的功能。之后,Copilot 会像之前一样生成函数的代码。由于我们已经提供了函数头,Copilot 能够基于此学习,减少出错的几率。

Here’s what the alternate approach would look like when writing that same larger function:

这里展示了如何采用另一种方式来编写那个 larger 函数:

def larger(num1, num2):
"""
num1 and num2 are two numbers.

Return the larger of the two numbers.
"""
    if num1 > num2:
        return num1
    else:
        return num2
def larger(num1, num2):
"""
num1 和 num2 是两个数字。

返回它们当中较大的那个。
"""
    if num1 > num2:
        return num1
    else:
        return num2

Notice that we wrote the function header as well as the docstring; Copilot supplied the body of the function.

请注意,我们不仅编写了函数头,还包括了文档字符串;而 Copilot 负责生成函数的具体内容。

3.1.2 Using a function

3.1.2 使用函数

Once we have a function, how do we use it? Thinking back to our f(x) = x2 analogy, how do we give the function a value of x that is 6 so that it returns to us 36? Let’s see how to do this with code by using that larger function we just wrote.

一旦我们创建了函数,我们应该如何运用它呢?让我们回想一下之前提到的 f(x) = x^2 的例子,我们如何设定 x 的值为 6,以便函数返回结果 36?接下来,我们将通过我们刚刚编写的这个更复杂的函数来演示这个过程。

The way to use a function is to call it. Calling a function means to invoke the function on specific values of parameters. These parameter values are called arguments. Each value in Python has a type, and we need to take care to give values of the proper type. For example, that larger function is expecting two numbers; it might not work in the expected way if we supply things that aren’t numbers. When we call a function, it runs its code and returns its result. We need to capture that result so that we can use it later, otherwise it will be lost. To capture a result, we use a variable, which is just a name that refers to a value.

要启用一个函数,关键在于调用它。所谓调用,就是根据特定的参数值来执行函数。这些参数值,我们称之为参数。在 Python 编程中,每个值都对应一个类型,我们必须确保传递正确类型的值。比如,那个我们之前编写的比较大小的函数,它需要两个数字作为输入;如果我们传入非数字的值,它可能就不会按预期执行。函数被调用后,会执行其内部代码并返回一个结果。为了之后能够使用这个结果,我们必须将其捕获,否则结果就会丢失。而捕获结果的方式,就是通过一个变量,它本质上是一个指向值的标识。

Here, we ask Copilot to call the function, store the result in a variable, and then print the result.

在这里,我们要求 Copilot 调用函数,将结果存储在一个变量中,然后打印结果。

# call the larger function with the values 3 and 5
# store the result in a variable called result
# then print result
result = larger(3, 5)
print(result)
# 调用 larger 函数,传入参数 3 和 5
# 将返回的结果赋值给变量 result
# 最后打印出 result 的值
result = larger(3, 5)
print(result)

The code correctly calls larger. Notice that it puts the two values we want compared after the opening parenthesis. When the function finishes, it returns a value that we assign to result. Then we print the result. If you run this program, you’ll see that the output 5 gets produced, and that’s because 5 is the larger of the two values that we asked about.

代码正确地调用了 larger 函数。注意,我们将想要比较的两个值放在了开放括号之后。当函数执行完毕后,它会返回一个值,我们将其赋给 result。然后我们打印出结果。如果你运行这个程序,你会看到输出结果是 5,这是因为在我们询问的两个值中,5 是较大的那个。

It’s okay if you aren’t comfortable with all the details here, but what we want you to be able to recognize is when a function is being called, e.g.,

如果你对这些细节还不太熟悉,那完全没关系,但我们希望你至少能够辨识出何时是在调用一个函数,比如:

larger(3, 5)

The general format for a function call is the following:

函数调用的一般格式如下:

function_name(argument1, argument2, argument3, ... )

So, when you see those parentheses right after a name, it means there’s a function call. Calling functions as we did here will be important to our workflow with Copilot, particularly in how we test functions to see if they are working properly. We’ll also need to call functions to get work done, as functions don’t do anything until we call them.

因此,当你看到某个名称后面紧跟着圆括号时,这表示正在进行函数调用。正如我们在此所做的,调用函数对我们与 Copilot 的协作流程极为重要,尤其是在我们对函数进行测试,以验证它们是否按预期工作时。此外,为了完成工作,我们也需要调用函数,因为函数在我们调用它们之前是不会执行任何操作的。

[译] [107] 大众对于 AI 编程助手的担忧

1.7 Societal concerns about AI code assistants like Copilot

1.7 大众对于 AI 编程助手的担忧

There's a lot of societal uncertainty right now about AI code assistants like Copilot. We thought we'd end the chapter with a few questions and our current answers. Perhaps you've been wondering about some of these questions yourself! Our answers may turn out to be hilariously incorrect, but they do capture our current thoughts as two professors and researchers who have dedicated their careers to teaching programming.

如今,社会对于像 Copilot 这样的 AI 代码助手充满了不确定性。我们想以几个问题及我们目前的答案结束这一章,你可能也在思考其中的一些问题!虽然我们的回答将来可能会显得有些荒谬,但它们确实反映了我们作为两位专注于编程教学的教授和研究者当前的想法。

Q: Are there going to be fewer tech and programming jobs now that we have Copilot?

Q: 现在有了 Copilot,技术和编程岗位会减少吗?

A: Probably not. What we do expect to change is the nature of these jobs. For example, we see Copilot as being able to help with many tasks typically associated with entry-level programming jobs. This doesn't mean that entry-level programming jobs go away, only that they change as programmers are able to get more done given increasingly sophisticated tools.

A: 不太可能。我们预期的是这些岗位性质的变化。比如,我们认为 Copilot 能帮助执行许多通常与初级编程岗位相关的任务。这并不意味着初级编程岗位将消失,只是随着程序员能够借助越来越先进的工具完成更多任务,这些岗位的性质将发生改变。

Q: Will Copilot stifle human creativity? Will it just keep swirling around and recycling the same code that humans have already written, limiting introduction of new ideas?

Q: Copilot 会扼杀人类的创造性吗?它会不会只是在不断地循环和回收人类已经编写的代码,限制新观点的引入?

A: We suspect not. Copilot helps us work at a higher level, further removed from the underlying machine code, assembly code, or Python code. Computer scientists use the term abstraction to refer to the extent that we can disconnect ourselves from low level details of computers. Abstraction has been happening since the dawn of computer science, and we don't seem to have suffered for it. On the contrary, it enables us to ignore problems that have already been solved and focus on solving broader and broader problems. Indeed, it’s been the advent of better programming languages that have facilitated better software – software that powers Google search, Amazon shopping carts, and Mac OS weren’t written (and likely could not have been written) when we only had assembly!

A: 我们认为不会。Copilot 使我们能够在更高层面上进行工作,远离了底层机器码、汇编语言或 Python 代码。计算机科学家用“抽象”这一术语来描述我们与计算机底层细节脱离的程度。抽象自计算机科学诞生之初就在进行,我们并没有因此遭受损失。相反,它让我们能够忽略那些已经解决的问题,专注于解决越来越广泛的问题。事实上,正是更好的编程语言的出现,推动了更高质量软件的开发——那些驱动 Google 搜索、亚马逊购物车和 Mac OS 的软件,并非在我们仅有汇编语言时编写的(可能也确实无法编写)!

Q: I keep hearing about ChatGPT. What is it? Is it the same as Copilot?

Q: 我一直在听人说 ChatGPT,这是什么?它和 Copilot 相同吗?

A: It's not the same as Copilot, but it's built on the same technology. Rather than focus on code, though, ChatGPT focuses on knowledge in general. And as a result, it has insinuated itself into a wider variety of tasks than Copilot. For example, it can answer questions, write essays, and even do well on a Wharton MBA exam [7]. Education will need to change as a result: we cannot have people ChatGPT’ing their ways to MBAs! The ways in which we spend our time in worthwhile ways may change. Will humans keep writing books, and in what ways? Will people want to read books knowing that that they were partially or fully written by AI? There will be impacts across industries, including finance, health care, and publishing [8]. At the same time, there is unfettered hype right now, so it can be difficult to separate truth from fiction. This problem is compounded by the simple truth that no one knows what's going to happen here long-term. In fact, there’s an old adage coined by Roy Amara (known as Amara’s Law) that says, “We tend to overestimate the effect of a technology in the short run and underestimate the effect in the long run.” As such, we need to do our best to be tuned into the discussion so that we can adapt accordingly.

A: ChatGPT 和 Copilot 不相同,但它们是基于同一种技术构建的。不过,与专注于编码的 Copilot 不同,ChatGPT 更多地关注于广泛的知识领域。因此,它已经融入了比 Copilot 更多样化的任务中。例如,它可以回答问题、撰写论文,甚至能够在沃顿商学院的 MBA 考试中取得好成绩 [7]。这意味着教育需要随之而变:我们不能让人们只靠使用 ChatGPT 就获得 MBA!我们花费时间的方式可能需要变化。人们会继续写作书籍吗?以何种方式?当人们知道书籍可能部分或完全由 AI 编写时,他们还会愿意阅读吗?这将对金融、医疗保健、出版等行业产生影响 [8]。同时,目前存在大量的过度炒作,因此很难区分事实与虚构。这个问题因为长期发展未知而更加复杂。实际上,有一句由 Roy Amara 提出的老话(阿马拉法则)指出:“我们倾向于高估技术在短期内的影响,而低估其长期影响。”因此,我们需要尽最大努力保持对讨论的关注,以便我们能够适当地适应。

In the next chapter, we’ll get you started with using Copilot on your computer so you can get up and running writing software.

在下一章中,我们将引导你在计算机上启动并使用 Copilot,让你能够迅速开始编写软件。

[译] [305] 使用 Copilot 设计函数的流程

3.5 The cycle of design of functions with Copilot

3.5 使用 Copilot 设计函数的流程

Designing functions with Copilot involves the following cycle of steps (see figure 3.3):

借助 Copilot 设计函数时,遵循的循环步骤如下(参见图 3.3):

  1. Determine the desired behavior of the function.
  2. Write a prompt that describes the function as clearly as possible.
  3. Allow Copilot to generate the code.
  4. Read through the code to see if it seems reasonable.
  5. Test the code to see if it is correct.
    1. If the code is correct after multiple tests, move on.
    2. If the code is incorrect, move to step 2 and edit the prompt.
  1. 确定函数想要实现的行为。
  2. 构建一段尽可能清晰的提示词来描述函数。
  3. 让 Copilot 生成相应的代码。
  4. 通读代码,判断其是否看起来比较合理。
  5. 对代码进行测试,确认其是否正确。
    1. 经过多次测试,若代码无误,便可以继续。
    2. 若发现代码有误,回到第 2 步,对提示词进行调整。

Figure 3.3 General editing cycle with Copilot. This assumes you define a reasonable function.

图 3.3 与 Copilot 协作的常规编辑流程。前提是你定义的函数是合理的。

We won’t learn how to do step 4 until the next chapter, but we bet you can already recognize when the code is blatantly wrong. For example, Copilot might give you only comments to fill the body of the function. Comments don’t do anything —- they are not code! —- so a bunch of comments with no other code is clearly not the right thing to do. Or it might just write a single line “return -1”. Or, our personal favorite, “Your code here”. Copilot learned that one from us professors when we provide students partial code and ask them to write the rest with “Your code here”. Those are all obviously incorrect, but in the next chapter, we’ll go over how to read code, so you can more quickly spot when more complicated code is incorrect and, perhaps more importantly, see where and how to fix it. In later chapters, we’ll keep expanding on this cycle to include effective debugging practices, and we’ll keep practicing how to improve prompts.

我们直到下一章才会学习如何执行第 4 步,但我们相信你现在已经能够识别出明显的代码错误了。比如,Copilot 可能只会用注释来填充函数的主体。注释本身不执行任何操作——它们并非代码!——因此,只有一系列注释而无其他代码显然不是正确的结果。它也可能仅仅返回一行 “return -1”。或者,我们特别喜欢用的,“Your code here”。Copilot 是从我们这些教授这里学到这个表达的,当我们向学生提供部分代码并要求他们完成剩余部分时,我们会说 “Your code here”。这些显然都是错误的,但在下一章中,我们会讲解如何阅读代码,这样当更复杂的代码出错时,你就能更快地发现。或许更重要的是,你能发现错在哪里以及如何修复。在后续章节中,我们将持续扩展这一循环,涵盖有效的调试技巧,并且持续练习如何改进提示词。

[译] [404] 本章小结

Summary

本章小结

  • We need to be able to read code to determine whether it is correct, test it effectively, and write our own code when needed.
  • The Copilot Labs Extension can provide line-by-line explanations of code to explain to you what the code is doing.
  • Python has built-in functions such as max, input, and print that we call just like we call our own functions.
  • A variable is a name that refers to a value.
  • An assignment statement makes a variable refer to a specific value.
  • An if statement is used to have our programs make decisions and proceed down one of multiple paths.
  • A string is used to store and manipulate text.
  • A method is a function associated with a particular type.
  • A list is used to store and manipulate a general sequence of values (like a sequence of numbers or a sequence of strings).
  • Each value in a string or list has an index; indexing starts at 0, not 1.
  • Strings are immutable (not changeable); lists are mutable (changeable).
  • 能够阅读代码是判断代码是否正确、有效测试代码,以及在需要时编写自己代码的关键能力。
  • Copilot 实验室扩展能提供代码的逐行解释,向你阐明代码的具体操作。
  • Python 具有像 maxinputprint 这样的内置函数,我们调用这些函数的方式就像调用我们自己定义的函数一样。
  • 变量是引用值的名字。
  • 赋值语句让变量指向了一个具体的值。
  • if 语句用于让我们的程序进行决策,并选择多条路径中的一条继续执行。
  • 字符串用来存储和操作文本。
  • 方法是与特定类型相关的函数。
  • 列表用来存储和操作一系列值(比如数字序列或字符串序列)。
  • 字符串或列表中的每个值都对应一个索引,这个索引从 0 开始计数,而不是从 1 开始。
  • 字符串是不可变的(不可以修改);而列表是可变的(可以修改)。

[译] [1100] 第十一章 展望未来

11 Future Directions

第十一章 展望未来

This chapter covers

本章内容概要

  • Using prompt patterns to write and explain code
  • Current limitations and future directions of generative AI tools
  • 应用提示词模式编写与解释代码
  • 生成式 AI 工具的现状限制及未来发展趋势

In this final chapter, we want to give you a glimpse of the creative ways people are currently using generative AI tools like GitHub Copilot and ChatGPT. For example, it’s possible to make Copilot Chat ask you the questions, rather than the other way around. And it’s possible to make Copilot take on a different persona to be even more helpful to your current programming task. We’re going to keep this brief, and it’s not clear how much of this will become standard practice, but we want to take this opportunity to demonstrate the power of being creative with these new tools. We’ll also talk about some of the current limitations of generative AI tools (you’ve seen some of them already in this book!) and offer our thoughts on what may be next.

在本书的终章,我们期望展示人们当前如何创造性地使用生成式 AI 工具,例如 GitHub Copilot 和 ChatGPT。比如,可以实现让 Copilot Chat 主动提出问题,而非单向交流。更进一步,还能让 Copilot 采取不同的身份,以便更好地适应你的编程任务。我们将保持简洁,尽管现在还不确定这些方法会在何种程度上成为常规操作,但我们希望借此机会展现出利用这些新工具进行创造性思维的潜力。此外,我们还将讨论生成式 AI 工具目前的一些限制(本书中已有所涉及),并就可能的未来趋势提供我们的见解。


本章目录

  • 11.1 Prompt patterns
    • 11.1.1 Flipped interaction pattern
    • 11.1.2 Persona pattern
  • 11.2 Limitations and future directions
    • 11.2.1 Where Copilot (currently) struggles
    • 11.2.2 Is Copilot a new programming language?
  • 11.1 提示词模式
    • 11.1.1 翻转互动模式
    • 11.1.2 角色扮演模式
  • 11.2 局限与未来走向
    • 11.2.1 Copilot 目前遇到的挑战
    • 11.2.2 Copilot 能被视为一种新的编程语言吗?

本章正文略。如需阅读,请购买正版。


[译] [202] 设置你的系统

2.2 Getting your system setup

2.2 设置你的系统

This is actually a four-step process. To streamline this chapter, we’re just providing the main steps for this process below. However, there are more detailed instructions available in the following locations:

这个过程实际上包含四个步骤。为了让本章节内容更加流畅,我们下面只列出了这个过程的主要步骤。不过,你可以在以下地点找到更为详尽的指南:

  • GitHub’s documentation: https://docs.github.com/en/copilot/getting-started-with-github-copilot

  • GitHub 相关文档:https://docs.github.com/en/copilot/getting-started-with-github-copilot

  • In the appendix of this book, there are detailed instructions for setting up your system for both PC and macOS. As these websites might change after we write this book, we encourage you to use a combination of the link above and the appendix together.

  • 本书附录中详细说明了针对 PC 和 macOS 系统设置的步骤。由于这些网站可能在本书完成后更新,我们建议你同时参考上述链接和附录进行操作。

  • In the online book forum, you can ask for help and see a list of answers to frequently asked questions.

  • 在线书籍论坛里,你可以请求帮助,同时可以查阅一个常见问题及其解答的列表。

The primary steps you’ll need to accomplish are as follows:

你需要完成的主要步骤如下:

1. Set up your GitHub account and sign up for Copilot

1. 创建并设置你的 GitHub 账户,注册 Copilot

a. Go to GitHub.com and sign up for a GitHub account: https://github.com/signup

a. 前往 GitHub.com,注册一个 GitHub 账户:https://github.com/signup

b. Go into your settings in GitHub and enable Copilot. This is the point where you’ll either need to verify you are a student or sign up for the 30-day free trial (available at the time of writing).

b. 在 GitHub 的设置中启用 Copilot。此步骤中,你将需要证明你是一名学生或者选择注册 30 天免费试用(文章编写时提供)。

2. Install Python

2. 安装 Python

a. Go to the website, www.Python.org/downloads/

a. 访问 www.Python.org/downloads/ 网站。

b. Download and install the latest version of Python (3.11.1 at the time of writing).

b. 下载并安装 Python 的最新版本(本书编写时为 3.11.1)。

3. Install Visual Studio Code

3. 安装 VS Code

a. Go to the website, https://code.visualstudio.com/Download, Select the main download for your Operating System (e.g., Windows Download or Mac Download).

a. 访问 https://code.visualstudio.com/Download,针对你的操作系统选择主要下载版本(比如,Windows 版本或 Mac 版本)。

b. Download and install the latest version of Visual Studio Code.

b. 下载并安装 VS Code 的最新版本。

4. Install Visual Studio Code Extensions (please read through https://code.visualstudio.com/docs/editor/extension-marketplace for details)

4. 安装 VS Code 扩展(详细信息请参考 https://code.visualstudio.com/docs/editor/extension-marketplace

a. Python (by Microsoft). Follow the instructions at https://code.visualstudio.com/docs/languages/python to set up the Python extension correctly (specifically selecting the correct interpreter).

a. Python(由 Microsoft 提供)。根据 https://code.visualstudio.com/docs/languages/python 的指导正确配置 Python 扩展(特别注意选择正确的解释器)。

b. GitHub Copilot (by GitHub)

b. GitHub Copilot(由 GitHub 提供)

c. GitHub Copilot Labs (by GitHub). Note that Copilot Labs is not needed for the majority of the book, so if you encounter a waiting list or it has changed from the time of writing, please do not be discouraged.

c. GitHub Copilot Labs(由 GitHub 提供)。请留意,本书绝大多数部分不需使用 Copilot Labs,因此如果你遇到等待名单或自本书编写以来该服务有所更改,也无需感到沮丧。

We know that the steps here are brief, if you encounter any issues, we encourage you to consult the resources mentioned earlier for more detailed setup instructions.

我们了解到这些步骤介绍得非常简要。如果你在执行过程中遇到任何困难,我们建议你参考之前提到的资源获取更详细的设置指南。

[译] [400] 第四章 理解 Python 代码(上)

4 Reading Python Code: Part 1

第四章 理解 Python 代码(上)

This chapter covers

本章内容概要

  • Why knowing how to read code is important
  • How to ask Copilot to explain code
  • Using functions to break a problem into smaller sub-problems
  • Using variables to hang on to values
  • Using if statements to make decisions
  • Using strings to store and manipulate text
  • Using lists to collect and manipulate many values
  • 为什么懂得如何阅读代码很重要
  • 如何让 Copilot 解释代码
  • 利用函数把问题拆分成若干更小的子问题
  • 利用变量保存数据
  • 利用 if 语句进行决策
  • 利用字符串来存储及处理文本
  • 利用列表来收集和处理多个值

In Chapter 3, we used Copilot to write several functions for us. What are they good for? Maybe our money_made function could be part of a stock trading system. Maybe our is_strong_password function could be used as part of a social network website. Maybe our best_word function could be used as part of some Scrabble AI. Overall, we’ve written some useful functions that could be part of larger programs. And we did this without writing much code ourselves or, indeed, understanding what the code even does.

在第三章,我们借助 Copilot 编写了几个有用的函数。这些函数能用在哪里呢?例如,我们的 money_made 函数可能成为股票交易系统的一部分;is_strong_password 函数可以用于社交网站的密码强度检测;best_word 函数则可能应用于 Scrabble 游戏的 AI 中。归根结底,我们编写了若干可以成为大型程序组成部分的实用函数,而这一切几乎不需要我们自己动手编写代码,或者完全理解这些代码的具体作用。

However, we believe that you need to understand at a high-level what code does and that this will require some time to learn, hence we’ve split this across two chapters. In this chapter, we’ll explain why reading code is important and introduce you to a Copilot labs feature that can help you understand the code. After that, we'll dive into the top ten programming features you’ll need to be able to recognize in order to read most basic code produced by Copilot. We’ll do the first five in this chapter and the remaining five in the next chapter. Don't worry: you've actually been informally introduced to all ten already—we're just going to deepen your understanding of each one.

然而,我们相信你需要从高层次理解代码的作用,这将需要一些时间来学习,因此我们将这部分内容分为两章。在本章中,我们将解释为什么阅读代码是重要的,并介绍一个 Copilot 实验室的特性,该特性能帮助你理解代码。之后,我们会深入讨论十个编程特性,这些特性对于阅读 Copilot 产出的大多数基础代码来说是必需认识的。我们会在本章介绍前五个特性,在下一章介绍剩余的五个。不用担心:你其实已经非正式地了解了这十个特性——我们只是要深化你对它们每一个的理解。

[译] [102] 本书涉及的技术

1.2 About the technology

1.2 本书涉及的技术

We’ll be using two main technologies in this book: Python and GitHub Copilot.

在这本书中,我们将重点介绍两项主要技术:Python 和 GitHub Copilot。

Python is a programming language. It’s a way to communicate with a computer. People use it to write all kinds of programs that do useful things, like games, interactive websites, visualizations, apps for file organization, automating routine tasks, and so on.

Python 是一门编程语言,是与计算机进行交流的途径。人们使用它编写各种程序来完成有用的事情,如游戏、交互式网站、数据可视化、文件管理应用以及自动化常规任务等。

There are other programming languages, too, like Java, C++, Rust, and many others. Copilot works with those, too, but at the time of writing, it works really well with Python. Python code is a lot easier to write compared to many other languages (especially assembly code). Even more importantly, Python is easy to read. After all, we’re not going to be the one writing the Python code. Our AI assistant is!

还有其他的编程语言,如 Java、C++、Rust 等。Copilot 也支持这些语言,但截至目前,它对 Python 的支持尤其出色。与许多其他语言(尤其是汇编语言)相比,编写 Python 代码要简单得多。更为重要的是,Python 更易于阅读。毕竟,编写 Python 代码的将不是我们,而是我们的 AI 助手!

Computers don’t actually know how to read and run Python code. The only thing computers can understand is something called machine code, which looks even more ridiculous than assembly code as it is the binary representation of the assembly code (yep, just a bunch of 0s and 1s!). Behind the scenes, your computer takes any Python code that you provide and converts it into machine code before it runs, as shown in Figure 1.1.

实际上,计算机并不懂得如何读取和执行 Python 代码。计算机唯一能够理解的是称为机器码的内容,这种码相比汇编代码来说更加难以理解,因为它是汇编代码的二进制表现形式(没错,仅仅是一连串的 0 和 1!)。在幕后,你的计算机将接收你所提供的任何 Python 代码,并在执行之前把它转换成机器码,正如图 1.1 所展示的那样。

Figure 1.1 Your Python program goes through several steps before you see the output on your screen

图 1.1 在你的屏幕上看到输出之前,你的 Python 程序需要经历几个步骤

Copilot, your AI Assistant

Copilot,你的 AI 助手

What is an AI Assistant? An AI Assistant is an Artificial Intelligence (AI) agent that helps you get work done. Maybe you have an Amazon Alexa device at home, or an iPhone with Siri-—these are AI assistants. Those ones help you order groceries, learn the weather, or determine that, yes, the woman who played Bellatrix in the Harry Potter movies really was in Fight Club. An AI assistant is just a computer program that responds to normal human inputs like speech and text with human-like answers.

什么是AI 助手?AI 助手是一种人工智能(AI)代理,它帮助你完成任务。你可能在家中使用亚马逊的 Alexa 设备,或拥有一部集成了 Siri 的 iPhone——这些都属于 AI 助手。这类助手能帮你订购生鲜、查询天气,或是确认那位在《哈利·波特》电影中饰演贝拉特里克斯的女演员确实也参演了《搏击俱乐部》。AI 助手只不过是一个响应人类语音和文本输入的计算机程序,并以类人的方式给出答案。

Copilot is an AI Assistant with a specific job: it converts English into computer programs. (It can also do a whole lot more as we will soon see.) There are other AI assistants like Copilot, including CodeWhisperer, Tabnine, and Ghostwriter. We chose Copilot for this book by a combination of the quality of code that we have been able to produce, stability (it has never crashed for us!), and our own personal preferences. We encourage you to check out other tools as well when you feel comfortable doing so.

Copilot 是一款具有特定任务的 AI 助手:它能将英语转换成计算机程序(我们很快就会看到,它还能做更多的事情)。除了 Copilot,还有其他一些类似的 AI 助手,如 CodeWhisperer、Tabnine 和 Ghostwriter。我们选择 Copilot 作为本书介绍的工具,这是基于我们所生成的代码质量、其稳定性(它从未发生过崩溃!)以及我们个人的偏好。我们也鼓励你在感觉合适时,探索其他的工具。

How Copilot works behind the scenes-—in 30 seconds

Copilot 幕后工作原理——仅用 30 秒

You can think of Copilot as a layer between you and the computer program you’re writing. Instead of writing the Python directly, you simply describe the program you want in words—this is called a prompt—and Copilot generates the program for you.

你可以把 Copilot 想象成你和你正在编写的计算机程序之间的一个中介层。不必直接编写 Python,你只需用语言描述你想要实现的程序——这称为提示词——之后 Copilot 就会为你生成相应的程序。

The brains behind Copilot is a fancy computer program called a large language model, or LLM. An LLM stores information about relationships between words, including which words make sense in certain contexts, and uses this to predict the best sequence of words to respond to a prompt.

Copilot 的智能核心是一个称为大型语言模型(LLM)的复杂计算机程序。大型语言模型存储了关于单词之间关系的信息,包括在特定上下文中哪些单词是有意义的,并利用这些信息来预测对一个提示词最佳的单词序列。

Imagine that we asked you what the next word should be in this sentence: "The person opened the ________". There are many words that you could fill in here, like “door” or “box” or “conversation,” but there are also many words that would not fit here, like “the” or “it” or “open.” An LLM takes into account the current context of words to produce the next word, and it keeps doing this until it has completed the task.

设想我们询问你在这个句子中:“这个人打开了______。” 下一个词应当是什么。这里有很多可能的填词,如“门”、“盒子”或“谈话”,但同时也存在许多不适合此处的词,如“这”、“它”或“打开”。大型语言模型会根据当前词汇的上下文来预测下一个合适的词,并持续这个过程直到任务完成。

Notice that we didn't say anything about Copilot having an understanding of what it is doing. It just uses the current context to keep writing code. Keep this in mind throughout your journey: only we know whether the code that's generated actually does what we intended it to do. Very often it does, but you should always exercise healthy skepticism regardless.

请注意,我们并未提及 Copilot 是否真的理解其正在进行的操作。它仅根据当前上下文继续编写代码。在你的编程旅程中,请始终牢记这一点:只有我们自己知道生成的代码是否确实按照我们的意图行事。虽然很多时候它确实能够实现这一点,但无论如何,你都应当始终保持一种健康的怀疑心态。

Figure 1.2 will give you an idea of how Copilot goes from prompt to program.

图 1.2 将为你揭示 Copilot 是如何从提示词生成程序的过程。

Figure 1.2 Going from prompt to program with Copilot

图 1.2 使用 Copilot 从提示词生成程序

You might wonder why Copilot writes Python code for us and not machine code directly. Isn’t Python an expendable intermediate step now? Well, no, and the reason is that Copilot is going to make mistakes. And if it’s going to make mistakes, and we need to get them fixed, then it’s a lot easier to do that with Python than with machine code.

你可能好奇,为什么 Copilot 会为我们编写 Python 代码,而不是直接生成机器码。Python 难道现在不只是一个可有可无的中间步骤吗?实际上,并非如此,这是因为 Copilot 可能会出错。既然它可能犯错,我们需要对这些错误进行修正,那么相比于机器码,使用 Python 进行修正无疑要简单得多。

In fact, virtually no one checks if the machine code produced from Python is correct. This is partially because of the determinism of the Python language specification. One could imagine a future where Copilot conversations are so accurate that inspecting the Python is unnecessary, but we’re a long way from that.

实际上,几乎无人检查由 Python 转换而成的机器码是否准确。这在一定程度上是因为 Python 语言规范的决定性。可以设想,将来 Copilot 的对话准确率如此之高,以致于审查 Python 代码将变得非必需,但距离那一天我们还有很长的路要走。

[译] [300] 第三章 设计函数

3 Designing Functions

第三章 设计函数

This chapter covers

本章内容概要

  • Functions in Python and their role in designing software
  • Reasonable tasks for Copilot to solve
  • The standard workflow when interacting with Copilot
  • Examples of writing good functions using Copilot
  • Python 函数以及它们在软件设计中的作用
  • 适合由 Copilot 来解决的合理任务
  • 与 Copilot 交互的标准工作流程
  • 利用 Copilot 编写优良函数的实例

One of the hardest challenges for programming novices is to know what a reasonable task is to give to Copilot so that it finds a good solution. If you give it too big a task, it will often fail in spectacular ways that can be extremely difficult to fix. What, then, is a reasonable task?

对编程初学者而言,识别出哪些任务适合交给 Copilot,以期待其提出有效解决方案,是一个巨大的挑战。如果赋予的任务过于庞大,Copilot 往往会以引人注目的方式失败,且这类失败的修正通常非常困难。那么,什么样的任务才算是合理的呢?

This question is important for our use of Copilot but goes far beyond it. Human programmers struggle with complexity, too. If experienced software engineers try to write code to solve a problem that’s too complex without breaking it down into smaller, more solvable sub-parts, they often have trouble as well. The solution for humans has been to use something called a function, whose job is to perform one task. There are various rules of thumb for how to write a reasonable function in terms of number of lines of code, but fundamentally these rules try to capture writing something that (1) performs a single task and (2) is not so complex that it’s hard to get right.

这个问题不仅对我们使用 Copilot 至关重要,而且意义深远。人类程序员也面临着处理复杂性的挑战。经验丰富的软件工程师如果试图直接编写代码来解决一个复杂问题,而不是将其分解为更小、更易于解决的子问题,往往也会陷入困境。人们的解决方法是采用所谓的函数,它的职责是完成一个任务。虽然有许多关于如何编写合理函数的经验规则,例如代码的行数,但这些规则的根本目的是要确保所写的内容(1)执行一个单一任务,并且(2)其复杂度不至于难以正确执行。

For students who learned to program the old-fashioned way, without Copilot, they’ll have been struggling with syntax in code that’s 5-10 lines long for about a month before we typically introduce functions and, at that point, it’s a natural segue to tell them not to write more code in a single function than they can handle testing and debugging. As you all are learning to work with Copilot rather than syntax directly, our job in this chapter is to teach you about functions and what are reasonable, and unreasonable, tasks to ask Copilot to solve in a single function.

对于通过传统方法学习编程且未使用 Copilot 的学生,他们在我们通常开始介绍函数的大约一个月前,可能已经与 5-10 行长的代码语法作斗争。在此时,向他们自然地指出不要在单一函数中写入超过他们测试和调试能力范围的代码是很合适的。鉴于你们正学习如何利用 Copilot 而不是直接处理语法,本章的使命是向你们介绍有关函数的知识,并说明在单个函数中要求 Copilot 解决的哪些任务是合理的,哪些是不合理的。

To help you gain perspective on functions, we’ll provide you with a number of examples in this chapter. For those examples, you’ll see us employ the central workflow of interacting with Copilot; specifically, the cycle of writing prompts, receiving code from Copilot, and testing to see if that code is correct. In the functions produced by Copilot, you’ll begin to see the core programming tools, like loops, conditionals, and lists, that we’ll expand on in the next chapter.

为了加深你对函数的理解,我们将在本章提供若干示例。在这些示例中,你将体验到与 Copilot 互动的核心流程;即编写提示词、从 Copilot 接收代码、测试代码是否正确的循环过程。通过 Copilot 创建的函数,你将初步了解像循环、条件语句和列表这样的核心编程工具,这些是我们下一章将要详细探讨的内容。

[译] [106] 我们所需的技能

1.6 The skills we need

1.6 我们所需的技能

If Copilot can write our code, explain it, and fix bugs in it, are we just done? Do we just tell Copilot what to do and celebrate our pure awesomeness?

如果 Copilot 能够编写代码、解释代码并修复里面的错误,那我们是不是就可以高枕无忧了?我们是不是只需要告诉 Copilot 我们想要什么,然后就可以庆祝我们的杰出成就了?

No. It's true that some of the skills that programmers rely upon (writing correct syntax, for example) will decrease in importance. But other skills remain critical. For example, you cannot throw a huge task at Copilot like,

不,并非如此。虽然一些程序员所依赖的技能(例如,编写正确的语法)的重要性将会减少,但其他技能仍然非常关键。例如,你不能简单地把一个庞大的任务扔给 Copilot,比如说,

"Make a video game. Oh, and make it fun." Copilot will fail. Instead, we need to break down such a large problem into smaller tasks that Copilot can help us with. And how do we break a problem down like that? Not easily, it turns out. This is a key skill that humans need to hone in their conversations with tools like Copilot, and a skill that we will teach throughout the book.

“创建一个视频游戏,并确保它富有乐趣。” Copilot 将难以胜任。相反,我们需要将这一庞大的任务分解成 Copilot 能帮忙的小任务。那么,我们怎样才能做到这一点呢?事实证明,这并非易事。这是人们在与 Copilot 这类工具交流时必须培养的关键技能,也是本书将要教授的技能。

Other skills, believe it or not, may take on even more importance with Copilot than without. Testing code has always been a critical task in writing code that works. We know a lot about testing code written by humans, because we know where to look for typical problems. We know that humans often make programming errors at the boundaries of values. For example, if we wrote a program to multiply two numbers, it’s likely that we’d get it right with most values but maybe not for when one value is 0. What about code written by AI, where twenty lines of flawless code could hide one line so absurd that we likely wouldn't expect it there? We don’t have experience with that. We need to test even more carefully than before.

信不信由你,有些技能在使用 Copilot 的时候可能比不用时更为重要。测试代码始终是编写可靠代码的关键任务。我们对于测试人类编写的代码有很多了解,因为我们知道应该在哪里寻找常见的问题。我们知道,人们在处理值的边界条件时经常会出错。例如,如果我们编写一个程序来乘两个数,大部分时候我们可能会做得很好,但对于其中一个值是 0 的情况可能就不行。那么,对于 AI 编写的代码,如果在二十行完美的代码中隐藏着一行我们完全不会预料到的荒唐代码呢?我们对此尚无经验。因此,我们需要比之前更加细致地进行测试。

Finally, some required skills are entirely new. The main one here is called prompt engineering, which involves how to tell Copilot what to do. When we're asking Copilot to write some code, we're using a prompt to make the request. It's true that we can use English to write that prompt and ask for what we want, but that alone isn't enough. We need to be very precise if we want Copilot to have any chance of doing the right thing. And even when we are precise, Copilot may still do the wrong thing. In that case, we need to first identify that Copilot has indeed made a mistake, and then tweak our description to hopefully nudge it in the right direction. In our experience, seemingly minor changes to the prompt can have outsized effects on what Copilot produces.

最后,一些必须掌握的技能是全新的。其中最关键的一项称为 “提示工程”,这涉及到如何准确告诉 Copilot 需要它做什么。当我们向 Copilot 请求编写代码时,我们通过一个 “提示词” 来提出这一请求。虽然我们可以用英语来编写这个提示词并表达我们的需求,但仅仅这样做是不足够的。如果我们希望 Copilot 有可能正确执行任务,我们就需要极为精确。即使我们已经足够精确,Copilot 还是可能会犯错。在这种情况下,我们首先需要确认 Copilot 确实出现了错误,然后调整我们的描述,希望能将其引导至正确的方向。根据我们的经验,即使是看似微不足道的提示词变动,也可能对 Copilot 生成的结果产生极大的影响。

In this book, we will teach you all of these skills.

在本书中,我们将教授你所有这些必备的技能。

[译] [104] Copilot 还可以做什么

1.4 What else can Copilot do for us?

1.4 Copilot 还可以做什么

As we’ve seen, we can use Copilot to write Python code for us starting from an English description of what we want. Programmers use the word syntax to refer to the symbols and words that are valid in a given language. So, we can say that Copilot takes a description in English syntax and gives us back code in Python syntax. That's a big win, because learning programming syntax has historically been a major stumbling block for new programmers. What kind of bracket— [, (, or { —am I supposed to use here? Do I need indentation here or not? What's the order that we're supposed to write these things: x and then y, or y and then x?

如我们所观察到的,借助 Copilot,我们能够从一段英语描述开始编写 Python 代码。程序员用 “语法” 一词来描述在特定语言中有效的符号与词汇。因此,我们可以说 Copilot 能够将英语语法的描述转换成 Python 语法的代码。这是一大进步,因为历史上,学习编程语法常常是新手程序员的一大障碍。我在这里应该使用哪种括号——[、(、还是 { 呢?这里需要缩进吗?我们书写这些元素的顺序应该是怎样的:是先 x 后 y,还是先 y 后 x?

Such questions abound and let's be honest: it's uninteresting stuff. Who cares about this when all we want to do is write a program to make something happen? Copilot can help free us from the tedium of syntax. We see this as an important step to help more people successfully write programs, and we look forward to the day when this artificial barrier is completely removed. For now, we still need Python syntax, but at least Copilot helps us with it.

这类问题层出不穷,坦白说:它们实在是些枯燥无味的细节。当我们的目标仅仅是编写程序以实现某些功能时,这些细节又有谁会去在意呢?Copilot 能够帮我们摆脱语法的枯燥。我们视这为帮助更多人成功编写程序的关键一步,并期待有一天,这种人为设定的障碍能够被彻底移除。目前,我们依然需要掌握 Python 语法,但至少有了 Copilot 的协助,这一任务变得不那么艰巨了。

But that's not all Copilot can do. Here are some associated—and no less important—tasks where Copilot can help us:

但 Copilot 的功能远不止这些。以下是一些同等重要的相关任务,在这些任务上 Copilot 同样能提供助力:

  • Explaining code. When Copilot generates Python code for us, we’ll need to determine whether that code does what we want. Again, as we said above, Copilot is going to make mistakes. We’re not interested in teaching you every nuance of how Python works (that’s the old model of programming). We are going to teach you how to read Python code to gain an overall understanding of what it does. But we’re also going to use the feature of Copilot that explains code to you in English. When you finish with this book and our explanations, you’ll still have Copilot available to help you understand that next bit of gnarly code that it gives you.

  • 代码解析。当 Copilot 为我们生成 Python 代码时,我们需要判断这些代码是否符合我们的预期。正如之前所述,Copilot 可能会犯错。我们不打算深入讲述 Python 的每一个细节(那属于过时的编程方式)。我们 教你如何阅读 Python 代码,以便全面理解其功能。同时,我们还将利用 Copilot 的代码解释功能,用英语为你解释代码。阅读完这本书及我们的解释之后,你仍然可以依靠 Copilot 帮助你理解它为你生成的那些复杂代码。

  • Making code easier to understand. There are many different ways to write code to accomplish the same task. Some of them may be easier to understand than others. Copilot has a tool that can reorganize your code to make it easier for you to work with. For example, code that’s easier to read is often easier to enhance or fix when needed.

  • 使代码更加易于理解。完成同一任务的编码方法有众多,其中一些方法可能比其他方法更容易理解。Copilot 提供了一种工具,可以重组你的代码,让它更加便于使用。例如,易于阅读的代码在需要增强或修复时,通常也更加简单直接。

  • Fixing bugs. A bug is a mistake made when writing a program that can result in the program doing the wrong thing. Sometimes, you’ll have some Python code, and it almost works, or works almost always but not in one specific circumstance. If you’ve listened to programmers talk, you may have heard the common story where a programmer would spend hours only to finally remove one = symbol that was making their program fail. Not a fun few hours! In these cases, you can try the Copilot feature that helps to automatically find and fix the bug in the program.

  • 修复 bug。所谓 “bug”,指的是编程时出现的错误,这些错误可能会让程序运行不正常。有时,你可能会遇到这样的情况:手中的 Python 代码几乎可以正常工作,或者在大部分情况下运行良好,但在某些特定情境下却无法正常执行。如果你有听过程序员之间的交流,可能会听到一个他们常谈的经历:有的程序员可能需要花费几小时的时间,最后发现只需去除一个 = 符号,就能解决让程序出错的问题。那确实是数小时的挑战和折磨!在这种情况下,你可以尝试使用 Copilot 功能,它能自动帮助你找出并修复程序中的 bug。

[译] [808] 本章小结

Summary

本章小结

  • Debugging is an important skill that includes finding errors in code and then correcting them.
  • Print statements can be an effective way of learning about what is happening in your code.
  • The VS Code debugger is another way of learning what is happening in your code that provides powerful features for monitoring how variables change as the code executes.
  • Once an error is uncovered, there are multiple ways to help Copilot fix the error for you, but if that fails, you can often fix the code directly.
  • Our workflow of designing functions now includes debugging, and with the skill of debugging, you are more apt to write the software you want.
  • Outside of debugging, the VS Code debugger can be a powerful tool in learning more about how the code works.
  • 调试是一项关键能力,它包括寻找并修正代码中的错误。
  • 利用打印语句能有效地帮助你理解代码的运行情况。
  • VS Code 调试器是学习代码执行细节的另一种方法,提供了监控变量随代码执行而变化的强大特性。
  • 当错误被发现时,有多种方式可以辅助 Copilot 帮你修正错误,但如果这些方法不成功,你通常可以直接对代码进行修改。
  • 我们的函数设计流程如今包含了调试环节,拥有调试技能使你更加能够创建出符合自己愿望的软件。
  • 在调试的范畴之外,VS Code 调试器还是一个强有力的工具,助你更深入地理解代码的工作机制。

[译] [201] 为接下来的学习做好准备工作

2.1 Time to setup your computer to keep learning

2.1 为接下来的学习做好准备工作

Learning how to write software requires that you perform the task of actually writing software, not just reading about it. If this were a book on how to play guitar, would you keep reading it without ever trying to play the guitar? We thought not. Reading this book without following along and trying it out yourself would be like watching a marathon runner finish the race and thinking you’re ready to go run one yourself. We’ll stop with the analogies, but seriously, you need to get your software installed and running before we go farther.

学习编写软件的过程中,你需要亲自动手编程,而不仅仅是通过阅读来理解。如果这是一本教你如何弹吉他的书,你肯定不会在从未尝试弹吉他的情况下就继续阅读下去,对吧?我们认为你不会。仅仅是阅读而不亲自实践,就像观看马拉松选手冲过终点,然后误认为自己也已经准备好参加马拉松一样。我们不再用类比来说明了,但请你认真对待:在我们进一步深入学习之前,你需要先安装并运行好所需的软件。

What scares us the most right now is that we just hit the most common point where novices, even those eager to learn programming, tend to fail, and we really want to see you succeed. Now, you might be thinking, “Wait, really? We’re just getting started.” Yes, that’s exactly the point. In Leo’s popular Coursera course about learning Java programming [1], can you guess the point when most new leaners leave? Is it the challenging assignment that involves plotting earthquake markers on the globe in real time at the end of the course? No. It’s actually the warmup assignment where the learners must set up their programming environment. As such, we understand this could be a hurdle for you. We hope that with this not-so-subtle nudge, we can help you achieve all the goals you had in mind when you bought this book. It all starts with installing the software.

我们当前最为担心的是,我们恰好碰到了许多新手,包括那些热衷于学习编程的人经常失败的一个关键点,我们非常期待看到你的成功。此刻,你可能在想:“真的吗?我们才刚开始。”是的,这正是问题所在。在 Leo 主讲的热门 Coursera Java 编程课程 [1] 中,你能猜出大多数新学者选择离开的时刻吗?是否是课程最后那个挑战,即实时在全球地图上绘制地震标记的任务?不,并不是。其实是最初的热身作业,即学习者必须搭建他们的编程环境。因此,我们理解这可能对你来说是一大障碍。我们希望通过这一不那么含蓄的提示,帮助你实现购书时心中的每一个目标。一切都始于安装软件。

2.1.1 Overview of the software in your programming environment

2.1.1 编程环境软件概览

To set up and use Copilot easily, we’ll be installing the software editing tools used by novices and software engineers alike. The tools you will be using are GitHub, Copilot, Python, and Visual Studio Code. Of course, if you already have all these tools installed, jump to section “Our first programming problem” later in this chapter.

为了便于设置和使用 Copilot,我们将介绍初学者和软件工程师都会用到的软件编辑工具。这些工具包括 GitHub、Copilot、Python 以及 Visual Studio Code。当然,如果你已经安装了所有这些工具,那么请直接跳转到本章后面的“我们的第一个编程问题”部分。

GitHub Account

GitHub 账号

GitHub is an industry standard tool for developing, maintaining, and storing software. We won’t be using GitHub in this book, however. We’re signing up for GitHub simply because you’ll need an account to get access to Copilot.

GitHub 是用于开发、维护和存储软件的行业标准工具。然而,我们在本书中不会使用 GitHub。注册 GitHub 的唯一原因是你需要一个账户以便访问 Copilot。

Signing up for a GitHub account is free but, at the time of writing, they charge for Copilot. If you are a student, they will waive that fee. If you aren’t a student, as of writing, you can get a 30-day free trial.

虽然注册 GitHub 账户是免费的,但在撰写本文时,他们对使用 Copilot 是收取费用的。如果你是学生,可以免除这笔费用。如果你不是学生,根据目前的情况,你可以享受 30 天的免费试用。

You might ask why they charge for the service, and there’s a good answer. It’s expensive to build the GPT3 models (imagine thousands of computers running for a year to build the model) and there are costs they incur by providing predictions from the model (many machines are receiving your input, running it through the model, and generating your output). If you are not ready to commit to using Copilot, you could make a calendar note for roughly 25 days from the day you sign up and if you aren’t using Copilot at that time, just cancel. If, on the other hand, you have succeeded in learning how to write software with Copilot and are using it to improve your productivity at work or just as a hobby, keep it.

你可能会质疑为什么需要为这项服务支付费用,这有一个充分的理由。构建 GPT3 模型的成本极高(想象一下,需要成千上万台计算机运行一年时间来构建这个模型),并且他们在提供模型的预测服务时也会面临一定的成本(众多服务器接受你的输入,在模型中处理,并输出结果)。如果你对是否要长期使用 Copilot 还在犹豫,可以在注册后大约 25 天设一个日历提醒,如果到那时你还没有使用 Copilot,就可以选择取消订阅。反之,如果你已经利用 Copilot 成功地学会了编程,并且它正在提高你的工作效率或者成为了你的一项爱好,那么就继续使用它。

Python

Python

Really any programming language would have worked for this book, but we picked Python because it is one of the most popular programming languages in the world and is the language we teach in our introductory courses at our universities. As we said in Chapter 1, compared to other languages Python is easier to read, easier to understand, and easier to write. For this book, Copilot will primarily be generating the code, not you. However, you will want to be able to read and understand the code generated by Copilot and Python is great for that.

事实上,本书适合应用任何一种编程语言,但我们选择了 Python,因为它是全球最受欢迎的编程语言之一,并且是我们在大学基础课程中教授的语言。如第一章所述,与其他语言相比,Python 更加易于阅读、理解和书写。本书中,主要是由 Copilot 而非你来生成代码。不过,你需要能够阅读和理解 Copilot 生成的代码,Python 在这方面表现优异。

Visual Studio Code (VSCode)

Visual Studio Code(VS Code)

You can use any text editor to program. However, if you want a nice programming environment where you can write code, easily get suggestions from Copilot, and run your code, VSCode is our preferred tool. VSCode is used by novices learning software and is well liked by students [2]. It’s also used globally by professional software engineers, which means you’ll be able to work and learn while using this environment after finishing the book.

你可以使用任何文本编辑器进行编程。但如果你追求一个既能编写代码、轻松获得 Copilot 建议,又可以运行代码的良好编程环境,VS Code 是我们的首选工具。VS Code 不仅受到学习软件开发的新手欢迎,也非常受学生群体的青睐 [2]。它同样被世界各地的专业软件工程师所使用,这意味着在你完成本书的学习之后,可以继续使用这一环境进行工作和进一步的学习。

For VSCode to work for this book, you’ll need to install a few extensions that enable working with Python and using Copilot, but one of the great things about VSCode is that it makes it easy to install those extensions.

为了配合本书的使用,你需要在 VS Code 中安装几个扩展,这将使其支持 Python 编程和使用 Copilot,但值得一提的是,VS Code 的一个巨大优点在于它让安装这些扩展变得非常简单。

[译] [206] 本章小结

Summary

本章小结

  • You installed Python and VSCode and set up Copilot so you are able to work along with the book and start writing code yourself!
  • The VSCode interface has areas for file management, code editing, and running code that will be used throughout the book.
  • Prompts are how we tell Copilot to generate code and, when written carefully, can be a highly effective way of creating software.
  • Data analysis is a common programming task and .csv files are a common way for storing data to be processed by computers.
  • Copilot may generate code that requires you to install additional Python modules.
  • Copilot is a powerful tool that is capable of producing code that is as sophisticated (or more) as that produced by college students finishing their first programming course.
  • 你已经完成了 Python 和 VS Code 的安装,并且配置好了 Copilot,现在你可以按照书中的指导开始自己动手编程了!
  • VS Code 界面包括文件管理、代码编辑和代码运行等区域,这些在整本书中都会用到。
  • 提示词是我们命令 Copilot 生成代码的方式,如果细致入微地编写,能够成为创造软件的一种极为有效的手段。
  • 数据分析是常见的编程任务之一,而 .csv 文件则是存储待计算机处理数据的一种常见格式。
  • Copilot 生成的代码可能需要你额外安装一些 Python 模块。
  • Copilot 是一个强大的工具,其生成的代码在复杂性方面能与完成第一门编程课的大学生产出的代码相匹敌,甚至更胜一筹。

[译] [700] 第七章 分解问题

7 Problem Decomposition

第七章 分解问题

This chapter covers

本章内容概要

  • Understanding problem decomposition and why we need to do it
  • Using top-down design to carry out problem decomposition and write programs
  • Writing an authorship identification program using top-down design
  • 理解问题的分解及其重要性
  • 运用自顶向下的设计方法来分解问题并编写程序
  • 使用自顶向下的设计编写一个作者识别程序

In Chapter 3, we talked about why we shouldn’t ask Copilot to solve big problems. Imagine what could happen if we asked Copilot, “Write a program to determine the author of a book.”

在第三章,我们探讨了为什么不应该要求 Copilot 去解决大型问题。试想一下,如果我们对 Copilot 说 “编写一个判定书籍作者的程序”,会发生什么情况?

In the best case, we’d get a canned program with all of the decisions made for us. That program may not match what we wanted. Part of the power of being a programmer is customizing what we’re creating. To do this, we need to feed small sub-problems to Copilot and assemble those solutions into a program of our own. Even if we didn’t want to customize anything, what would we do if the program from Copilot had flaws? It would be difficult for us to fix a large program that we don’t understand.

在最理想的情况下,我们将获得一个全套的程序,其中所有决策都已经代我们做出。但这个程序可能并不完全满足我们的要求。程序员这个角色的魅力在于能够定制我们所创造的东西。为了实现这一点,我们需要向 Copilot 提出具体的子问题,并将这些解决方案组合成我们自己的程序。或许我们不想定制任何内容,但如果 Copilot 编写的程序存在瑕疵怎么办?修复一个我们不了解的大型程序会非常困难。

In the worst case, Copilot wouldn’t do anything useful. We observe this sometimes when Copilot gives us comments again and again but never provides us with real code.

在最糟糕的情况下,Copilot 给不出任何有用的东西。我们有时候会发现这种情况,比如 Copilot 只是反复输出注释,而不是提供实质性的代码。

In this chapter, we will learn how to break large problems into smaller ones. We can then use Copilot to solve each of the small sub-problems, thereby solving the large problem that we ultimately care about solving.

在本章中,我们将学习如何将大问题分解成小问题。然后我们可以使用 Copilot 来解决每个小的子问题,从而解决我们最终关心的大问题。


本章目录

  • 7.1 Problem decomposition
  • 7.2 Small examples of top-down design
  • 7.3 Authorship identification
  • 7.4 Authorship identification using top-down design
  • 7.5 Breaking down the process sub-problem
    • 7.5.1 Figuring out the signature for the mystery book
  • 7.6 Summary of our top-down design
  • 7.7 Implementing our functions
  • 7.8 Going further
  • 7.1 问题分解
  • 7.2 自顶向下设计的示例
  • 7.3 作者身份识别
  • 7.4 应用自顶向下设计识别作者身份
  • 7.5 分解子问题过程
    • 7.5.1 解析神秘书籍的签名
  • 7.6 自顶向下设计的总结
  • 7.7 实现具体函数
  • 7.8 更进一步

本章正文略。如需阅读,请购买正版。


[译] [306] 与 Copilot 配合创建优秀函数的示例

3.6 Examples of creating good functions with Copilot

3.6 与 Copilot 配合创建优秀函数的示例

In this section, we’re going to write a bunch of functions with Copilot. We’ll be coding them entirely in Copilot to help you see the cycle of function design we just described. Although our goal in this chapter isn’t to help you read code just yet, we will be seeing programming features (sometimes called constructs) in the solutions that are very common in code (e.g., if statements, loops) so we’ll point those out when we see them. Then in Chapter 4, we’ll say more about how to read this code in more detail.

在本节中,我们将使用 Copilot 编写一系列函数。我们会全程利用 Copilot 进行编程,以便您能更好地理解我们先前提到的函数设计流程。尽管本章的目标并不是立刻教会您阅读代码,但我们还是会在解决方案中看到各种编程特性(有时也称作编程结构),这些特性在代码中非常常见(例如,if 语句、循环),因此我们会在看到它们时指出。然后在第四章,我们将更详细地讨论如何读懂这些代码。

Many of the functions we’re about to work on are unrelated to each other. For example, we’ll start with a function about stock share prices and move to functions about strong passwords. You typically wouldn’t store unrelated stuff like this in the same Python file. But as we’re just exploring different examples of good functions, please feel free to store all functions in the same Python file, perhaps named function_practice.py or ch3.py.

我们接下来要编写的函数,大多数是彼此不相关的。比如,我们会先编写一个关于股票价格的函数,接着是关于强密码的函数。通常,你不会把这些风马牛不相及的代码放在同一个 Python 文件里。不过,既然我们现在只是在探索如何编写好的函数的各种示例,那么就请随意把所有的函数都放在一个 Python 文件里,可以命名为 function_practice.py 或者 ch3.py

3.6.1 Dan’s stock pick

3.6.1 阿丹的股票选择

Dan is an investor in a stock called AAAPL. He purchased 10 shares for $15 each. Now, each of those shares is worth $17. Dan would like to know how much money he has made on the stock.

阿丹投资了一支名为 AAAPL 的股票。他曾以每股15美元的价格购入了10股。如今,这些股票的每股价值已经上升到17美元。阿丹渴望了解他在这项投资中究竟获利多少。

Remember that we want to make our function as general as possible. If the only thing our function could do is calculate this exact AAAPL situation, it wouldn’t be that useful in general. Sure, it would help Dan right now, but what about when AAAPL’s share price changes again, or when he is interested in another stock entirely?

要记住,我们希望函数具有尽可能广泛的通用性。倘若我们的函数仅适用于计算 AAAPL 此刻的涨跌情况,那么它的实用性就大打折扣了。诚然,这在当下对阿丹有所帮助,但如果 AAAPL 的股价再次波动,或者他开始关注其他股票,那怎么办?

A useful general function here would take three parameters, all of which are numbers. The first parameter is the number of shares purchased, the second is the share price when the shares were purchased, and the third is the current share price. Let’s call this function money_made, since it’s going to determine how much money we’ve made or lost on the stock. In general, you want to name your function as an action word or words that describe what your function is doing. With that, we have enough to write the function header:

一个实用的通用函数应当接收三个参数,这三个参数均为数字类型。首个参数代表购买的股票数量,第二个参数代表购买时每股的股价,而第三个参数则是目前每股的股价。我们将这个函数命名为 money_made,因为它的作用是计算我们在股票投资上的盈亏。在命名函数时,通常会选用一个动词或短语来描述函数的功能。如此,我们就可以撰写函数的声明部分了:

def money_made(num_shares, purchase_share_price, current_share_price):

Now we need a docstring. In the docstring, we need to explain what each parameter is for by using its name in a sentence. We also need to include what the function is supposed to do.

接下来,我们要撰写一个文档字符串。在这个文档字符串里,我们要通过一句话来阐明每个参数的作用,并在其中提及参数的名称。同时,我们也要指明这个函数旨在实现的功能。

Adding our docstring, here is the full prompt that we provide to Copilot:

加入我们的文档注释,这是我们给 Copilot 的完整提问:

def money_made(num_shares, purchase_share_price, current_share_price):
"""
num_shares is the number of shares of a stock that we purchased.
purchase_share_price is the price of each of those shares.
current_share_price is the current share price.
Return the amount of money we have earned on the stock.
"""
def money_made(num_shares, purchase_share_price, current_share_price):
"""
num_shares 是我们购买的股票数量。
purchase_share_price 是每股的购买价格。
current_share_price 是当前的股价。
返回我们在股票上赚取的金额。
"""

After typing that prompt, go to the next line and press the tab key. Copilot will fill in the code for the function. Don’t worry that the code gets indented: the code of functions is supposed to be indented, and in fact it’s an error if it isn’t!

输入完那段提示词后,按下回车键,然后按Tab键继续。Copilot会自动填充函数的代码。请注意,代码的缩进是必要的,如果代码没有正确缩进,那将是一个错误!

Here’s what we got from Copilot:

这就是我们从Copilot那里获得的代码:

	return num_shares * (current_share_price - purchase_share_price)

This code seems sensible. In the parentheses it figures out the difference between the current price and the purchase price (the - is used for subtraction), and then it multiplies that by the number of shares that we have (the * is used for multiplication). Inspecting code like this is a useful skill, and we’ll get serious about it in the next chapter. Another useful skill is testing the function.

这段代码看起来合情合理。它利用括号计算出当前股价与购买时股价的差价(使用 - 表示减法),再将这个差价乘以我们所持有的股份数量(使用 * 表示乘法)。细致检查此类代码能够锻炼我们的编程能力,我们将在接下来的章节中深入探讨。同时,对函数进行测试也是一项重要的技能。

To test the function, we call it using various inputs and observe the output in each case. We could do this by asking Copilot to call the function and then running our program, much as we did with our “larger” function. We could then ask Copilot to change the function call by asking it to call the function with a different input, and run our program again, repeating as many times as needed.

要测试这个函数,我们可以采用不同的输入来调用它,并观察各个情况下的输出结果。我们可以请求Copilot来调用这个函数,接着执行我们的程序,正如我们之前对待那个“较为复杂”的函数一样。之后,我们可以请Copilot通过改变输入参数来重新调用函数,并再次执行程序,这样的测试可以根据需要反复进行。

However, we find it easier and more convenient to call the function ourselves from an interactive window.

不过,我们认为直接通过交互式窗口来调用函数更加轻松便捷。

This way, we can call the function as many times as we like without going through Copilot at all, and without cluttering up our program with stuff we’re going to delete anyway. To try this interactive approach, select/highlight all of the code of the function, then press <Shift>+Enter (you can access a similar interactive session by selecting the text, right-clicking, and choosing Run Selection/Line in Python Window, but the guidance here is if you use <Shift>+Enter). Figure 3.4 shows what this looks like if you select the text of the function and press <Shift>+Enter.

这样,我们就可以随意调用函数,完全不需要通过 Copilot,也避免了在我们的程序里填满最终要删除的冗余代码。要尝试这种交互方式,选中/高亮函数的所有代码,然后按下 <Shift>+Enter(此外,通过选中文本,右键并选择“在 Python 窗口中运行选择/行”也能进入这种的交互式会话,但此处建议使用 <Shift>+Enter)。图 3.4 展示了如果你选中函数文本并按下 <Shift>+Enter 会是什么样子。

Figure 3.4 Running Python in an interactive session in the Terminal of VSCode. Note the >>> at the bottom of the Terminal.

图 3.4 在 VS Code 终端里运行 Python 交互式会话。请注意终端底部的 >>> 提示符。

At the bottom of the resulting window, you will see three greater-than symbols >>>. This is called a prompt, and you’re allowed to type Python code here. (This prompt has nothing to do with the kind of prompt that we use when interacting with Copilot.) It will show us the result of the code that we type right away, which is convenient and fast.

在结果窗口底部,你会看到三个大于号 >>>。这个叫做 “提示符”,你可以直接在这里编写 Python 代码。(注意,这个提示符和我们与 Copilot 互动时所用的提示词是两码事。)它会立刻展示我们代码的执行结果,非常方便和迅速。

To call our money_made function, we need to provide three arguments, and they will be assigned left to right to the parameters. Whatever we put first will be assigned to num_shares, whatever we put second will be assigned to purchase_share_price, and whatever we put third will be assigned to current_share_price.

要调用我们的 money_made 函数,我们需要提供三个数值,它们将按照顺序分别赋值给函数的参数。我们最先给出的数值将赋值给 num_shares,第二个数值将赋值给 purchase_share_price,而第三个数值将赋值给 current_share_price

Let’s try this! At the prompt, type the following and press enter (or <shift>+Enter). Don’t type the >>> as that’s already there, and we are including it throughout the book to make it clear where we are typing. Please see figure 3.5 for an example of running the function in the terminal at the Python prompt.

来试试吧!在提示符处输入上述内容,然后按回车(或 <shift>+Enter)。无需输入>>>,它已经显示在那里了,我们全书都这样标注,以便清晰指示输入点。图 3.5 展示了在终端窗口的 Python 提示符下运行函数的实际效果。

>>> money_made(10, 15, 17)

You’ll see an output of

你将看到输出结果如下:

20

Is 20 correct? Well, we bought 10 shares, and each of them went up $2 (from $15 to $17), so we did make $20. Looks good!

20 这个答案正确吗?嗯,我们买了 10 股,每股涨了 2 美元(从 15 美元涨到 17 美元),所以我们确实赚了 20 美元。看起来不错!

Figure 3.5 Calling the money_made function from Python prompt in the VSCode Terminal.

图 3.5 在 VS Code 终端窗口的 Python 提示符下调用 money_made 函数。

We’re not done testing, though. When testing a function, you want to test it in various ways, not just once. All one test case tells you is that it happened to work with the particular input values that you provided. The more test cases we try, each testing the function in a different way, the more confident we are that our function is correct.

测试工作尚未结束。对函数进行测试时,应该采用多种方法,而不仅仅局限于一次。单一的测试用例仅能说明函数在你给出的特定输入值下有效。我们进行的测试用例越多样化,每种用例都从不同角度检验函数,我们对该函数正确性的把握就越大。

How do we test this function in a different way? We’re looking for inputs that are somehow a different category of input. One not-so-good test right now would be to say, “what if our stock went from $15 to $18, instead of $15 to $17?”. This is pretty much the same test as before, and chances are that it will work just fine.

我们怎样才能用另一种方法来测试这个函数呢?我们需要寻找的输入,应该是另一种不同“类型”的数据。举个例子,我们把刚才股价从15美元涨到17美元的情况,换成15美元涨到18美元,这就不算是一个太好的测试。这样的测试和之前的太相似了,很可能函数同样能够很好地处理。

A good idea is to test what happens when the stock actually loses money. We expect to get a negative return value in this case. And it appears that our function works just fine with this category of test. Here’s our function call and the output that is returned to us:

测试一下股票实际发生亏损的情形是个不错的主意。我们预期在这种情况下会得到一个负收益。结果表明,我们的函数对这种类型的测试也能够很好地应对。以下是我们进行的函数调用及其返回结果:

>>> money_made(10, 17, 15)
-20

What other tests can we do? Well, sometimes a stock price doesn’t change at all. We expect 0 in this case. Let’s verify it:

我们还能做些什么其他的测试吗?比如说,有时候股票的价格可能完全没有变化。在这种情况下,我们预期收益应该是0。让我们来验证这一点:

>>> money_made(10, 15, 15)
0

Looks good!

看起来不错!

Testing is a combination of science and art. How many categories of things are there to test? Are these two calls really two different categories? Have we missed any categories? You will improve your testing ability through practice, and we’ll spend all of Chapter 6 talking about testing. For now, it looks like our money_made function is doing its job.

测试既是一门科学,也是一门艺术。我们究竟需要测试哪些种类的问题?这两个函数调用真的代表了两个截然不同的测试类别吗?我们是否遗漏了某些类别?通过不断的实践,你的测试技巧将得到提升,而在第六章中,我们将全面探讨有关测试的话题。就目前而言,我们的money_made函数似乎正在有效地履行其职责。

It’s possible for a function to use variables (rather than just its parameters) in its code, and we want to show you an example of that now so that you’re ready when you see Copilot doing it.

函数在其代码中可以使用变量(而不仅仅是其参数),我们现在就给你展示一个例子,以便将来遇到 Copilot 这样做时你能够应对自如。

Here’s an equivalent way to write the code for our money_made function:

这里展示了另一种编写 money_made 函数代码的等价方式:

	price_difference = current_share_price - purchase_share_price
	return num_shares * price_difference

This may even be easier to read for you: first it figures out the difference in share price, and then it multiplies that by the number of shares. We encourage you to test this version to help convince yourself that it is still correct.

这样的表达或许更加清晰:先计算出每股的差价,再将其与持股数量相乘。建议你同样对这个版本进行测试,以确保它也是正确的。

3.6.2 Leo’s password

3.6.2 小李子的密码

Leo is signing up for a new social network website called ProgrammerBook. He wants to make sure that his password is strong.

小李子正在注册一个名为 ProgrammerBook 的新兴社交网络网站,他希望确保自己的密码足够安全。

Leo starts with a modest definition of what it means for a password to be strong: it’s strong if it’s not the word ‘password’ and not the word ‘qwerty.’ (Those are terrible passwords, for sure, but in reality, we have to do way better than this definition to ensure that our password is strong!)

小李子对 “强密码” 的定义颇为低调:他觉得只要密码不是 “password” 或 “qwerty”,就算是一个强密码了。(这两个密码无疑是极差的,然而实际上,若真想确保密码安全,我们还需做到比这更好的水平!)

A helpful function would be one that takes a proposed password and tells us whether it is strong or not.

这里我们需要一个实用的函数,它能够接收一个拟定的密码,并判断其强度是否达标。

Unlike our previous functions in this chapter, we’re not dealing with numbers here. The parameter, the password to check, is text. And the return value is supposed to indicate some yes/no result. We need new types!

不同于本章前面介绍的函数,本次我们处理的不是数字,而是文本类型的密码。同时,返回值用于表示一个肯定或否定的结果。这就涉及到了新的数据类型。

The Python type for text is called a string. There are zillions of possible strings, because we can use a string to store whatever text we want. And the Python type for a yes/no result is called a Boolean or bool. A bool has only two values: True or False.

在 Python 编程语言中,文本是通过一个名为字符串的数据类型来表示的,它包含了几乎无限多的可能组合,因为我们可以用它来保存任何我们想要的文本。对于表示是或否的结果,Python 使用了一个特殊的数据类型,我们称之为布尔值或者 bool。布尔值只包含两种状态:True 和 False。

Alright! We’re ready to prompt Copilot. For functions that return bool (True/False) values, we usually name the function as has_x, or is_x, or using some other verb that implies a true/false result.

好的!现在我们可以开始引导 Copilot。通常,对于返回布尔值(True/False)的函数,我们会采用 has_xis_x 这样的命名方式,或者使用其他能够隐含真/假结果的动词。

def is_strong_password(password):
"""
A strong password is not the word 'password'
and is not the word 'qwerty'.

Return True if the password is a strong password, False if not.
"""
def is_strong_password(password):
"""
一个强密码应当避免使用 ‘password’ 或 ‘qwerty’ 这样的常见词汇。

判断密码是否符合强密码标准,是则返回 True,否则返回 False。
"""

In response, here’s what Copilot wrote for us:

作为回应,以下是 Copilot 为我们编写的代码:

return password != 'password' and password != 'qwerty'

Look over that code for a moment. That != operator is the ‘not equal’ operator, and each string that doesn’t count as a strong password is in single quotation marks. We’re going to use quotation marks every time we work with a string value. Again, we’ll talk more about how to read code in the next chapter, but for now we hope that the code makes a fuzzy sort of sense: it looks like it’s checking that the password isn’t password and isn’t qwerty, which matches our definition of a strong password.

我们来仔细查看这段代码。!= 是 “不等于” 操作符,每个不符合强密码标准的字符串都用单引号括起来。我们在操作字符串值时,每次都要使用引号。关于代码阅读的更多细节,我们将在下一章进行深入讨论,但目前希望代码能给您一个大致的印象:它似乎在检查密码不是 password 也不是 qwerty,这正符合我们对强密码的定义。

Now let’s make sure that this function does indeed work, by making a few test calls.

现在,我们就通过几轮测试来检验这个函数是否真的有效。

We’ll start by testing the passwords that are not strong. We had better get back False for each of these—and we do!

我们先来测试那些强度不足的密码。对于这些密码,我们期望得到的响应是 False,而且结果正是如此!

>>> is_strong_password('password')
False

>>> is_strong_password('qwerty')
False

Good! Now, let’s try a strong password. We need to see True here:

很好!接下来,我们来测试一个强密码。我们期望这里的结果为 True

>>> is_strong_password('N3w Y0rk J375')
True

Good stuff! We now see that the function sometimes returns False and other times returns True, so at least it’s not always returning False.

好极了!现在我们看到函数有时候返回 False,有时候返回 True,至少它不是一直返回 False

Let’s try one more:

再尝试一个例子:

>>> is_strong_password('Leo')
True

Well, technically True is correct, but wow is ‘Leo’ a bad password. We should really have done better with our definition of a strong password. Let’s change our prompt to give a more reasonable definition of what it means for a password to be strong.

嗯……虽然返回 True 是附合预期的,但严格来说 “Leo” 算是个糟糕的密码。我们本可以对我们所谓的 “强密码” 给出一个更恰当的定义。让我们调整提示词,给强密码下一个更合理的定义。

Here's our next prompt and what Copilot gave us.

接下来是我们的下一段提示词,以及 Copilot 提供的代码。

def is_strong_password(password):
"""
A strong password has at least one uppercase character,
at least one number, and at least one special symbol.

Return True if the password is a strong password, False if not.
"""
    return any(char.isupper() for char in password) and
             any(char.isdigit() for char in password) and
             any(not char.isalnum() for char in password)
def is_strong_password(password):
"""
强密码应至少包含一个大写字母,
至少一个数字,以及至少一个特殊符号。

判断密码是否符合强密码标准,是则返回 True,否则返回 False。
"""
    return any(char.isupper() for char in password) and
             any(char.isdigit() for char in password) and
             any(not char.isalnum() for char in password)

That for char in password that shows up a few times here is a way for Python to consider each character of the password. It enables something called a loop, which is a key topic that we’ll study in Chapter 5.

这几处 for char in password 是 Python 用来遍历密码中每个字符的语法。它开启了循环这一功能,这是我们在第五章将要详细学习的核心概念。

It’s of course possible that you didn’t get the same code from Copilot as we did. But based on the code that we got, it looks like the first piece is looking for any uppercase character, the second piece is looking for any digit, and the third is looking for something that's not an isalnum.

当然,你从 Copilot 那里得到的代码可能和我们的不一样。不过,就我们所获得的代码来看,第一部分似乎是在检查是否有任何大写字母,第二部分在寻找任意数字,而第三部分则在寻找那些不属于 isalnum 方法定义的字符。

Maybe isalnum means ‘is alphanumeric’. Does that include special symbols and, if so, which ones?

也许 isalnum 的意思是 “is alphanumeric(是否为字母和数字)”。那么它是否包括特殊符号呢?如果包括的话,又是哪些特殊符号呢?

Let's test this function to see how well we’re doing.

我们来测试这个函数,评估一下它的效果如何。

>>> is_strong_password('Leo')
False

>>> is_strong_password('N3w Y0rk J37$')
True

>>> is_strong_password('N3w Y0rk J375')
True

Uh oh—we got the wrong answer for that third password. It looks pretty strong, but it's not, because it lacks a special symbol. Our function returned True, but that’s the wrong return value: it was supposed to return False.

哎呀——第三个密码的结果出错了。尽管它看起来很安全,但实际上并不达标,因为它缺少了一个特殊符号。我们的函数给出了 True 的返回值,但这是错误的;它本应该返回 False

The code that Copilot gave us is wrong. Maybe the code that you got worked fine, but we didn’t get so lucky and it’s important we know how to identify (like we just did) when the code is wrong and then fix it. It’s not important to know why it’s wrong right now, but if you are curious about why our prompt may have given us the wrong behavior, we didn’t specify what we meant by a special character and the code it gave us counts spaces as special characters.

Copilot 提供的代码存在问题。尽管你得到的代码可能运行得很好,我们却没那么幸运,因此识别并修复代码错误至关重要。目前,我们不必深究错误的原因,但如果你对为何我们的提示词可能导致了错误的结果感到好奇,那是因为我们没有具体说明特殊字符的定义,而代码却错误地将空格当作了特殊字符。

At this point, with our wrong code, we have two options. One option is to hit <Ctrl>+Enter and look through the other suggestions that Copilot gives us. Maybe one of those suggestions will have something to do with special symbols that looks different from the one that didn't work?

此时,我们的代码存在错误,我们面临两种选择。一是按下 <Ctrl>+Enter,浏览 Copilot 提供的其他选项。说不定其中会有关于特殊符号的不同方案?

Indeed, on our computer here, we see such options, including this one:

确实,在我们这台电脑上,我们看到了其他方案,比如这一个:

    num_upper = 0
    num_num = 0
    num_special = 0
    for char in password:
        if char.isupper():
            num_upper += 1
        elif char.isnumeric():
            num_num += 1
        elif char in '!@#$%^&*':
            num_special += 1
    if num_upper >= 1 and num_num >= 1 and num_special >= 1:
        return True
    else:
        return False

Test this function with strings like Leo and N3w Y0rk J375 and N3w Y0rk J375$ and you should see that the function works well.

采用 LeoN3w Y0rk J375 以及 N3w Y0rk J375$ 这些字符串对函数进行测试,你应该能观察到其运行效果相当不错。

Our second option is to do some prompt engineering, which means adjusting our prompt to influence what Copilot gives us.

我们的第二个选择是进行所谓的提示工程,也就是优化我们的提示词,以此来改变 Copilot 提供的答案。

In our original prompt, we talked about "special symbols". This, in retrospect, is pretty vague. We probably meant something more specific, like "punctuation". If we specifically talk about punctuation in our prompt, we get this interaction with Copilot:

在我们原先的提示词里,我们提及了“特殊符号”。现在回头来看,这个表述其实相当含糊。我们当时可能更想表达的是“标点符号”。当我们在提示词中明确提及标点符号时,我们便与 Copilot 有了如下的互动:

def is_strong_password(password):
"""
A strong password has at least one uppercase character,
at least one number, and at least one punctuation.

Return True if the password is a strong password, False if not.
"""
    return any(char.isupper() for char in password) and \
           any(char.isdigit() for char in password) and \
           any(char in string.punctuation for char in password)
def is_strong_password(password):
"""
强密码应至少包含一个大写字母,
至少一个数字,以及至少一个标点符号。

判断密码是否符合强密码标准,是则返回 True,否则返回 False。
"""
    return any(char.isupper() for char in password) and \
           any(char.isdigit() for char in password) and \
           any(char in string.punctuation for char in password)

Looks good... that last line is talking about 'punctuation', which is hopeful. Let's test:

看上去不错……最后一句提到了 “标点符号”,令人心怀期待。那么,我们来试试看吧:

>>> is_strong_password('Leo')
False

>>> is_strong_password('N3w Y0rk J375')
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "ch2.py", line 44, in is_strong_password
   any(char in string.punctuation for char in password)
 File "ch2.py", line 44, in <genexpr>
   any(char in string.punctuation for char in password)
               ^^^^^^
NameError: name 'string' is not defined

Look at the bottom of that error message, 'string' is not defined, eh? We’re running into an issue that’s similar to what we saw in Chapter 2 with modules. Copilot wants to use a module, called string, but it is a module that needs to be imported before we can use it. There are a lot of modules in Python, but the string module is pretty well known. As you work with Copilot more, you’ll learn which modules are commonly used so you know to import them. You could also do a quick internet search to ask, “is string a Python module” and the results would confirm that it is. What we need to do is import the module.

观察一下错误提示的最下方,string 模块未定义,对吧?我们碰到了一个与第二章介绍的模块相关的问题。Copilot 试图使用一个称作 string 的模块,这个模块在使用前必须先进行导入。Python 世界里有众多模块,而 string 模块颇为著名。随着你对 Copilot 的使用日益深入,你会逐渐学会辨识哪些模块是频繁使用的,从而知道应当导入它们。你同样可以通过简单的网络搜索询问 “string 是一个 Python 模块吗?”,搜索结果会证实这一点。接下来,我们所要做的,就是导入这个模块。

Note that this is different, somewhat, from what we encountered in Chapter 2. In Chapter 2, we saw what happens when the code from Copilot imports modules we didn’t have installed, and we had to install the package containing those modules to fix the issue. In this case, the code from Copilot is using a module that already happens to be installed with Python, but it forgot to import it. So, we don’t need to install string; we just have to import it.

需要注意的是,这种情况与第二章所述略有差异。在第二章,我们了解到如果 Copilot 导入了我们尚未安装的模块,我们需要安装相应的包来解决这个问题。然而,当前的情况是,Copilot 使用了 Python 自带的模块,但却遗漏了导入步骤。所以,我们无需安装 string 模块,仅需进行导入操作即可。

Importing Modules

There are a number of useful modules available in Python. We saw how powerful matplotlib is in Chapter 2. But in order for Python code to take advantage of modules, we have to import that module. You might ask why don’t we just have modules available to us without importing them, but that would massively increase the complexity of the code and what Python has to do to run code behind the scenes. Instead, the model is to include modules if you want to use them, and they aren’t included by default.

导入模块

Python 提供了许多有用的模块。回想第二章,我们了解到 matplotlib 模块的威力。但要让 Python 代码发挥模块的功能,我们必须先导入这些模块。你可能会好奇,为何不直接让我们使用模块,而非要经过导入这一步骤?因为如果模块默认就可用,那将极大增加代码的复杂性,以及 Python 在幕后执行代码时的负担。所以,我们只能包含那些我们确实想要使用的模块,而它们默认是不会包含在我们的代码中的。

Let's import string at the top of our code:

import string 添加到我们代码的最顶部:

import string

def is_strong_password(password):
"""
A strong password has at least one uppercase character,
at least one number, and at least one punctuation.

Return True if the password is a strong password, False if not.
"""
    return any(char.isupper() for char in password) and \
           any(char.isdigit() for char in password) and \
           any(char in string.punctuation for char in password)

Now we're in good shape:

目前我们的状况相当不错:

>>> is_strong_password('Leo')
False

>>> is_strong_password('N3w Y0rk J375')
False

>>> is_strong_password('N3w Y0rk J375$')
True

That last one is True—-it's a strong password!—because it has the $ punctuation added to it.

最后一个结果显示为 True——这个密码很安全!——这得益于它包含了 $ 这个符号。

We hope that you're now convinced of the value of testing! Sometimes our students don’t test their code. They assume that the code they write is correct because it made sense to them. An interesting difference between novice and experienced programmers is that novices often assume their code is right, whereas experience programmers assume the code is wrong until thoroughly tested and proved otherwise. Beyond this, we find that our students sometimes fail to test well because it's disheartening to learn that the code is wrong. But it's better to know now, rather than later when others are using your code in a serious application. Finding errors through testing is actually a good thing.

我们期望你现在深刻理解了测试的重要性!有时候,我们的学生在编写代码后不会去进行测试。他们倾向于认为代码是正确的,因为它在他们看来是合理的。新手程序员与经验丰富的程序员之间的一个显著差异在于,新手通常会假设他们编写的代码是正确的,而经验丰富的程序员则会预设代码存在问题,直到通过全面的测试验证其正确性。除此之外,我们还发现学生们有时不愿意进行测试,因为面对代码中的错误会让他们感到沮丧。但及早发现问题总比等到他人在实际应用中使用代码时才发现要好得多。通过测试来发现并修正错误,其实是件值得庆幸的事情。

3.6.3 Getting a strong password

3.6.3 获得一个强密码

Now that we have a function that tells us whether a password is strong or not, let’s write a function that obtains a strong password from the user. It will ask again and again for a password until the user types a strong one. This is the kind of code that websites use when they tell you, “Sorry, your password is too weak, try again.”

既然我们已经有了一个能够判断密码强度的函数,那么现在就让我们来编写一个能够从用户那里获得强密码的函数。这个函数会不断询问用户,直到他们输入了一个强密码。这正是那些网站在提示 “抱歉,您的密码太弱了,请再次尝试” 时所使用的代码逻辑。

What should the header for such a function look like? Well, it’s going to ask the user for a password, so we won’t have a password parameter. In fact, we won’t have any parameters at all, because the function doesn’t need any information to do its job – it just has to prompt the user and the user is going to type the password at the keyboard. When the function has done its work, it will return the strong password, so it will continue to return a string as the output.

那么,这个函数的声明应该如何书写呢?它将会引导用户输入密码,所以我们不会设置密码参数。实际上,我们不需要任何参数,因为该函数在执行任务时并不需要额外信息——它仅仅需要向用户发起提示,而用户则会通过键盘输入密码。一旦函数完成了它的使命,便会返回一个强密码,因此,它的输出依旧会是一个字符串。

Let’s try this prompt:

我们来试试这段提示词吧:

def get_strong_password():
"""
Keep asking the user for a password until it is a strong
password, and return that strong password.
"""
def get_strong_password():
"""
不断要求用户输入密码,直到用户输入一个强密码,
然后返回该强密码。
"""

We got the following code from Copilot as a result:

我们由此得到了 Copilot 提供的如下代码:

password = input("Enter a strong password: ")
    while not is_strong_password(password):
        password = input("Enter a strong password: ")
    return password

That while keyword creates another kind of loop, this one continuing as long as the entered password is not strong. Copilot is also smart enough to call our earlier is_strong_password function to determine what counts as a strong password. As we will see in future chapters, using functions as building blocks in this way is precisely how large programs are built. You will often notice Copilot calling your earlier functions to solve later ones, much as we observed here.

这里的 while 关键字构建了一种循环,它会不断地运行,直到用户输入的密码达到强密码的标准为止。Copilot 展现了它的智能,能够调用我们先前定义的 is_strong_password 函数来确定何为强密码。在未来的章节中,我们将看到,正是通过将函数作为基础模块来构建,大型程序的开发才得以实现。你会发现 Copilot 经常调用你之前编写的函数来解决新的问题,正如我们在这里所看到的。

Let’s test this! Highlight all of our password function code and hit <Shift>+Enter. We’ll call the function that we want to test. Then, try typing passwords, hitting enter after each one. You’ll notice that it keeps asking you until you finally provide a strong password:

来,我们来做个测试!选中我们所有的密码函数代码,然后按下 <Shift>+Enter。我们将调用我们想要测试的函数。接着,尝试输入不同的密码,每输入一个就按一次回车键。你会发现,除非你输入了一个真正的强密码,否则它会不断地提示你:

>>> get_strong_password()
Enter a strong password: Leo
Enter a strong password: N3w Y0rk J375
Enter a strong password: N3w Y0rk J375$
'N3w Y0rk J375$'

Notice that it stops asking us for a password when we finally provide a strong password. Then, we see the string in quotes that it returned, which is indeed our strong password.

请注意,一旦我们提供了一个强密码,它就不再要求我们输入密码了。随后,屏幕上会展示一个被引号包围的字符串,那正是我们输入的强密码。

3.6.4 Scrabble scoring

3.6.4 拼词游戏计分

One of Dan’s favorite board games is Scrabble. Have you played it? If not, all you need to know is that you have some tiles in your hand, each with a letter on it, and your goal is to form a word using any combination of those letters. You don’t need to form the word exclusively with your tiles – you can attach those letters to existing letters on the board to create longer words – but we’re not going to worry about that here. The important thing for us is that different letters are worth different numbers of points. For example, an ‘a’ is worth only one point, because ‘a’ is such a common letter. But ‘q’? ‘z’? Those doozies are each worth ten points, because they’re so tough to use. … or should we say ‘puzzling’ to use. Yeah, that’s better.

阿丹钟爱的众多桌游中,拼词游戏(Scrabble)算是一个。你是否曾体验过这款游戏?如果你还未曾尝试,那么只需了解,你手中握有一些印有字母的瓷砖,你的任务是利用这些瓷砖上的字母,通过任意组合来拼出一个单词。你不必完全依靠手中的瓷砖来拼词——事实上,你完全可以将这些字母与棋盘上已有的字母相结合,以构造出更长的单词——但在本例中,我们无需过多纠结于此。我们关注的是,每个字母都有其对应的分值。比如,a 字母仅值一分,因为它实在是太常见了。但 qz 这两个字母呢?这两个字母各值十分,因为它们真的很难用……或者说,很难驾驭。对,这样说更准确。

To calculate the score for a word, we add up the scores for each of its letters. For example, the score for ‘zap’ would be 14. That’s because z is worth 10, a is worth 1, and p is worth 3.

计算单词的得分,就是将其每个字母的分数累加起来。譬如,“zap”这个单词的总分为 14 分。这是因为 z 的分数是 10,a 是1,而 p 则是3。

Dan would like a function that, given a word, tells him how many points that word is worth. OK, so we need a function that takes a word (which is just one parameter). That’s just one parameter. Let’s try this prompt, where we’ve included the number of points that each letter is worth per official Scrabble rules:

阿丹希望有一个函数,能够告诉他一个单词对应的分数。好的,我们需要设计一个函数,它接受一个单词作为参数。这个函数只有一个参数。我们可以尝试以下的提示词,其中包含了拼词游戏官方规则所规定的每个字母的得分:

def num_points(word):
"""
Each letter is worth the following points:
a, e, i, o, u, l, n, s, t, r: 1 point
d, g: 2 points
b, c, m, p: 3 points
f, h, v, w, y: 4 points
k: 5 points
j, x: 8 points
q, z: 10 points

word is a word consisting of lowercase characters.
Return the sum of points for each letter in word.
"""
def num_points(word):
"""
每个字母的得分如下:
a, e, i, o, u, l, n, s, t, r: 1 分
d, g: 2 分
b, c, m, p: 3 分
f, h, v, w, y: 4 分
k: 5 分
j, x: 8 分
q, z: 10 分

word 为全小写字母组成的单词。
返回 word 中每个字母的得分总和。
"""

For this function, Copilot isn’t filling in the whole body for us with one press of the tab key. Tab is also used to indent the code, which we also need to do here before Copilot will present us with code. So, we’re having to press tab (sometimes once for the indentation, then again for Copilot), wait for the code to appear, then press enter to accept that code, then press tab again for more code, and repeat the process.

在这个函数中,Copilot 并不会一次就通过 Tab 键帮我们把整个函数的代码都填充完毕。Tab 键同样用于代码的缩进,这也是我们在得到 Copilot 的代码提示前必须进行的步骤。所以,我们需要按下 Tab 键(有时是为了缩进按一次,为了接收 Copilot 的建议再按一次),等待代码显示出来,然后按 Enter 键确认代码,之后再次按 Tab 键继续获取代码,如此循环往复。

Getting Copilot to suggest code may require pressing <Tab> or <Enter>

We find that Copilot will sometimes pause in giving suggestions until you press a key to help prompt it. As mentioned in table 2.1, if you are giving it comments, it will sometimes just want to give you more comments as suggestions. Pressing Enter another time can often trigger it to start generating code instead. Other times, pressing tab will cause it to continue providing suggestions. Please refer back to table 2.1 if Copilot is not generating code for you.

要让 Copilot 提供代码建议,可能需按 <Tab><Enter>

我们注意到,Copilot 有时会停下来,等待你按键来继续。根据表格 2.1 的说明,如果你提供的是注释,它有时候可能只会回馈更多的注释建议。再次按下 Enter 键,往往能够促使它开始编写代码。在其他情况下,按 Tab 键会让它继续给出建议。如果 Copilot 没有为你编写代码,请回顾表格 2.1 的建议。

Finally, after doing that a few times, the entire code unfurls, and we get the following:

经过几次尝试,完整的代码终于呈现在我们面前:

    points = 0
    for char in word:
        if char in "aeioulnstr":
            points += 1
        elif char in "dg":
            points += 2
        elif char in "bcmp":
            points += 3
        elif char in "fhvwy":
            points += 4
        elif char == "k":
            points += 5
        elif char in "jx":
            points += 8
        elif char in "qz":
            points += 10
    return points

Notice how all of the letters of the alphabet are accounted for here, and the number of points that each category is worth is on the following line. We’ll discuss this overall kind of decision structure in the next chapter, but, at a high level, it is walking through the word character by character and for each character, figuring out which letter it is and adding the appropriate number of points for that letter.

请注意,这段代码已经包含了字母表中的每一个字母,并且每种字母类别的得分也列在了下一行。在后面的章节中,我们会详细讨论这种决策结构,但简单来说,它通过逐个分析单词中的字符,识别每个字符,并为该字符加上相应的分数。

Let’s test this to make sure our code is performing the way we want. We know that the word ‘zap’ should be worth 14 points. And it is:

我们来测试一下,确认代码的表现符合我们的预期。我们知道单词 'zap' 应该值 14 分。结果也确实如此:

>>> num_points('zap')
14

Let’s try a couple more. In each case, calculate by hand what you expect the answer to be – that way you’ll know if the code is doing the right thing.

继续尝试几个例子。每次测试前,先心算出预期的答案——这能帮助你确认代码是否按预期执行。

>>> num_points('pack')
12

>>> num_points('quack')
20

There are many ways to write correct code for a function. If you press <Ctrl>+Enter and look at the Copilot suggestions, you may see different types of code. It doesn’t necessarily mean that one of these types is right and the others are wrong.

一个函数可以有多种正确的编写方法。当你按下<Ctrl>+Enter,浏览Copilot提供的各种代码建议时,这并不意味着只有一种方法是对的,其他的都是错的。

For example, here’s another solution for num_points that we got from the other Copilot suggestions.

举个例子,下面是我们从 Copilot 的其他建议里得到的,另一种针对 num_points 函数的写法。

points = {'a': 1, 'e': 1, 'i': 1, 'o': 1, 'u': 1, 'l': 1,
             'n': 1, 's': 1, 't': 1, 'r': 1,
             'd': 2, 'g': 2,
             'b': 3, 'c': 3, 'm': 3, 'p': 3,
             'f': 4, 'h': 4, 'v': 4, 'w': 4, 'y': 4,
             'k': 5,
             'j': 8, 'x': 8,
             'q': 10, 'z': 10}
   return sum(points[char] for char in word)

Notice here that each letter is listed individually along with its number of points. This structure with the letters and points is called a dictionary and, again, we’ll learn about dictionaries properly in a later chapter. The point for now is just that there are often multiple, equivalently good ways for Copilot to write the code for a function. Give this one a try with some test cases before you continue!

请留意,每个字母及其对应的分数都一一陈列于此。这种字母与分数的组合结构,我们称之为“字典”。关于字典的详细知识,我们将在后续章节中深入探讨。目前,你只需知道在编写函数的代码时,Copilot 往往可以提供多种同样出色的建议可供选择。在你继续探索之前,不妨用几个测试用例来验证这段代码的效果吧!

3.6.5 The best word

3.6.5 最佳单词

Let’s continue with the Scrabble theme. Suppose that Dan has a bunch of words that he can make right now, but he doesn’t know which one will give him the most points. Should he make the word ‘zap’? ‘pack’? ‘quack’? It would be nice if we had a function that could take a bunch of words and tell us which is the best one.

让我们继续拼词游戏的话题。假设阿丹现在可以组成一堆单词,但他不知道哪个单词能给他带来最多的分数。他应该组成“zap”?“pack”?还是“quack”?如果我们有一个函数,能从一堆单词中告诉我们哪个是最好的,那就太好了。

How many parameters should we have in such a function? Your first instinct may be 3, one for each of the 3 words in our options. But that wouldn’t be very flexible. After all, what if we want to know the best of five words? Ten words? Fifty? We would like our function to work, no matter how many words we are considering.

我们的函数应该设置多少个参数才合适呢?你可能会直觉地想到三个,对应我们候选列表中的每个单词。但是,这样的设计缺乏弹性。试想,如果我们想从五个单词中挑选出最优的一个怎么办?十个?五十个?我们期望无论面对多少候选单词,函数都能够有效地工作。

The trick is to use a function with one (yes, just one!) parameter, which is a list of words. Just like numbers and strings and dictionaries, a list is a type supported by Python. It’s incredibly useful because it allows us to collect as many values into one place as we like.

这里的诀窍在于把函数的参数设计为一个单词的 “列表”。与数字、字符串和字典类似,列表也是 Python 所支持的一种数据类型。它之所以强大,是因为我们可以将任意数量的值汇集于一处。

We can prompt Copilot like this to get the function that we want:

我们可以够通过如下提示词来引导 Copilot,从而得到我们所需的函数:

def best_word(word_list):
"""
word_list is a list of words.

Return the word worth the most points.
"""
def best_word(word_list):
"""
word_list 是一个包含单词的列表。

返回分值最高的单词。
"""

How will Copilot know how many points each word is worth? Well, it can call that num_points function that we wrote in the previous section!

Copilot 要如何判断每个单词的得分呢?其实很简单,它可以调用我们之前编写的 num_points 函数来实现这一点!

Here’s the code that Copilot gives us.

以下是 Copilot 给出的代码。

    best_word = ""
    best_points = 0
    for word in word_list:
        points = num_points(word)
        if points > best_points:
            best_word = word
            best_points = points
    return best_word

We need to be careful to call this function correctly when testing. This won’t work:

在进行测试时,我们必须注意正确地调用这个函数。以下的做法是错误的:

>>> best_word('zap', 'pack', 'quack')

Because that’s passing values for three parameters, not one. The right way to call it is to put the words inside square brackets, like this (we’ll see more about lists in the next chapter):

这样做实际上是在为三个参数传递值,而非单一参数。正确的调用方法应当是把单词放入方括号中,如下所示(关于列表的更多内容,我们将在接下来的章节中详细探讨):

>>> best_word(['zap', 'pack', 'quack'])
'quack'

But, as usual, you shouldn’t be content with just one test case. This function should work in other valid but perhaps strange cases, too, such as when we have only one word in the list:

另外,如前所述,我们不应该仅仅满足于单一的测试场景。该函数在其他合理但或许有些不常见的情况下也应能正常运作,例如,当我们的单词列表中仅有一个单词的情形:

>>> best_word(['zap'])
'zap'

However, we wouldn’t test this function on a list that has no words in it. What would it even make sense to do in that case? Regardless of what the function does, it’d be hard to say one way or the other whether it was doing the correct thing in a situation where there really is no correct behavior!

然而,我们并不会对一个空的单词列表进行这个函数的测试。在这种情况下,进行测试是否真的有其必要性呢?不管函数如何运行,在这种根本没有所谓正确行为的场合,要界定它是否正确,确实是颇为困难的。

Overall, in this chapter we’ve learned about functions in Python and how we can use Copilot to help us write them. We’ve also learned about the characteristics of good functions and how important it is to make sure our functions are solving tasks that can be managed well by Copilot. Our next steps in this book all revolve around understanding if the code produced by Copilot is correct and how to fix it when it isn’t. In the next chapter, we’ll start by learning the basics of being able to read the code produced by Copilot as this gives us the first sanity check for whether Copilot is doing what we think it should be. Then in later chapters we’ll dig deeper into how to carefully test the code and what to do when it is wrong.

总的来说,在这一章中,我们学习了如何编写 Python 中的函数,并展示了如何利用 Copilot 来辅助我们的编程工作。我们同样了解到,优秀的函数应具备哪些特质,并且要确保我们为函数所设定的任务都能够由 Copilot 很好地掌控,这一点至关重要。在本书的后续内容中,我们将专注于如何判断 Copilot 生成的代码是否正确,以及在代码出现问题时如何修复。在下一章,我们将首先学习如何阅读 Copilot 编写的代码,这将帮助我们初步确认 Copilot 是否按照我们的预期在工作。而在之后的章节里,我们会进一步学习如何对代码进行细致的测试,以及在发现问题时的如何解决。

[译] [600] 第六章 程序测试与提示工程

6 Testing and Prompt Engineering

第六章 程序测试与提示工程

This chapter covers

本章内容概要

  • Understanding the importance of testing Copilot code
  • Using closed-box versus open-box testing
  • Addressing errors by Copilot by modifying prompts
  • Viewing examples of testing code produced by Copilot
  • 理解测试 Copilot 代码重要性
  • 使用黑盒测试与白盒测试
  • 通过修改提示词解决 Copilot 的错误
  • 查看测试 Copilot 生成代码的示例

In Chapter 3, we first started to see the importance of testing the code produced by Copilot. Testing is an essential skill for anyone writing software because it gives you confidence that the code is functioning properly. In this chapter, we’ll learn how to test our code thoroughly and how to help Copilot fix code that doesn’t work by modifying our prompts.

在第三章,我们初次体会到测试 Copilot 生成代码的重要性。对于编写软件的人而言,测试是一项必不可少的技能,它能够让开发者对代码的正确运行有足够的信心。在这一章中,我们将学习如何对代码进行彻底的测试,并且了解如何通过调整提示词来帮助 Copilot 修复不正确的代码。


本章目录

  • 6.1 Why it is crucial to test code
  • 6.2 Closed-box and open-box testing
    • 6.2.1 Closed-box testing
    • 6.2.2 How do we know which test cases to use?
    • 6.2.3 Open-box testing
  • 6.3 How to test your code
    • 6.3.1 Testing using the Python prompt
    • 6.3.2 Testing in your Python file (we won’t be doing it this way)
    • 6.3.3 doctest
  • 6.4 Revisiting the cycle of designing functions with Copilot
  • 6.5 Full testing example
    • 6.5.1 Finding the most students we can add to a row
    • 6.5.2 Improving the prompt to find a better solution
    • 6.5.3 Testing the new solution
  • 6.6 Another full testing example—Testing with files
    • 6.6.1 What tests should we run?
    • 6.6.2 Creating the function
    • 6.6.3 Testing the function
    • 6.6.4 Common challenges with doctest
  • 6.1 为什么测试代码至关重要
  • 6.2 黑盒测试与白盒测试
    • 6.2.1 黑盒测试
    • 6.2.2 我们如何选定合适的测试用例?
    • 6.2.3 白盒测试
  • 6.3 如何测试你的代码
    • 6.3.1 使用 Python 提示词进行测试
    • 6.3.2 在 Python 文件中测试(我们不会采用这种方式)
    • 6.3.3 基于 doctest 的测试
  • 6.4 重新考量有 Copilot 参与的函数设计流程
  • 6.5 完整的测试案例
    • 6.5.1 找出能加入一行的最多学生数量
    • 6.5.2 优化提示词以寻找更优的解决方案
    • 6.5.3 测试新方案
  • 6.6 另一个完整的测试案例——文件测试
    • 6.6.1 我们应运行哪些测试?
    • 6.6.2 函数创建
    • 6.6.3 函数测试
    • 6.6.4 doctest 面临的常见问题

本章正文略。如需阅读,请购买正版。


[译] [303] 函数的角色

3.3 Roles of functions

3.3 函数的角色

Functions are used in many different roles in programming. At a high level, programs are really just functions that (often) call other functions. Critically, all programs, including Python programs, originate with a single function (named main in languages like Java, C, and C++). Python’s main function is essentially the first line of code that isn’t in a function. But if every program starts with a single function and we just told you that trying to solve a large problem with a single function is a mistake, how does that work? Well, main will call other functions which, in turn, will call other functions, and so forth. The code will still execute (mostly) sequentially in each function, so it may start in main, but then move to another function, and so forth.

函数在编程领域内承担着多种多样的角色。简而言之,程序本质上是由一系列函数组成,这些函数通常会调用其他函数。关键的一点是,所有的程序——包括 Python 程序——都是从一个基本的函数开始构建的(在 Java、C 或 C++ 等语言中,这个基本函数被称为 main)。在 Python 中,main 函数实际上就是代码中不属于任何函数的第一行。但是,如果每个程序都是从这样一个单一的函数起步,而我们刚才又提到,试图仅用一个函数来解决复杂问题是错误的,这该如何理解呢?其实,main 函数会调用其他函数,而这些被调用的函数又会继续调用更多的函数,依此类推。代码在每个函数内部大体上还是会顺序执行,因此虽然执行可能始于 main 函数,但之后可能会转移到其他函数,如此循环往复。

As an example, let’s use the following code. We wrote this code, not Copilot, because no one would ever want to write this code for anything useful outside teaching. It’s just for demonstrating how function calls work.

来看一个例子,这段代码出自我们之手,并非 Copilot。在教学之外,没有人会对编写这样的代码感兴趣。它的唯一目的是为了展示函数调用的工作机制。

Listing 3.1 Python code to demonstrate how Python handles function calls

代码示例 3.1 展示 Python 处理函数调用的方式

def funct1():
    print("there")
    funct2()
    print("friend")
    funct3()
    print("")

def funct2():
    print("my")

def funct3():
    print(".")

def funct4():
    print("well")

print("Hi")
funct1()
print("I'm")
funct4()
funct3()
print("")
print("Bye.")

If we were to run this program, this would be the output (we’ll explain why below):

假如我们执行这段代码,它会产生以下的输出结果(具体原因我们会在下文进行解释):

Hi
there
my
friend
.

I'm
well
.

Bye.

In figure 3.2, we are providing a diagram of how the code in listing 3.1 would be executed by the computer. We’ve intentionally provided an example that has many function calls to tie together what we just learned. Again, this is NOT practical code; it’s just for learning purposes. Let’s trace through the code execution together. It may be easier to refer to figure 3.2 than listing 3.1 as you follow along, but either will work.

在图 3.2 里,我们展示了一个图表,用以解释计算机是如何处理清单 3.1 里的代码的。我们有意选用了一个包含众多函数调用的例子,目的是为了巩固我们刚刚学习的知识。请记住,这些代码并无实际应用价值;它们仅供教学之用。现在,让我们一步步跟踪代码的执行过程。在跟随这段解释时,你或许会觉得参考图 3.2 比直接看清单 3.1 要更直观一些,但两种方式都是可行的。

The program will start execution with the first line in the Python code that isn’t a function ( print("Hi") ). Although Python doesn’t have a main function per-se, we’ll refer to the block of code after the functions as main to help this explanation. Code executes sequentially unless it encounters commands that tell it to execute code somewhere else. So, after executing print("Hi"), it will go to the next line which is the call to funct1: funct1(). The call to funct1 changes where the code is executing to the very start of that function which is the statement: print("there"). The next line of funct1 calls funct2, so this means the program will execute the first line of funct2: print("my"). What gets really interesting is what happens when funct2 finishes. There are no more lines of code to execute, so it automatically moves execution back to the first line following the call to funct2 in funct1. (If the function call is in the middle of another statement, that statement resumes execution, but for this example, the function calls are each on their own line.) If you are curious why it goes to the next line after the call to funct2 rather than back to the call of funct2, the issue is if it returned back to the call to funct2, it would be trapped calling funct2 forever. As a result, functions always return back to the next piece of code to execute (in this example, the next line) after they were called.

程序执行将从 Python 代码中首个非函数语句(print("Hi"))开始。Python 并没有严格定义 main 函数,但为了讲解方便,我们将函数定义后的第一个代码块称为 main。代码按顺序执行,除非遇到需要执行别处代码的命令。执行了 print("Hi") 后,程序将执行 funct1()funct1 被调用后,执行点转移到该函数的起始语句 print("there")。接着,funct1 调用 funct2,程序便执行 funct2 的首句 print("my")funct2 执行完毕后,情况变得有趣起来:当 funct2 执行完毕,由于没有更多代码,执行点会自动回到 funct1funct2 调用之后的下一行。(若函数调用位于某个语句的中间,那么调用结束后,该语句的剩余部分将继续执行。不过在本例中,每个函数调用都独占一行。)你可能会问,为什么执行点是跳转到 funct2 调用之后的行,而不是回到 funct2 被调用的位置。这是因为如果执行点返回到 funct2 调用处,程序就会陷入无限循环调用 funct2。因此,函数在被调用后,总是返回到紧接着它们的下一段待执行代码(在这个例子中是紧接着的下一行)。

Continuing this example, the next line of the code executed will be the line that prints “friend”. The next line calls funct3. funct3 prints a “.” and then returns back to its caller.

继续这个示例,接下来执行的代码行将输出“friend”。紧接着,代码调用了 funct3 函数。funct3 函数输出了一个句点“.”,然后返回到调用它的地方。

So, we’re back in funct1, on the line print(""). Printing an empty piece of text just causes a new line. Now funct1 is finished, so it transfers execution back to the next line in main after it was called. We suspect you are getting the idea by now, so let’s move a bit more quickly:

这样,我们又回到了 funct1 函数里的 print("") 行。输出一个空字符串仅仅会创建一个新的行。funct1 函数已经执行结束,接下来它会将执行权交还给 main 函数中它的调用之后的行。我们推测你现在应该已经理解了这个过程,因此让我们更快地继续:

  • Main next prints “I'm” and then calls funct4.

  • main 接着打印“我”,然后执行 funct4 函数。

  • funct4 prints “well” and then returns back to main where the next line of code calls funct3.

  • funct4 打印 “well” 然后返回到 main,接下来 main 中的下一行代码调用 funct3

  • funct3 prints a “.” and then returns back to main. Notice that funct3 was called both by funct1 and by main, but that’s okay because functions remember how to return back to the function that called them. In fact, having multiple functions calling the same function is a sign that the function being called multiple times is a good function, because of its reuse.

  • funct3 打印出一个“.”接着回到 main。值得注意的是,funct1main 都调用了 funct3,这并无不妥,因为函数总是知道如何返回到最初调用它的位置。事实上,一个函数被多个其他函数调用,这正说明了这个函数设计得好,因为它具有很好的可复用性。

  • After funct3 returns to main, it will print a “” which causes a new line to be started, and then it prints the word “Bye.”

  • 一旦 funct3 返回到 main,它会打印一个空字符串,这将导致开始新的一行,然后它会打印单词 “Bye”。

Figure 3.2 Flow of function execution in our example from listing 3.1.

图 3.2 展示的是来自清单 3.1 的函数调用流程。

That was a long example, but we provided it to help give you an idea of how functions execute and how programs consist of defining and calling functions. In any software you use, think about the specific tasks that it performs: the programmers probably wrote one or more functions for each one. The button in a text editor that changes the text to bold probably calls a function to change the text to bold. That function might change the editor’s internal idea of the text (the editor likely stores your text in a different format than how you view it) and then it might call another function that updates the user’s (your) view of the text.

这个例子虽然篇幅颇长,但我们之所以提供,是为了帮助你更好地理解函数的执行方式,以及程序是如何通过定义和调用函数来构成的。当你使用任何软件时,想想它所执行的具体任务:程序员可能为每项任务编写了一个或多个函数。比如,在文本编辑器中将文字加粗的按钮,很可能是调用了一个函数来实现这一功能。这个函数可能会改变编辑器对文字的内部表示(编辑器存储的文字格式可能与你看到的不同),之后它可能还会调用另一个函数来刷新用户(即你)的文字显示。

We’d also like to use this example to discuss the different roles that functions play. A helper function is a function whose job is to make another functions job easier. In a sense, every function that isn’t “main” is a helper function.

我们也可以借此机会探讨函数扮演的各种角色。所谓的“辅助(helper)”函数,是指那些旨在简化其他函数工作的函数。实际上,除了主程序“main”以外,其他的函数都可以视为辅助函数。

Some functions simply call a bunch of other functions without doing any of their own work. There aren’t any of these in our example. However, if you removed the three print statements from funct1, it would become this type of coordinating function. Others may call helper function(s), then do some work on their own. funct1 is a great example of a function that calls other functions but also does work on its own. And another group of functions stand on their own, without calling other functions (except perhaps functions that already come with Python) for help—we’ll call these functions leaf functions. Why leaf? If you imagine all of the function calls as a big tree, these functions are the leaves of the tree as they have nothing coming out of them. funct2, funct3, and funct4 are all leaf functions in our example. We’re primarily concerned with leaf functions in this chapter, but you’ll see examples of other kinds of functions here and especially in later chapters.

某些函数单纯地调用众多其他函数,本身却不执行任何特定任务。我们的示例中并未包含这类函数。不过,如果你拿掉了 funct1 中的三条打印语句,它就会转变为这种协调性质的函数。还有一些函数可能会先调用一些助手函数,然后自己执行一些任务。funct1 是一个典型的例子,它既调用了其他函数,也完成了自己的工作。还有一些函数是独立的,不依赖于其他函数(除非是Python内置的函数)来提供帮助——我们将这类函数称作“叶子”函数。为何称之为叶子?把函数调用想象成一棵枝繁叶茂的大树,这些函数就像是树的叶片,它们不会产生更多的分支。funct2funct3funct4 在本例中便是典型的叶子函数。本章节主要关注叶子函数的探讨,但在本书的其他部分,尤其是后续章节中,你将看到更多其他类型函数的应用实例。

[译] [1103] 本章小结

Summary

本章小结

  • A prompt pattern is a template to help us construct a prompt to meet a given goal.
  • The flipped interaction pattern flips the script: rather than asking questions to the LLM, the LLM asks questions to us.
  • The flipped interaction pattern is useful when we don’t know how to effectively prompt the LLM.
  • The persona pattern is used to have the AI take on a particular persona, such as “introductory programming instructor,” or point of view.
  • The persona pattern is useful when we want the LLM to respond from a given perspective.
  • Copilot currently struggles with tasks that are contrived, not well defined, or large.
  • Some believe that LLMs will replace programming languages; others believe that programming languages are here to stay.
  • LLMs are helping people do data science without those people ever seeing the Python code.
  • It may be that programming languages themselves are not replaced, but that the leading languages will be developed to be more readable than today’s languages.
  • 提示词模式是一种模版,旨在帮助我们构建提示词,以实现一个特定目标。
  • 翻转互动模式颠覆了常规互动方式:不是我们向 LLM 提问,而是 LLM 向我们提问。
  • 当我们不确定如何有效地引导 LLM 时,翻转互动模式特别有用。
  • 角色扮演模式使 AI 能够扮演特定的角色,例如“初级编程教师”,或代表一个特定观点。
  • 当我们需要 LLM 从一个特定角度进行回应时,角色扮演模式非常有益。

[译] [200] 第二章 快速上手 Copilot

2 Getting Started with Copilot

第二章 快速上手 Copilot

This chapter covers

本章内容概要

  • Setting up Python, VS Code, and Copilot on your system
  • Introducing the Copilot design process
  • Copilot’s value for a basic data processing task
  • 在你的系统中安装 Python、VS Code 以及 Copilot
  • Copilot 设计流程介绍
  • Copilot 在基础数据处理任务中的应用价值

This chapter will help you start working with Copilot on your own machine and familiarize you with how to interact with it. After you are set up with Copilot, we’ll ask that you follow along with our examples when you can. There’s no substitute for practice, and we believe you can learn right alongside us for the remainder of the book.

本章将指导你如何在自己的设备上开始使用 Copilot,并教你如何与它交流。一旦你成功配置 Copilot,我们建议你跟随书中的示例进行实践。毕竟,实践出真知。我们坚信,在本书的剩余章节中,你能够跟着我们一起学习进步。

Once you’ve set up Copilot, we’ll walk through a fun example that showcases the power of Copilot in solving standard tasks, you’ll see how to interact with Copilot, and you’ll learn how we can write software without writing any actual code ourselves.

一旦配置完毕 Copilot,我们将引导你体验一个既有趣又能体现 Copilot 解决标准任务能力的示例。你将掌握如何与 Copilot 进行互动,并学习到我们如何能在不直接编写任何实际代码的前提下进行软件开发。

[译] [108] 本章小结

Summary

本章小结

  • Copilot is an AI Assistant, which is an Artificial Intelligence (AI) agent that helps you get work done.
  • Copilot changes how humans interact with computers, and the way that we write programs.
  • Copilot changes the focus of skills we need to hone (less focus on syntax, more focus on prompt engineering and testing).
  • Copilot is nondeterministic; sometimes it produces correct code, sometimes it doesn't. We need to be vigilant.
  • Issues around copyright of code, education and job training, and bias in Copilot results still need to be worked out.
  • Copilot 是一款 AI 助手,这是一个能够帮助你完成工作的人工智能代理。
  • Copilot 重新定义了人与计算机的交互方式及程序编写方法。
  • Copilot 转变了我们技能提升的焦点(减少对语法的关注,增加对提示工程和测试的重视)。
  • Copilot 具有非确定性特点;它有时能生成正确的代码,有时却不能。我们需要时刻保持警惕。
  • 关于代码的版权、教育与职业培训以及 Copilot 结果中的偏见问题仍需要解决。代码版权、教育与职业培训问题,以及 Copilot 结果的偏见等问题,仍需寻找解决方案。

[译] [709] 本章小结

Summary

本章小结

  • We need to divide a big programming problem into smaller sub-problems before we can effectively implement it.
  • Top-down design is a systematic technique for breaking a problem down into small subtask functions.
  • In top-down design, we seek small functions that solve well-defined tasks and that can be used by one or more other functions.
  • Author identification is the process of guessing the author of a mystery book.
  • We can use features about words (e.g., average word length) and sentences (e.g., average number of words per sentence) to characterize how each known author writes.
  • Machine learning is an important area of computer science that investigates how machines can learn from data and make predictions.
  • In supervised learning, we have some training data in the form of objects (e.g., books) and their categories (who wrote each book). We can learn from that data to make predictions about new objects.
  • A signature consists of a list of features, one signature per object.
  • When we’re ready to implement our functions that arose from top-down design, we implement them from the bottom up; that is, we implement the leaf functions first, then functions that depend on those leaf functions, and so on until we implement the topmost function.
  • Refactoring code means to improve the design of the code (e.g., by reducing code repetition).
  • 在有效实施一个庞大编程任务之前,我们需要将其划分为更细小的子问题。
  • 自顶向下设计是一个系统化技巧,用于把复杂问题拆解为多个小的子任务函数。
  • 在自顶向下设计策略中,我们寻求那些能够解决具体任务并能被其他函数复用的小型函数。
  • 作者识别是推测一部未知书籍作者的过程。
  • 通过分析词语(例如,平均词长)和句子(例如,平均每句话的词数)的特性,我们能够描绘出每位已知作者的写作风格。
  • 机器学习是计算机科学中的一个重要分支,它研究机器如何通过数据学习并进行预测。
  • 在监督学习场景下,我们有一些训练数据,这些数据以对象(如书籍)及其分类(例如,每本书的作者)的形式存在。我们可以利用这些数据来对新的对象进行预测。
  • 一个签名包括了一组特征,每个对象配有一个独特的签名。
  • 当我们开始根据自顶向下的设计准备实现我们的功能时,我们采用自底向上的实现方式;也就是说,我们首先实施最基础的叶子功能,然后是依赖这些叶子功能的更高层次的功能,依次类推,直至实现最高层的功能。
  • 重构代码旨在提升代码设计的质量(例如,通过降低代码冗余)。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.