Python编程实战
上QQ阅读APP看书,第一时间看更新

1.1 项目1:生成假名

在此阶段的热身项目中,你将编写一个把姓氏和名字随机组合来产生虚假姓名的简易Python程序。该程序将毫不费力地生成大量令人意想不到的假名。你还将学习编程规范的最佳实践,以及在外部应用程序帮助下编写符合这些规范的代码。

《灵异妙探》的剧情跟你毫不相关吗?你也可以用喜欢的姓名来替换代码列表中的姓名。你可以轻而易举地将此项目变成《权利的游戏》的姓名生成器,或者发现一个让自己感到很惊喜的姓名“Benedict Cumberbatch”。对我来说,我最喜欢的姓名是“Bendylick Cricketbat”。


 

目标

编写符合既定样式的Python代码,随机生成一些有趣的姓名。


 

制定项目计划绝不是一件浪费时间的事。不管编程是出于乐趣,还是为了盈利,从某种程度上来说,你都需要十分精准地评估一些因素,诸如此项目的耗时、可能遇到的困难、完成此项工作所需的工具和资源等。要解决好这些问题,你首先需要明确自己在做什么。

一位成功的管理人士曾告诉我,他成功的秘诀就是不断地向自己提出一些问题。例如,你在做怎样的尝试?你为什么要做这种尝试?为什么要采用这种方式?耗费的时间和需要的资金分别是多少?厘清这些问题将有助于我们完成最终的项目设计,还能使这些问题的解决方案在我们的头脑中呈现出清晰的脉络。

艾伦·唐尼(Allen Downey)在他的编写《像计算机科学家一样思考Python(第2版)》中描述了两种类型的软件开发计划:“原型和补丁(Prototype and Patch)”和“按计划开发(Designed Development)”。在“原型和补丁”思想的指导下,先从一个简单的程序开始,然后使用补丁和可编辑的代码去解决测试过程中遇到的问题。在解决一个难以理解的复杂问题时,这不失为一个好办法,但是这会产生复杂且不可靠的代码。如果对问题本身有清晰的认识,并且知道如何去解决它,那么你就应该采用“按计划开发”的软件设计思想,尽量解决未来可能出现的问题,避免后续为程序添加补丁。这种方法可以使编写的代码更简单和有效,而且通常也会使代码变得更健壮和可靠。

对于本书中的每个项目,你都需要先清晰地理解其中的问题,明确项目目标,在此基础上才开始编写代码。这样一来,你就能够更好地理解问题,制定开发计划和设计策略。

首先,定义两个清单,它们分别用于存储虚假的名字和姓氏。由于这两个清单长度较短,它们不会占用大量内存,且不需要动态更新。在程序运行的过程中,这两个列表应该也不会出现任何运行问题。因此,你可以用元组存储名字和姓氏清单。

当程序运行后,它会根据这两个元组存储的元素,匹配姓氏和名字,生成一个新的姓名组合。用户可以重复该过程,直到产生足够多的虚假姓名为止。

在解释器窗口中以某种方式突出显示姓名,使它与命令提示信息有明显的不同。IDLE提供的可用字体选项并不多,但是我们都知道错误信息会被标红。在解释器窗口中,默认的标准输出函数是print(),但当加载sys模块后,可以使用file参数将输出重定向到错误信息输出通道,使输出的文字颜色为红色:

print(something, file=sys.stderr).

最后,需要确定哪种编程风格是Python编程规范目前所推荐的。这些编程风格不仅约定了代码的编写规范,而且对嵌入在代码内的文档字符串(Docstring)也有具体的要求。

丘吉尔曾说过,“不要尝试做你喜欢的事,要去喜欢你正在做的事”,你很难将编写伪代码与这句话联系在一起,但这句话却道出了人们使用伪代码的心理过程。

伪代码是一种使用任何结构化的人类语言对计算机程序执行过程进行解释的高级非正式描述,它像是一种包含关键词和适当缩进的简单编程语言。程序开发者使用伪代码的主要目的是忽略所有编程语言中的复杂语法,从而专注于程序的底层逻辑。尽管伪代码被广泛使用,但是目前只存在一些伪代码使用方法的指导原则,而这些指导原则还没有形成正式的标准。

如果编写程序时受挫,那么主要原因是你没有花时间去编写伪代码。对我来说,每当对编写代码感到困惑迷茫的时候,伪代码总能给我新的启发。因此,在本书的大多数项目中,你都会看到伪代码的使用。至少,我希望你能看到伪代码的实用价值,也希望你能养成在项目中使用伪代码的习惯。

虚假姓名生成器程序的伪代码描述如下:

定义名字元组
定义姓氏元组
从名字元组中随机选择一个名字
将这个名字分配给变量
从姓氏元组中随机选择一个姓氏
将这个姓氏分配给变量
在屏幕上用红色字体按选择的顺序输出姓名
询问用户是退出程序,还是重新开始
如果用户选择重新开始:
   重复上述过程
如果用户选择退出:
   结束并退出程序

如果不是只为了通过编程课程考核和向他人提供清晰的程序说明,那么请牢记使用伪代码的目的。你不要认为这样做是盲目服从(非标准的)编程规范,也不要将伪代码的应用局限于编程,尝试将它应用到更多的地方。一旦掌握了其应用窍门,你就会发现它可以帮助你完成其他任务,例如管理税务、计划投资、建造房子,甚至是准备野营。这是一种使思维集中的好方式,同时也能锻炼你把编程思想移植到现实生活中的能力。

清单1-1是虚假姓名生成器程序pseudonyms.py,它根据已有的名字和姓氏元组生成假名,并输出这个假名。如果不想将这些姓名全部输入,你可以分别输入它们的一个子集,也可以直接从配套资源中获取这些假名数据。

清单1-1 根据名字和姓氏元组生成假名

pseudonyms.py 
  ➊ import sys, random

  ➋ print("Welcome to the Psych 'Sidekick Name Picker.'\n")
     print("A name just like Sean would pick for Gus:\n\n")

     first = ('Baby Oil', 'Bad News', 'Big Burps', "Bill 'Beenie-Weenie'",
              "Bob 'Stinkbug'", 'Bowel Noises', 'Boxelder', "Bud 'Lite' ",
              'Butterbean', 'Buttermilk', 'Buttocks', 'Chad', 'Chesterfield',
              'Chewy', 'Chigger", "Cinnabuns', 'Cleet', 'Cornbread', 'Crab Meat',
              'Crapps', 'Dark Skies', 'Dennis Clawhammer', 'Dicman', 'Elphonso',
              'Fancypants', 'Figgs', 'Foncy', 'Gootsy', 'Greasy Jim', 'Huckleberry',
              'Huggy', 'Ignatious', 'Jimbo', "Joe 'Pottin Soil'", 'Johnny',
              'Lemongrass', 'Lil Debil', 'Longbranch', '"Lunch Money"', 'Mergatroid',
               '"Mr Peabody"', 'Oil-Can', 'Oinks', 'Old Scratch',
              'Ovaltine', 'Pennywhistle', 'Pitchfork Ben', 'Potato Bug',
              'Pushmeet','Rock Candy', 'Schlomo', 'Scratchensniff', 'Scut',
              "Sid 'The Squirts'", 'Skidmark', 'Slaps', 'Snakes', 'Snoobs',
              'Snorki', 'Soupcan Sam', 'Spitzitout', 'Squids', 'Stinky',
              'Storyboard', 'Sweet Tea', 'TeeTee', 'Wheezy Joe',
              "Winston 'Jazz Hands'", 'Worms')

     last = ('Appleyard', 'Bigmeat', 'Bloominshine', 'Boogerbottom',
             'Breedslovetrout', 'Butterbaugh', 'Clovenhoof', 'Clutterbuck',
             'Cocktoasten', 'Endicott', 'Fewhairs', 'Gooberdapple', 'Goodensmith',
             'Goodpasture', 'Guster', 'Henderson', 'Hooperbag', 'Hoosenater',
             'Hootkins', 'Jefferson', 'Jenkins', 'Jingley-Schmidt', 'Johnson',
             'Kingfish', 'Listenbee', "M'Bembo", 'McFadden', 'Moonshine', 'Nettles',
             'Noseworthy', 'Olivetti', 'Outerbridge', 'Overpeck', 'Overturf',
             'Oxhandler', 'Pealike', 'Pennywhistle', 'Peterson', 'Pieplow',
             'Pinkerton', 'Porkins', 'Putney', 'Quakenbush', 'Rainwater',
             'Rosenthal', 'Rubbins', 'Sackrider', 'Snuggleshine', 'Splern',
             'Stevens', 'Stroganoff', 'Sugar-Gold', 'Swackhamer', 'Tippins',
             'Turnipseed', 'Vinaigrette', 'Walkingstick', 'Wallbanger', 'Weewax',
             'Weiners', 'Whipkey', 'Wigglesworth', 'Wimplesnatch', 'Winterkorn', 
             'Woolysocks')

  ➌ while True:
      ➍ firstName = random.choice(first)

      ➎ lastName = random.choice(last)

         print("\n\n")
      ➏ print("{} {}".format(firstName, lastName), file=sys.stderr)
         print("\n\n")

      ➐ try_again = input("\n\nTry again? (Press Enter else n to quit)\n")
         if try_again.lower() == "n":
             break

  ➑ input("\nPress Enter to exit.")

首先,向程序导入sys模块和random模块➊。sys模块使你能够访问具体的系统错误消息,同时允许你把IDLE窗口中的输出文字设置为醒目的红色。random模块使你可以随机地从存放名字和姓氏的元组中选择数据项。

print语句的作用是向用户介绍本程序的功能➋。换行命令符\n强制开始新行;在字符串输出的双引号中,不必使用\转义字符,而可以直接使用单引号’。需要注意的是,使用转义字符将会降低代码的可读性。

接下来,分别定义名字和姓氏元组。然后,程序开始执行while循环➌。将while循环语句的循环条件设置为True,即让程序“一直运行,直到我让你停下来为止”。最终,你会使用break语句来结束这个循环。

在while循环体内,先从first元组中随机选择一个名字,并把这个名字赋给firstName变量➍。random模块的choice()函数会随机地从非空序列中选择一个元素,并把该元素当作函数的返回值。就本例而言,非空序列指的是名字元组。

紧接着,从last元组中随机选择一个姓氏,并将它分配给lastName变量➎。此时,你已经得到一组名字和姓氏,把它们输出在shell窗口中。向print()函数提供可选参数file=sys.stderr➏,使IDLE窗口中的“错误”信息显示为红色。同时,使用新的字符串格式化方法把姓名变量值转换为字符串。需要注意的是,旧的字符串格式方法指的是使用操作符%将变量值转换为字符串。在Python的官方网站可以获得更多与新的字符串格式化方法有关的信息。

之后,程序生成的假名就会显示出来。接着,利用input语句显示一段提示信息,询问用户是再来一次,还是退出程序。为了使这个有趣的姓名在IDLE窗口中更加显眼,本示例程序会使输出结果之间包含一些空白行。对于这个请求,如果用户直接按Enter键,那么变量try_again就不会捕捉到任何输入内容➐。由于没有返回任何内容,因此if条件语句不成立,while循环会继续运行,程序会再次输出新的名字和姓氏对。如果用户按N键,那么if语句中的条件成立,break语句会被执行。此时,while循环语句的判断条件也不再是True,循环随之结束。为了避免用户不小心按下Caps lock键,利用字符串对象的lower()函数把用户的输入值转换为小写字符。换言之,用户不必考虑输入的是小写字符还是大写字符,程序会总是将它视为小写。

最后,程序显示一条提示信息,告诉用户按Enter键结束程序➑。当用户按Enter键时,input()函数不会把返回值赋给任何变量,程序结束,同时控制台窗口关闭。在IDLE编辑器窗口中,按F5键将会执行程序。

这段代码可以正常运行,但是仅能正常运行是不够的,Python程序还得合乎一定的编程规范。

1.Python社区的编程规范

根据Python之禅(The Zen of Python)的说法,“做好一件事情的方法应该有且只有一种”。在实践的基础上,Python社区不断推出新的编程指导原则,发布新的Python增强提议(Python Enhancement Proposals,PEP),这些提议涉及一系列编程规范。Python发行版中的标准库也遵循相关编程规范。PEP 8是这些增强提议中最重要的编程规范。新的编程规范不断涌现,而旧的编程规范随着语言的改变逐渐被淘汰,PEP 8标准也随着时间的推移而不断演化。

PEP 8标准不仅约定了Python中标识符的命名原则,还规定了空白行、制表符和空格的使用方式,以及每行允许的最大字符长度和可采用的注释方式。PEP 8标准能提高代码的可读性,使所有的Python程序在编程规范上保持一致。当开始用Python编写程序时,你应该努力学习这些公认的编程规范,并养成遵守这些规范的良好习惯。本书的代码样式将与PEP 8标准保持一致,但是为了满足出版行业的排版要求,我在撰写本书时并没有严格遵守编程规范(例如,在本书中我会尽量减少代码注释和空行,也会让文档字符串尽可能地短)。

在跨职能团队中,标准化的名称和过程对程序的开发尤为重要。否则,科学家和工程师之间可能会产生很多误解。1999年,由于不同的团队使用不同的测量单位,工程师们失去了对火星气候轨道飞行器(Mars Climate Orbiter)的控制。在过去的20年时间里,我建立了能够应用于工程上的地球计算机模型。工程师可以使用脚本将这些模型加载到专门的软件中。为了帮助那些没有经验的人提高效率,工程师们会在项目开发过程中共享这些脚本。由于这些“命令文件”是根据每个项目定制的,因此在模型更新期间,工程师会对属性名的更改感到恼火。实际上,他们的内部准则之一便是“要求建模者使用一致的属性名”。

2.使用Pylint模块检查代码

尽管你已经熟悉PEP 8标准,但是仍然可能会犯错误。查看代码是否遵守PEP 8标准也是一件很麻烦的事情。幸运的是,你有许多工具可用,例如Pylint、pycodestyle和Flake8。在这些工具的帮助下,你可以轻松地编写出遵循PEP 8标准的代码。对于本章中的这个项目,你可以使用Pylint模块检查其代码的规范性。

(1)安装Pylint模块

Pylint模块是一款Python的源代码错误和代码质量检查器。在Pylint模块官网上,你可以找到该模块安装包的免费副本;根据所使用的操作系统,找到Install按钮。这个按钮将显示安装Pylint模块的命令。例如,在Windows操作系统上进入Python的安装主目录(如C:\Python35),在主目录中按住Shift键并单击鼠标右键,打开上下文菜单,根据你所使用的Windows版本,选择“在此处打开PowerShell窗口”(open PowerShell window here)选项。最后,在弹出的窗口中执行pip install pylint命令。

(2)运行Pylint模块

在Windows操作系统中,在命令行窗口中可以运行Pylint模块。而对于较新的操作系统,也可以在PowerShell(在待检查的Python模块主目录中,按住Shift并单击鼠标右键即可打开它)中运行它。输入pylint filename就可以运行该程序,如图1-1所示。扩展名.py是可选的,需要注意的是,实际的目录路径可能与图中显示的有所不同。在macOS和类UNIX操作系统上可以使用终端模拟器来执行这些操作。

图1-1 在Windows操作系统的命令行窗口中运行Pylint模块

Pylint模块把程序的检查结果显示在命令行窗口中。下面是一个Pylint模块的输出示例:

C:\Python35\Python 3 Stuff\Psych>pylint pseudonyms.py
No config file found, using default configuration
************* Module pseudonyms
C: 45, 0: No space allowed around keyword argument assignment
    print(firstName, lastName, file = sys.stderr)
                                    ^ (bad-whitespace)
C: 1, 0: Missing module docstring (missing-docstring)
C: 2, 0: Multiple imports on one line (sys, random) (multiple-imports)
C: 7, 0: Invalid constant name "first" (invalid-name)
C: 23, 0: Invalid constant name "last" (invalid-name)
C: 40, 4: Invalid constant name "firstName" (invalid-name)
C: 42, 4: Invalid constant name "lastName" (invalid-name)
C: 48, 4: Invalid constant name "try_again" (invalid-name)

每行开头的大写字母代表消息码。例如,“C:15, 0”指的是第15行第0列违背Python编程规范。下面是一些常见的Pylint模块消息码:

R:违反“良好实践”原则。

C:违反编程规范。

W:不严重的编程问题。

E:严重的编程问题(可能是一个错误)。

F:致命错误,阻止Pylint模块进一步运行。

通过与PEP 8标准进行一致性对比,Pylint模块会为你的代码打分。在这个例子中,代码的得分为4(满分10分):

Global evaluation
-----------------
Your code has been rated at 4.00/10 (previous run: 4.00/10, +0.00)

(3)处理常量名称错误

你可能已经注意到,Pylint模块错误地假设全局代码空间中的所有变量都表示常量,因此它们必须全部使用大写。该问题的解决方法有许多。第一种方法就是将这些变量放入main()函数中,如清单1-2所示。这样一来,这些常量就不再位于全局代码空间。代码如下:

清单1-2 main()函数的定义和调用方式

     def main():
         some indented code
         some indented code
         some indented code
  ➊ if __name__ == "__main__":
      ➋ main()

变量__name__是一种特殊的内置变量,你可以用它判断程序的运行方式,即程序是独立运行的,还是以导入其他程序中的方式运行的。需要注意的是,导入模块指的是在一个Python程序内使用另一个Python程序的行为。如果直接运行该程序,变量__name__就会被设置为“__main__”。在清单1-2中,变量__name__的作用就是确保当该脚本以模块的形式导入其他程序时,main()函数不会被调用。只有当直接运行该脚本时,if语句的判断条件才成立➊,main()函数才会被调用➋。但你并非总要遵守这样的约定。例如,若代码中仅有一个函数,就不需要在代码中使用变量__name__,直接以模块的形式将它导入另一个调用它的模块中即可。

除import语句之外,我们把程序pseudonyms.py的所有代码都放到main()函数中,在if语句内调用main()函数,如清单1-2所示。你既可以手动修改程序,使程序pseudonyms.py的代码满足前面所述的编程规范,也可以从本书配套资源中下载对应的程序pseudonyms_main.py。然后重新运行Pylint模块,检查修改后的程序。你会在命令行窗口中看到下面的输出结果:

C:\Python35\Python 3 Stuff\Psych>pylint pseudonyms_main
No config file found, using default configuration
************* Module pseudonyms_main
C: 47, 0: No space allowed around keyword argument assignment
        print(firstName, lastName, file = sys.stderr)
                                        ^ (bad-whitespace)
C: 1, 0: Missing module docstring (missing-docstring)
C: 2, 0: Multiple imports on one line (sys, random) (multiple-imports)
C: 4, 0: Missing function docstring (missing-docstring)
C: 42, 8: Invalid variable name "firstName" (invalid-name)
C: 44, 8: Invalid variable name "lastName" (invalid-name)

现在,那些令人讨厌的无效常量名称提示已经消失,但是你的代码依然没有完全遵守PEP 8标准。尽管我很喜欢使用像firstName这样的驼峰命名法,但是Python规范不允许我这样做。

(4)配置Pylint模块

当使用Pylint模块检查较短的脚本时,我更倾向于使用Pylint模块的默认设置,并忽略掉“无效常量名称”提示。我还喜欢使用-rn(-reports=n的简写)选项,它会阻止Pylint模块返回大量无关的统计信息:

C:\Python35\Python 3 Stuff\Psych>pylint -rn pseudonyms_main.py

注意,-rn选项会禁用Pylint模块的代码评分功能。

当使用Pylint模块时,经常遇到的另外一个问题是:它的默认最大行长为100个字符,但是,PEP 8建议的最多字符个数为79。若想与PEP 8保持一致,请用下面所示的设置运行Pylint模块:

C:\Python35\Python 3 Stuff\Psych>pylint --max-line-length=79    pseudonyms_main

此时,你会看到缩减最大行长后,main()函数内某些行的长度超出规定值:

C: 12, 0: Line too long (80/79) (line-too-long)
C: 14, 0: Line too long (83/79) (line-too-long)
--snip--

当运行Pylint模块时,你不必每次都手动输入这些选项和参数。你可以使用该模块的--generate-rcfile命令生成自定义的配置文件。例如,为了避免输出大量的统计信息,将最大行长设置为79个字符,在命令行窗口中输入如下内容,即可生成所需的配置文件:

your pathname>pylint -rn --max-line-length=79 --generate-rcfile >
name.pylintrc

将自定义参数设置放在--generate-rcfile > name.pylintrc命令之前,在扩展名.pylintrc的前面输入配置文件名。你可以像前面所做的那样,生成一个独立的配置文件来评估Python程序的规范性。当生成配置文件时,Pylint模块允许设置配置文件的存储路径,但是默认情况下,它会自动保存在当前的工作目录中。

为了使用自定义的配置文件,你需要在待检查程序前输入自定义配置文件名,并在配置文件名前输入--rcfile。例如,若要在文件myconfig.pylintrc指定的配置下运行pseudonyms_main.py程序,则需要在命令行窗口中输入如下内容:

C:\Python35\Python 3 Stuff\Psych>pylint --rcfile myconfig.pylintrc
pseudonyms_main

3.使用文档字符串描述代码

Pylint模块检测出程序pseudonyms_main.py缺少文档字符串。PEP 257标准指出:文档字符串是一种常放在模块、函数、类和方法定义开头的字符串。文档字符串的作用是简要地描述代码的功能,它可能会包括诸如输入要求等在内的说明信息。下面是一个在函数中使用单行文档字符串的示例,该字符串在一对三引号内:

def circ(r):
    """Return the circumference of a circle with radius of r."""
    c = 2 * r * math.pi
    return c

上面的文档字符串只是为了说明函数的功能,在实际的应用中,文档字符串可能更长,包含的信息也更多。例如,下面是这个函数的多行文档字符串示例,该字符串包含函数的输入和输出参数说明等信息:

     def circ(r):
         """Return the circumference of a circle with radius of r.

         Arguments:
         r – radius of circle

         Returns:
             float: circumference of circle
         """

         c = 2 * r * math.pi
         return c

文档字符串的书写方式会因个人、项目和公司的不同而有所差别。因此,你会发现存在很多相互矛盾的规范。谷歌公司就有独具该公司特色的编程规范。科学界的某些人士喜欢使用NumPy文档字符串书写标准。reStructuresText是一种流行的文档字符串格式化工具,它主要与工具Sphinx结合使用。通过Python代码的文档字符串,这些工具可为项目生成HTML和PDF格式的文档。如果阅读过一些Python模块的字符串文档,你可能看到过Sphinx字符串文档格式工具的使用。在1.11节中,你可以学到一些不同风格的文档字符串编写规范。

利用一款名为pydocstring的免费工具可以检查文档字符串是否符合PEP 257标准。若想把这款工具安装在Windows操作系统和任何其他操作系统上,你可以打开命令行窗口并执行pip install pydocstyle命令(如果操作系统中既安装有Python2,又安装有Python3,那么安装该工具时,你要在命令行窗口中输入pip3)。

为了使用pydocstring工具,先打开命令行窗口,将当前工作目录切换至待检查代码所在的目录。如果在命令行窗口中未指定文件名,pydocstring工具会检查该目录中的所有Python程序,并为每个程序生成相应的检查报告:

C:\Python35\Python 3 Stuff\Psych>pydocstyle
.\OLD_pseudonyms_main.py:1 at module level:
        D100: Missing docstring in public module
.\OLD_pseudonyms_main.py:4 in public function `main`:
        D103: Missing docstring in public function
.\ pseudonyms.py:1 at module level:
        D100: Missing docstring in public module
.\ pseudonyms_main_broken.py:1 at module level:
        D200: Oneline docstring should fit on one line with quotes (found 2)
.\ pseudonyms_main_broken.py:6 in public function `main`:
        D205: 1 blank line required between summary line and description
(found 0)

如果程序的文档字符串满足Python编程规范,那么pydocstring工具不会返回任何内容:

C:\Python35\Python 3 Stuff\Psych>pydocstyle pseudonyms_main_fixed.py

C:\Python35\Python 3 Stuff\Psych>

对于本书的所有项目,我将会在项目代码中使用一些简单的文档字符串,尽量降低注释的使用频率,避免过多注释影响代码的可读性。当然,若想练习文档字符串的编写方法,你可以对前面的示例进行随意拓展,也可以使用pydocstring工具来检查文档字符串的规范性。

4.检查代码风格

下面来了解一下如何使虚假姓名生成器程序的代码更符合PEP 8和PEP 257标准。

将程序pseudonyms_main.py复制一份,把它重命名为pseudonyms_main_fixed.py,使用Pylint模块对它的规范性进行评估:

your_path>pylint --max-line-length=79 pseudonyms_main_fixed

不要使用-rn选项,否则会禁用评分功能。在命令行窗口的底部,你会看到如下输出信息:

Global evaluation
-----------------
Your code has been rated at 3.33/10

现在,根据Pylint模块的检查结果修改程序pseudonyms_main_fixed.py的代码。在下面的例子中,我已经用粗体标注了要更正的地方。为了解决最大行长度不一致引起的问题,我修改了元组的名称。在本书的配套资源中,你可以找到修改后的代码对应的程序pseudonyms_ main_fixed.py。

pseudonyms_main_fixed.py
"""从两个独立的名字元组中随机选择元素来生成有趣的假名"""
import sys
import random

def main():
    """从两个名字元组中随机选择一些名字并输出到屏幕上"""
    print("Welcome to the Psych 'Sidekick Name Picker.'\n")
    print("A name just like Sean would pick for Gus:\n\n")

    first = ('Baby Oil', 'Bad News', 'Big Burps', "Bill 'Beenie-Weenie'",
             "Bob 'Stinkbug'", 'Bowel Noises', 'Boxelder', "Bud 'Lite'",
             'Butterbean', 'Buttermilk', 'Buttocks', 'Chad', 'Chesterfield',
             'Chewy', 'Chigger', 'Cinnabuns', 'Cleet', 'Cornbread',
             'Crab Meat', 'Crapps', 'Dark Skies', 'Dennis Clawhammer',
             'Dicman', 'Elphonso', 'Fancypants', 'Figgs', 'Foncy', 'Gootsy',
             'Greasy Jim', 'Huckleberry', 'Huggy', 'Ignatious', 'Jimbo',
             "Joe 'Pottin Soil'", 'Johnny', 'Lemongrass', 'Lil Debil',
             'Longbranch', '"Lunch Money"', 'Mergatroid', '"Mr Peabody"',
             'Oil-Can', 'Oinks', 'Old Scratch', 'Ovaltine', 'Pennywhistle',
             'Pitchfork Ben', 'Potato Bug', 'Pushmeet', 'Rock Candy',
             'Schlomo', 'Scratchensniff', 'Scut', "Sid 'The Squirts'",
             'Skidmark', 'Slaps', 'Snakes', 'Snoobs', 'Snorki', 'Soupcan Sam',
             'Spitzitout', 'Squids', 'Stinky', 'Storyboard', 'Sweet Tea',
             'TeeTee', 'Wheezy Joe', "Winston 'Jazz Hands'", 'Worms')

    last = ('Appleyard', 'Bigmeat', 'Bloominshine', 'Boogerbottom',
            'Breedslovetrout', 'Butterbaugh', 'Clovenhoof', 'Clutterbuck',
            'Cocktoasten', 'Endicott', 'Fewhairs', 'Gooberdapple',
            'Goodensmith', 'Goodpasture', 'Guster', 'Henderson', 'Hooperbag',
            'Hoosenater', 'Hootkins', 'Jefferson', 'Jenkins',
            'Jingley-Schmidt', 'Johnson', 'Kingfish', 'Listenbee', "M'Bembo",
            'McFadden', 'Moonshine', 'Nettles', 'Noseworthy', 'Olivetti',
            'Outerbridge', 'Overpeck', 'Overturf', 'Oxhandler', 'Pealike',
            'Pennywhistle', 'Peterson', 'Pieplow', 'Pinkerton', 'Porkins',
            'Putney', 'Quakenbush', 'Rainwater', 'Rosenthal', 'Rubbins',
            'Sackrider', 'Snuggleshine', 'Splern', 'Stevens', 'Stroganoff',
            'Sugar-Gold', 'Swackhamer', 'Tippins', 'Turnipseed',
            'Vinaigrette', 'Walkingstick', 'Wallbanger', 'Weewax', 'Weiners',
            'Whipkey', 'Wigglesworth', 'Wimplesnatch', 'Winterkorn',
            'Woolysocks')

         while True:
             first_name = random.choice(first)
             last_name = random.choice(last)

             print("\n\n")
             # 使用“致命错误”字体设置,在IDLE窗口中将输出的姓名颜色设为红色
             print("{} {}".format(first_name, last_name), file=sys.stderr)
             print("\n\n")

             try_again = input("\n\nTry again? (Press Enter else n to quit)\n ")

             if try_again.lower() == "n":
                 break

         input("\nPress Enter to exit.")

     if __name__ == "__main__":
         main()

在满分为10分的情况下,Pylint模块给修改后的程序代码打了10分:

Global evaluation
-----------------
Your code has been rated at 10.00/10 (previous run: 3.33/10, +6.67)

从前面的内容可以看到,当评估程序pseudonyms_main_fix .py时,pydocstyle工具没有产生错误信息。但是,不要错误地认为程序的编程规范良好,甚至认为它已经足够好。例如,下面的文档字符串也可以通过pydocstyle工具的检查:

"""ksjkdls lskjds kjs jdi wllk sijkljs dsdw noiu sss."""

编写简洁且真正有用的文档字符串和注释是一件很困难的事情。我们可以借助PEP 257标准来处理文档字符串,但是注释的样式通常比较灵活,适用范围也相对更为开放。注释太多会产生视觉干扰,从而导致用户反感。过多的注释是不必要的,良好的代码本身就是编写思路的自述。开发者添加注释的原因有很多,如澄清代码意图,避免用户使用程序时出现潜在的错误;提醒用户注意输入数据的度量单位或格式等。若想正确地使用注释,则需要关注别人使用注释的方式,借鉴那些好的注释例子。此外,你还要考虑5年后重新阅读自己的代码时,希望在注释中看到什么内容。

Pylint模块和pydocstyle工具都易于安装和运行,它们可以帮助你更好地学习并遵守Python社区公认的编程标准。当你通过Web论坛寻求帮助,并希望获得友好、温和的回答时,你应该遵循的一个原则就是:在代码发布到论坛之前,先运行Pylint模块,评估代码的规范性。