《Python 3程序开发指南(第2版•修订版)》——1.3 实例

简介:

本节书摘来自异步社区《Python 3程序开发指南(第2版•修订版)》一书中的第1章,第1.3节,作者[英]Mark Summerfield,王弘博,孙传庆 译,更多章节内容可以访问云栖社区“异步社区”公众号查看。

1.3 实例

在前面的几节中,我们介绍了足以编写实际程序的Python知识与技术。在这一节中,我们将介绍两个完整的程序,这些程序只涉及前面已经讲过的知识。一方面是为了展示前面所学的知识可以完成什么任务,一方面也是为了巩固前面所学的知识。

在后面的章节中,我们会逐渐学习更多的Python知识与库模块,以便于编写出比这里展示的程序更精确与更强壮的程序——但是首先我们必须先掌握这些基础知识。

1.3.1 bigdigits.py

这里给出的第一个程序非常短小,尽管该程序也有一些微妙之处,包括列表组成的列表等。这个程序的功能是:在命令行中提供一个数值,之后该程序会使用“大”数字向控制台输出该数值。

在大量用户共享高速行式打印机的站点上,使用这种技术是很常见的做法,即为每个用户的打印作业打印一个引导页,使其包含用户名与其他有助于区分不同用户的详细资料。

我们分3个部分查看该程序的代码:import部分;创建列表(其中存放程序要使用的数据)部分;处理部分本身。不过,我们首先看一下运行的效果:

bigdigits.py 41072819
   *     *     ***    *****   ***    ***    *     ****
  **    **    *   *       *  *   *  *   *  **   *    *
 * *     *   *     *     *   *  *   *   *   *   *    *
*  *     *   *     *    *      *     ***    *     ****
******   *   *     *   *      *     *   *   *        *
   *     *    *   *   *      *      *   *   *        *
   *    ***    ***    *      *****   ***   ***       *

我们没有展示控制台提示符(或UNIX用户的./),而是将这些内容默认为已经存在的。

import sys

由于我们必须从命令行中读入一个参数(也就是要输出的数值),我们需要访问sys.argv列表,因此我们从导入sys模块开始。

我们以字符串列表的形式展示每个数值,比如,下面展示的是zreo:

Zero = ["  ***  ",
        " *   * ",
        "*     *",
        "*     *",
        "*     *",
        " *   * ",
        "  ***  "]

这里需要注意的一个细节是,Zero的字符串列表表示形式跨越了多个行。通常,Python语句只占用一行,但是也可以跨越多行,比如使用圆括号包含的表达式、列表、集合、字典字面值、函数调用参数列表以及多行语句(除最后一行之外,每行的行终结符都使用反斜线进行引导并转义处理)。上面的这些Python语句可以跨越任意多的行,代码缩排也并不会影响第二行以及后续的行。

用于表示数值的每个列表包含7个字符串,在对同一个数值的表示中,这些字符串是等宽的,而表示不同数值的字符串宽度不同。表示其他数值的列表在形式上与上面给出的Zero类似。下面给出的几个表示主要是出于紧致性考虑,因而不那么形象和清晰:

One = [" * ", "** ", " * ", " * ", " * ", " * ", "***"]
Two = [" *** ", "*   *", "*  * ", "  * ",  " *   ", "*    ", "*****"]
# ...
Nine = [" ****", "*  *", "*  *", " ****", "   *", "    *", "   *"]

我们还需要的最后一个数据结构是所有数字列表组成的列表:

Digits = [Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine]

我们也可以直接创建Digits列表,而不必创建额外的变量,例如:

Digits = [
    ["  ***  ", " *   * ", "*     *", "*     *", "*     *",
     " *   * ", "  ***  "], # Zero
    [" * ", "** ", " * ", " * ", " * ", " * ", "***"], # One
    # ...
    [" ****", "*   *", "*   *", " ****", "    *", "    *",
     "    *"] # Nine
    ]

我们更愿意使用单独的变量来分别表示每个数值,一方面是为了便于理解,另一方面是因为使用变量来表示看起来更整洁。

下面一起展示了余下的代码部分,以便你在阅读其后的解释之前自己可以设想其工作方式。

try:
    digits = sys.argv[1]
    row = 0
    while row < 7:
        line = ""
        column = 0
        while column < len(digits):
            number = int(digits[column])
            digit = Digits[number]
            line += digit[row] + " "
            column += 1
        print(line)
        row += 1
except IndexError:
    print("usage: bigdigits.py <number>")
except ValueError as err:
    print(err, "in", digits)

上面这段代码整体包含在一个异常处理模块中,并可以捕获两个异常。该段代码首先取回程序的命令行参数,与所有Python列表类似,sys.argv列表的索引项是从0开始的,索引位置为0的数据项是调用的程序名,因此,在一个运行的程序中,该列表总是至少包含一项。如果没有给定参数,我们会在一个单数据项的列表中尝试访问第二个数据项,并导致产生一个IndexError异常。如果发生这种情况,控制流立即转向相应的异常处理块,这里只是简单地打印出程序的用法。在Try块结束后,程序继续执行,但是由于已经没有更多的代码,因此程序只是简单地退出。

如果没有发生IndexError异常,那么digits字符串会存放命令行参数,如果一切正常,就应该是一个数字字符序列。(记住,要素2中讲过,标识符对大小写敏感,因此,digits与Digits是不同的。)每个大数都使用7个字符串表示,为正确地输出数值,我们必须首先输出每个数字的顶行,之后是下一行,依此类推,直至输出所有的7行。我们使用一个while循环,以便逐行迭代。我们也可以采用另一种方法:for row in (0, 1, 2, 3, 4, 5, 6):,后面我们还将看到一种更好的、使用内置的range()函数的方法。

我们使用line字符串来存放所有数字的行字符串,之后根据列进行循环,也就是说,根据命令行参数中每个相继的字符进行循环。我们使用digits[column]取回每个字符,并将数字转换为称为number的整数。如果转换失败,就会产生一个ValueError异常,控制流立即转向相应的异常处理模块。这种情况下,我们将打印出错误消息,并在try块之后恢复控制。与前面类似,由于没有其他代码等待执行,因此程序只是简单地退出。

如果转换成功,我们就使用number作为索引来存取digits列表,并从其中抽取字符串列表digit,之后我们从这一列表中将相应的字符串添加到我们正在构建的行,并添加两个空格,以便在数字之间添加水平分隔。

内部的while循环每次结束时,我们会打印出刚构建好的行。理解这一程序的关键之处在于我们将每个数字的row字符串添加到当前row的行。读者可以尝试运行该程序,以便理解其运作方式。在章后练习中,我们将再次讲到该程序,以便对其输出进行稍许改变。

1.3.2 generate_grid.py

我们频繁面临的需求是测试数据的生成。由于不同场景下测试数据变化巨大,因此无法找到一个满足所有测试数据需求的通用程序。由于编写与修改Python程序都很容易, Python经常被用于生成测试数据。在这一小节中,我们将创建一个生成随机整数组成的网格的程序,用户可以规定需要多少行、多少列,以及整数所在的区域。我们首先从一个运行实例开始:

generate_grid.py
rows: 4x
invalid literal for int() with base 10: '4x'
rows: 4
columns: 7
minimum (or Enter for 0): -100
maximum (or Enter for 1000):
       554       720       550       217       810       649        912
       -24       908       742       -65       -74        724        825
       711       968       824       505       741        55        723
       180       -60       794       173       487         4        -35

该程序以交互式的方式运行,最开始在输入行数时,由于输入的行数有误,导致程序打印一条错误消息,并要求用户重新输入行数。对于maximum,我们只是简单地按Enter键,以便接受默认值。

我们将分别解读该程序的4个部分:import、函数get_int()的定义(此函数比要素8中展示的类似函数更复杂)、用户交互以便获取要使用的值、处理过程本身。

import random

我们需要random模块,以便访问其中的random.randinit()函数。

def get_int(msg, minimum, default):
    while True:
        try:
            line = input(msg)
            if not line and default is not None:
                return default
            i = int(line)
            if i < minimum:
                print("must be >=", minimum)
            else:
                return i
        except ValueError as err:
            print(err)

这一函数需要3个参数:一个消息字符串、一个最小值、一个默认值。如果用户只是简单地按Enter键,就有两种可能性。如果default为None,也就是说没有给定默认值,那么控制流将转到int()行,在该处转换将失败(因为无法转换为整数),并产生一个ValueError异常;如果default非None,就返回该值。否则,函数将尝试把用户输入的文本转换为整数,如果转换成功,接下来将检查该整数是否至少等于指定的minimum。

因此,该函数的返回总是两种情况,或者是default(用户只是按Enter键),或者是一个有效的整数(大于或等于指定的minimum)。

rows = get_int("rows: ", 1, None)
columns = get_int("columns: ", 1, None)
minimum = get_int("minimum (or Enter for 0): ", -1000000, 0)
default = 1000
if default < minimum:
    default = 2 * minimum
maximum = get_int("maximum (or Enter for " + str(default) + "): ",
                    minimum, default)

通过我们的get_int()函数,可以很容易地获取行数、列数以及用户需要的最小随机数值。对于给定默认值None的行数与列数,也就是没有指定默认值的情况,用户必须输入一个整数。对于minimum,我们提供的默认值为0:对于maximum,我们提供的默认值为1000或minimum的2倍(如果minimum大于或等于1000)。

与前面的例子类似,函数调用参数列表可以跨越任意数量的行数,并且缩排与第二行及后继行无关。

在确定用户需要的具体行数、列数以及随机数的最大值与最小值后,就可以进行具体的随机数生成过程:

row = 0
while row < rows:
    line = ""
    column = 0
    while column < columns:
        i = random.randint(minimum, maximum)
        s = str(i)
        while len(s) < 10:
            s = " " + s
        line += s
        column += 1
    print(line)
    row += 1

为生成随机数网格,我们使用3个while循环,外部循环以行数进行循环,中间循环以列数进行循环,内部循环则以字符进行循环。在中间循环中,我们获取指定范围内的随机数,并将其转换为字符串。内部while循环用于对字符串进行填充(填充数据为空格),以便每个数字都使用10个字符的字符串表示,对每一行,使用字符串line来累积数值,在每一列的数字添加完毕后,就打印出该行表示的数字,至此,第二个程序功能讲解完毕。

Python提供了非常高级的格式化功能,以及对for ... in循环的良好支持能力,因此,bigdigits.py与generate_grid.py这两个程序的更真实的版本会使用for ... in循环,generate_grid.py程序将使用Python的字符串格式化功能,而不是像这里这样不带修饰地使用空格进行填充。但是在本章中,我们将自己约束在使用本章介绍的8个关键要素进行程序设计,并且这8个要素对编写完整而有用的程序也已足够。在接下来的每一章中,我们都将学习Python的一些新特性,因此,随着本书内容的推进,所看到的程序将逐步复杂起来。

相关文章
|
1月前
|
测试技术 Python
Python中的装饰器:概念、用法及实例
【2月更文挑战第25天】 装饰器在Python中是一种强大的工具,它允许我们在不修改原始函数代码的情况下,增加函数的功能。本文将深入探讨装饰器的概念,解析其工作原理,并通过实例来展示如何在Python中使用装饰器。
|
1月前
|
存储 缓存 Python
Python中的装饰器:概念、用法和实例
【2月更文挑战第25天】 在Python编程中,装饰器是一种强大的工具,它允许我们在不修改原始函数代码的情况下,增加或修改函数的行为。本文将深入探讨装饰器的概念、用法以及通过实例来使其更易于理解。我们将看到,装饰器不仅仅是语法糖,而是一种可以极大提高代码复用性和可读性的有效工具。
|
1月前
|
Python
Python 中的装饰器:概念、用法和实例
【2月更文挑战第23天】 在编程世界中,装饰器是一种强大的工具,它允许我们在不改变现有代码的情况下增加或修改函数和类的行为。本文将深入探讨 Python 中装饰器的概念、用法以及如何创建自定义装饰器。我们将通过实例来演示装饰器的实用性和灵活性,帮助读者更好地理解和应用这一技术。
|
1月前
|
Linux 数据安全/隐私保护 iOS开发
python如何将程序编译成exe
python如何将程序编译成exe
32 0
|
30天前
|
存储 算法 数据处理
使用Python编写高效的数据处理程序
在当今信息爆炸的时代,数据处理变得越来越重要。本文将介绍如何使用Python语言编写高效的数据处理程序,包括利用Python内置的数据结构和函数、优化算法和并行处理等技术,帮助开发者更好地处理和分析大规模数据。
|
1天前
|
存储 机器学习/深度学习 数据可视化
Python面板时间序列数据预测:格兰杰因果关系检验Granger causality test药品销售实例与可视化
Python面板时间序列数据预测:格兰杰因果关系检验Granger causality test药品销售实例与可视化
39 6
|
1天前
|
机器学习/深度学习 数据可视化 算法
PYTHON用决策树分类预测糖尿病和可视化实例
PYTHON用决策树分类预测糖尿病和可视化实例
11 0
|
2天前
|
算法 数据可视化 Python
Python中LARS和Lasso回归之最小角算法Lars分析波士顿住房数据实例
Python中LARS和Lasso回归之最小角算法Lars分析波士顿住房数据实例
10 0
|
4天前
|
Python
python学习12-类对象和实例对象
python学习12-类对象和实例对象
|
17天前
|
分布式计算 算法 搜索推荐
优化 Python 程序的五大技巧
本文介绍了优化 Python 程序的五大技巧,涵盖了代码结构优化、算法选择、内置函数利用、库的使用以及并行处理等方面。通过对这些技巧的实践,可以提升 Python 程序的性能和效率,从而更好地满足各类应用的需求。

热门文章

最新文章