Skip to content

Commit 76faf4b

Browse files
committed
feat: update chapter(s) by CAT
1 parent ccca78e commit 76faf4b

File tree

1 file changed

+107
-8
lines changed

1 file changed

+107
-8
lines changed

content/chapter-3/306.md

Lines changed: 107 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,19 +182,32 @@ This may even be easier to read for you: first it figures out the difference in
182182
这样的表达或许更加清晰:先计算出每股的差价,再将其与持股数量相乘。建议你同样对这个版本进行测试,以确保它也是正确的。
183183

184184
### 3.6.2 Leo’s password
185+
### 3.6.2 Leo 的密码
185186

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

189+
Leo 正在注册一个名为 ProgrammerBook 的新兴社交网络网站,他希望确保自己的密码足够安全。
190+
188191
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!)
189192

193+
Leo 对 “强密码” 的定义颇为低调:他觉得只要密码不是 “password” 或 “qwerty”,就算是一个强密码了。(这两个密码无疑是极差的,然而实际上,若真想确保密码安全,我们还需做到比这更好的水平!)
194+
190195
A helpful function would be one that takes a proposed password and tells us whether it is strong or not.
191196

197+
这里我们需要一个实用的函数,它能够接收一个拟定的密码,并判断其强度是否达标。
198+
192199
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!
193200

201+
不同于本章前面介绍的函数,本次我们处理的不是数字,而是文本类型的密码。同时,返回值用于表示一个肯定或否定的结果。这就涉及到了新的数据类型。
202+
194203
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.
195204

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

209+
好的!现在我们可以开始引导 Copilot。通常,对于返回布尔值(True/False)的函数,我们会采用 `has_x``is_x` 这样的命名方式,或者使用其他能够隐含真/假结果的动词。
210+
198211
```python
199212
def is_strong_password(password):
200213
"""
@@ -205,19 +218,35 @@ Return True if the password is a strong password, False if not.
205218
"""
206219
```
207220

221+
```python
222+
def is_strong_password(password):
223+
"""
224+
一个强密码应当避免使用 ‘password’ 或 ‘qwerty’ 这样的常见词汇。
225+
226+
判断密码是否符合强密码标准,是则返回 True,否则返回 False。
227+
"""
228+
```
208229

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

232+
作为回应,以下是 Copilot 为我们编写的代码:
233+
211234
```python
212235
return password != 'password' and password != 'qwerty'
213236
```
214237

215238
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.
216239

240+
我们来仔细查看这段代码。`!=` 是 “不等于” 操作符,每个不符合强密码标准的字符串都用单引号括起来。我们在操作字符串值时,每次都要使用引号。关于代码阅读的更多细节,我们将在下一章进行深入讨论,但目前希望代码能给您一个大致的印象:它似乎在检查密码不是 `password` 也不是 `qwerty`,这正符合我们对强密码的定义。
241+
217242
Now let’s make sure that this function does indeed work, by making a few test calls.
218243

244+
现在,我们就通过几轮测试来检验这个函数是否真的有效。
245+
219246
We’ll start by testing the passwords that are not strong. We had better get back `False` for each of these—and we do!
220247

248+
我们先来测试那些强度不足的密码。对于这些密码,我们期望得到的响应是 `False`,而且结果正是如此!
249+
221250
```
222251
>>> is_strong_password('password')
223252
False
@@ -226,28 +255,36 @@ False
226255
False
227256
```
228257

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

260+
很好!接下来,我们来测试一个强密码。我们期望这里的结果为 `True`
261+
232262
```
233263
>>> is_strong_password('N3w Y0rk J375')
234264
True
235265
```
236266

237-
238267
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.
239268

269+
好极了!现在我们看到函数有时候返回 `False`,有时候返回 `True`,至少它不是一直返回 `False`
270+
240271
Let’s try one more:
241272

273+
再尝试一个例子:
274+
242275
```
243276
>>> is_strong_password('Leo')
244277
True
245278
```
246279

247280
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.
248281

282+
嗯……虽然返回 `True` 是附合预期的,但严格来说 “Leo” 算是个糟糕的密码。我们本可以对我们所谓的 “强密码” 给出一个更恰当的定义。让我们调整提示词,给强密码下一个更合理的定义。
283+
249284
Here's our next prompt and what Copilot gave us.
250285

286+
接下来是我们的下一段提示词,以及 Copilot 提供的代码。
287+
251288
```python
252289
def is_strong_password(password):
253290
"""
@@ -256,19 +293,40 @@ at least one number, and at least one special symbol.
256293
257294
Return True if the password is a strong password, False if not.
258295
"""
259-
return any(char.isupper() for char in password) and \
260-
any(char.isdigit() for char in password) and \
296+
return any(char.isupper() for char in password) and
297+
any(char.isdigit() for char in password) and
298+
any(not char.isalnum() for char in password)
299+
```
300+
301+
```python
302+
def is_strong_password(password):
303+
"""
304+
强密码应至少包含一个大写字母,
305+
至少一个数字,以及至少一个特殊符号。
306+
307+
判断密码是否符合强密码标准,是则返回 True,否则返回 False。
308+
"""
309+
return any(char.isupper() for char in password) and
310+
any(char.isdigit() for char in password) and
261311
any(not char.isalnum() for char in password)
262312
```
263313

264314
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.
265315

316+
这几处 `for char in password` 是 Python 用来遍历密码中每个字符的语法。它开启了循环这一功能,这是我们在第五章将要详细学习的核心概念。
317+
266318
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`.
267319

320+
当然,你从 Copilot 那里得到的代码可能和我们的不一样。不过,就我们所获得的代码来看,第一部分似乎是在检查是否有任何大写字母,第二部分在寻找任意数字,而第三部分则在寻找那些不属于 `isalnum` 方法定义的字符。
321+
268322
Maybe isalnum means ‘is alphanumeric’. Does that include special symbols and, if so, which ones?
269323

324+
也许 `isalnum` 的意思是 “is alphanumeric(是否为字母和数字)”。那么它是否包括特殊符号呢?如果包括的话,又是哪些特殊符号呢?
325+
270326
Let's test this function to see how well we’re doing.
271327

328+
我们来测试这个函数,评估一下它的效果如何。
329+
272330
```
273331
>>> is_strong_password('Leo')
274332
False
@@ -282,14 +340,22 @@ True
282340

283341
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`.
284342

343+
哎呀——第三个密码的结果出错了。尽管它看起来很安全,但实际上并不达标,因为它缺少了一个特殊符号。我们的函数给出了 `True` 的返回值,但这是错误的;它本应该返回 `False`
344+
285345
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.
286346

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

351+
此时,我们的代码存在错误,我们面临两种选择。一是按下 `<Ctrl>+Enter`,浏览 Copilot 提供的其他选项。说不定其中会有关于特殊符号的不同方案?
352+
289353
Indeed, on our computer here, we see such options, including this one:
290354

355+
确实,在我们这台电脑上,我们看到了其他方案,比如这一个:
356+
291357
```python
292-
num_upper = 0
358+
num_upper = 0
293359
num_num = 0
294360
num_special = 0
295361
for char in password:
@@ -305,13 +371,18 @@ num_upper = 0
305371
return False
306372
```
307373

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

376+
采用 `Leo``N3w Y0rk J375` 以及 `N3w Y0rk J375$` 这些字符串对函数进行测试,你应该能观察到其运行效果相当不错。
377+
311378
Our second option is to do some prompt engineering, which means adjusting our prompt to influence what Copilot gives us.
312379

380+
我们的第二个选择是进行所谓的提示工程,也就是优化我们的提示词,以此来改变 Copilot 提供的答案。
381+
313382
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:
314383

384+
在我们原先的提示词里,我们提及了“特殊符号”。现在回头来看,这个表述其实相当含糊。我们当时可能更想表达的是“标点符号”。当我们在提示词中明确提及标点符号时,我们便与 Copilot 有了如下的互动:
385+
315386
```python
316387
def is_strong_password(password):
317388
"""
@@ -325,8 +396,23 @@ Return True if the password is a strong password, False if not.
325396
any(char in string.punctuation for char in password)
326397
```
327398

399+
```python
400+
def is_strong_password(password):
401+
"""
402+
强密码应至少包含一个大写字母,
403+
至少一个数字,以及至少一个标点符号。
404+
405+
判断密码是否符合强密码标准,是则返回 True,否则返回 False。
406+
"""
407+
return any(char.isupper() for char in password) and \
408+
any(char.isdigit() for char in password) and \
409+
any(char in string.punctuation for char in password)
410+
```
411+
328412
Looks good... that last line is talking about 'punctuation', which is hopeful. Let's test:
329413

414+
看上去不错……最后一句提到了 “标点符号”,令人心怀期待。那么,我们来试试看吧:
415+
330416
```
331417
>>> is_strong_password('Leo')
332418
False
@@ -342,17 +428,25 @@ Traceback (most recent call last):
342428
NameError: name 'string' is not defined
343429
```
344430

345-
346431
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.
347432

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

437+
需要注意的是,这种情况与第二章所述略有差异。在第二章,我们了解到如果 Copilot 导入了我们尚未安装的模块,我们需要安装相应的包来解决这个问题。然而,当前的情况是,Copilot 使用了 Python 自带的模块,但却遗漏了导入步骤。所以,我们无需安装 `string` 模块,仅需进行导入操作即可。
438+
350439
> **Importing Modules**
351440
>
352441
> 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.
353442
443+
> **导入模块**
444+
>
445+
> Python 提供了许多有用的模块。回想第二章,我们了解到 matplotlib 模块的威力。但要让 Python 代码发挥模块的功能,我们必须先导入这些模块。你可能会好奇,为何不直接让我们使用模块,而非要经过导入这一步骤?因为如果模块默认就可用,那将极大增加代码的复杂性,以及 Python 在幕后执行代码时的负担。所以,我们只能包含那些我们确实想要使用的模块,而它们默认是不会包含在我们的代码中的。
446+
354447
Let's `import string` at the top of our code:
355448

449+
`import string` 添加到我们代码的最顶部:
356450

357451
```python
358452
import string
@@ -369,9 +463,10 @@ Return True if the password is a strong password, False if not.
369463
any(char in string.punctuation for char in password)
370464
```
371465

372-
373466
Now we're in good shape:
374467

468+
目前我们的状况相当不错:
469+
375470
```
376471
>>> is_strong_password('Leo')
377472
False
@@ -385,8 +480,12 @@ True
385480

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

483+
最后一个结果显示为 `True`——这个密码很安全!——这得益于它包含了 `$` 这个符号。
484+
388485
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.
389486

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

392491
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.”

0 commit comments

Comments
 (0)