Python 脚本编写实践

PythonPythonBeginner
立即练习

This tutorial is from open-source community. Access the source code

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

在这部分内容中,我们将更深入地探讨编写 Python 脚本的实践。

什么是脚本?

脚本是一个运行一系列语句然后停止的程序。

## program.py

语句1
语句2
语句3
...

到目前为止,我们编写的大多都是脚本。

一个问题

如果你编写了一个实用的脚本,它的特性和功能会不断增加。你可能想要将它应用于其他相关问题。随着时间的推移,它可能会变成一个关键应用程序。而如果你不小心,它可能会变成一个巨大的混乱局面。所以,让我们开始组织起来吧。

定义事物

名称在使用之前必须始终先进行定义。

def square(x):
    return x*x

a = 42
b = a + 2     ## 要求 `a` 已定义

z = square(b) ## 要求 `square` 和 `b` 已定义

顺序很重要。你几乎总是将变量和函数的定义放在接近顶部的位置。

定义函数

将与单个任务相关的所有代码放在一个地方是个好主意。使用函数。

def read_prices(filename):
    prices = {}
    with open(filename) as f:
        f_csv = csv.reader(f)
        for row in f_csv:
            prices[row[0]] = float(row[1])
    return prices

函数还简化了重复操作。

oldprices = read_prices('oldprices.csv')
newprices = read_prices('newprices.csv')

什么是函数?

函数是一系列有名称的语句。

def funcname(args):
  statement
  statement
...
  return result

任何Python语句都可以用在函数内部。

def foo():
    import math
    print(math.sqrt(2))
    help(math)

Python中没有特殊语句(这使得它很容易记忆)。

函数定义

函数可以按任何顺序进行定义

def foo(x):
    bar(x)

def bar(x):
    statements

## 或者
def bar(x):
    statements

def foo(x):
    bar(x)

在程序执行期间,函数必须仅在实际被使用(或调用)之前进行定义。

foo(3)        ## foo 必须已经被定义

从风格上来说,函数以自底向上的方式进行定义可能更为常见。

自底向上的风格

函数被视为构建模块。较小/较简单的模块先出现。

## myprogram.py
def foo(x):
 ...

def bar(x):
 ...
    foo(x)          ## 在上面定义
 ...

def spam(x):
 ...
    bar(x)          ## 在上面定义
 ...

spam(42)            ## 使用这些函数的代码出现在最后

后面的函数基于前面的函数构建。同样,这只是一种风格问题。在上面的程序中,唯一重要的是对 spam(42) 的调用要放在最后。

函数设计

理想情况下,函数应该是一个黑盒。它们应该只对传递的输入进行操作,避免使用全局变量和产生神秘的副作用。你的主要目标是:模块化可预测性

文档字符串

以文档字符串的形式包含文档是个好习惯。文档字符串是紧跟在函数名之后编写的字符串。它们为 help()、集成开发环境(IDE)和其他工具提供信息。

def read_prices(filename):
    '''
    从包含名称、价格数据的CSV文件中读取价格
    '''
    prices = {}
    with open(filename) as f:
        f_csv = csv.reader(f)
        for row in f_csv:
            prices[row[0]] = float(row[1])
    return prices

编写文档字符串的一个好习惯是用一句话简短总结函数的功能。如果需要更多信息,可以包含一个简短的使用示例以及对参数更详细的描述。

类型注释

你还可以向函数定义添加可选的类型提示。

def read_prices(filename: str) -> dict:
    '''
    从包含名称、价格数据的CSV文件中读取价格
    '''
    prices = {}
    with open(filename) as f:
        f_csv = csv.reader(f)
        for row in f_csv:
            prices[row[0]] = float(row[1])
    return prices

这些提示在操作上没有任何作用。它们纯粹是提供信息的。然而,集成开发环境(IDE)、代码检查器和其他工具可能会利用它们做更多的事情。

在第2节中,你编写了一个名为 report.py 的程序,它打印出一份显示股票投资组合表现的报告。这个程序由一些函数组成。例如:

## report.py
import csv

def read_portfolio(filename):
    '''
    将股票投资组合文件读入一个字典列表,字典的键为
    name、shares 和 price。
    '''
    portfolio = []
    with open(filename) as f:
        rows = csv.reader(f)
        headers = next(rows)

        for row in rows:
            record = dict(zip(headers, row))
            stock = {
                'name' : record['name'],
               'shares' : int(record['shares']),
                'price' : float(record['price'])
            }
            portfolio.append(stock)
    return portfolio
...

然而,程序中也有一些部分只是执行了一系列按脚本编写的计算。这段代码出现在程序的末尾附近。例如:

...

## 输出报告

headers = ('Name', 'Shares', 'Price', 'Change')
print('%10s %10s %10s %10s'  % headers)
print(('-' * 10 +'') * len(headers))
for row in report:
    print('%10s %10d %10.2f %10.2f' % row)
...

在这个练习中,我们将采用这个程序,并围绕函数的使用对其进行更严格的组织。

练习3.1:将程序构建为函数集合

修改你的 report.py 程序,使所有主要操作(包括计算和输出)都由一组函数来执行。具体要求如下:

  • 创建一个函数 print_report(report),用于打印报告。
  • 修改程序的最后一部分,使其仅包含一系列函数调用,而不进行其他计算。
✨ 查看解决方案并练习

练习3.2:为程序执行创建一个顶级函数

将你程序的最后一部分打包成一个名为 portfolio_report(portfolio_filename, prices_filename) 的单一函数。让这个函数能够正常工作,以便以下函数调用能像之前一样生成报告:

portfolio_report('/home/labex/project/portfolio.csv', '/home/labex/project/prices.csv')

在这个最终版本中,你的程序将仅仅是一系列函数定义,最后跟着一个对 portfolio_report() 的单一函数调用(它会执行程序中涉及的所有步骤)。

通过将你的程序转换为一个单一函数,就可以很容易地在不同输入上运行它。例如,在运行你的程序后,交互式地尝试以下语句:

>>> portfolio_report('/home/labex/project/portfolio2.csv', '/home/labex/project/prices.csv')
... 查看输出...
>>> files = ['/home/labex/project/portfolio.csv', '/home/labex/project/portfolio2.csv']
>>> for name in files:
        print(f'{name:-^43s}')
        portfolio_report(name, '/home/labex/project/prices.csv')
        print()

... 查看输出...
>>>
✨ 查看解决方案并练习

说明

Python 使得编写相对无结构的脚本代码变得非常容易,在这种代码中,你只需在一个文件中按顺序编写一系列语句。从整体来看,只要有可能,使用函数几乎总是更好的选择。在某个时候,那个脚本会不断增长,而你会希望自己当初能有更多的组织性。另外,一个鲜为人知的事实是,如果你使用函数,Python 的运行速度会快一点。

总结

恭喜你!你已经完成了脚本实验。你可以在LabEx中练习更多实验来提升你的技能。