简介
在这部分内容中,我们将更深入地探讨编写 Python 脚本的实践。
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)
...
在这个练习中,我们将采用这个程序,并围绕函数的使用对其进行更严格的组织。
修改你的 report.py
程序,使所有主要操作(包括计算和输出)都由一组函数来执行。具体要求如下:
print_report(report)
,用于打印报告。将你程序的最后一部分打包成一个名为 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中练习更多实验来提升你的技能。