目录 | 上一节 (1.6 文件) | 下一节 (2.0 处理数据)

1.7 函数

随着程序开始变大,我们会想要有条理地组织这些程序。本节简要介绍函数、库模块以及带有异常的错误处理。

自定义函数

对你要重用的代码使用函数。下面是函数的定义方式:

def sumcount(n):
'''
Returns the sum of the first n integers
'''
total = 0
while n > 0:
total += n
n -= 1
return total

函数调用:

a = sumcount(100)

函数是执行某些任务并返回结果的一系列语句。 return 关键字需要显式指定函数的返回值。

库函数

Python 带有一个大型的标准库。使用 import 访问库模块。示例:

import math
x = math.sqrt(10) import urllib.request
u = urllib.request.urlopen('http://www.python.org/')
data = u.read()

稍后,我们将更详细地介绍库和模块。

错误和异常

函数将错误报告为异常。异常会导致函数中止,如果未处理,可能会导致整个程序终止。

在你的 Python 解释器(REPL)中尝试一下:

>>> int('N/A')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'N/A'
>>>

出于调试的目的,上面的错误信息描述了发生的情况,错误产生的位置以及回溯。该回溯显示导致失败的其它函数调用。

捕获和处理异常

异常可以被捕获并处理。要捕获异常,使用 try - except 语句:

for line in f:
fields = line.split()
try:
shares = int(fields[1])
except ValueError:
print("Couldn't parse", line)
...

该名称 ValueError 必须与你尝试捕获的错误类型匹配。

通常,根据所执行的操作,很难提前确切地知道可能会发生哪种错误。不管是好是坏,通常会添加在程序意外奔溃后的异常处理(示例:”天哪,我们忘记捕获错误了。我们应该处理错误的。“)。

触发异常

要触发异常,请使用 raise 语句:

raise RuntimeError('What a kerfuffle')

这将导致程序因异常回溯而中止,除非该异常通过 try-except 代码块捕获。

% python3 foo.py
Traceback (most recent call last):
File "foo.py", line 21, in <module>
raise RuntimeError("What a kerfuffle")
RuntimeError: What a kerfuffle

练习

练习 1.29:定义一个函数

尝试定义一个简单的函数:

>>> def greeting(name):
'Issues a greeting'
print('Hello', name) >>> greeting('Guido')
Hello Guido
>>> greeting('Paula')
Hello Paula
>>>

如果函数的第一条语句是字符串,那么它被当做文档字符串。尝试输入一个命令来显示该文档字符串,例如 help(greeting)

练习 1.30:将脚本转换为函数

把你在 练习1.27pcost.py 程序编写的代码放到 portfolio_cost(filename) 函数里面。此函数以文件名作为输入,读取文件中的投资组合数据,把投资组合总的费用作为浮点数返回。

要使用你的函数,请修改程序,使其看起来像下面这样:

def portfolio_cost(filename):
...
# Your code here
... cost = portfolio_cost('Data/portfolio.csv')
print('Total cost:', cost)

运行程序时,你应该会看到和以前一样的输出。运行程序后,你也可以输入一下命令来交互式地调用函数:

bash $ python3 -i pcost.py

这将允许你从交互模式调用函数:

>>> portfolio_cost('Data/portfolio.csv')
44671.15
>>>

能够交互地试验代码对调试和测试非常有用。

练习 1.31:错误处理

如果你在缺少某些字段的文件上使用函数,会发生什么情况?

>>> portfolio_cost('Data/missing.csv')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pcost.py", line 11, in portfolio_cost
nshares = int(fields[1])
ValueError: invalid literal for int() with base 10: ''
>>>

在这一点上,你面临一个决定:要使程序正常工作,你可以通过消除错误行(bad lines)来清理原始输入文件,或者修改代码,以某种方式处理错误行。

请修改 pcost.py 程序以捕获异常,打印警告信息然后继续处理文件余下部分。

练习1.32:使用库函数

Python 带有一个拥有大量有用函数的大型标准库。csv 模块是一个在这里可能有用的库。无论何时,每当必须必须使用 CSV 数据文件时,都应使用 csv 模块。下面是一个有关 csv 模块是如何工作的示例:

>>> import csv
>>> f = open('Data/portfolio.csv')
>>> rows = csv.reader(f)
>>> headers = next(rows)
>>> headers
['name', 'shares', 'price']
>>> for row in rows:
print(row) ['AA', '100', '32.20']
['IBM', '50', '91.10']
['CAT', '150', '83.44']
['MSFT', '200', '51.23']
['GE', '95', '40.37']
['MSFT', '50', '65.10']
['IBM', '100', '70.44']
>>> f.close()
>>>

csv 模块有一个非常棒的功能——它处理各种底层细节,例如引号和适当的逗号拆分。在上面的输出中,你会注意到它从第一列的名称(names)中删除了双引号。

修改你的 pcost.py 程序,以使用 csv 模块进行解析,然后尝试运行前面的示例。

练习 1.33:从命令行读取

pcost.py 程序中,输入文件的名称已经被硬编码到代码中:

# pcost.py

def portfolio_cost(filename):
...
# Your code here
... cost = portfolio_cost('Data/portfolio.csv')
print('Total cost:', cost)

虽然用于学习和测试还行,但在实际的程序中,你可能不会这么做。

相反,你可以把文件名作为参数传递给脚本。尝试按以下步骤修改程序的底部:

# pcost.py
import sys def portfolio_cost(filename):
...
# Your code here
... if len(sys.argv) == 2:
filename = sys.argv[1]
else:
filename = 'Data/portfolio.csv' cost = portfolio_cost(filename)
print('Total cost:', cost)

sys.argv 是一个列表,该列表包含了在命令行上传递的参数(如果有)。

要运行程序,你需要从终端(terminal)运行 Python。

示例,从 Unix系统中的 bash 运行:

bash % python3 pcost.py Data/portfolio.csv
Total cost: 44671.15
bash %

目录 | 上一节 (1.6 文件) | 下一节 (2.0 处理数据)

注:完整翻译见 https://github.com/codists/practical-python-zh

翻译:《实用的Python编程》01_07_Functions的更多相关文章

  1. 翻译:《实用的Python编程》InstructorNotes

    实用的 Python 编程--讲师说明 作者:戴维·比兹利(David Beazley) 概述 对于如何使用我的课程"实用的 Python 编程"进行教学的问题,本文档提供一些通用 ...

  2. 翻译:《实用的Python编程》README

    欢迎光临 大约 25 年前,当我第一次学习 Python 时,发现 Python 竟然可以被高效地应用到各种混乱的工作项目上,我立即被震惊了.15 年前,我自己也将这种乐趣教授给别人.教学的结果就是本 ...

  3. 翻译:《实用的Python编程》05_02_Classes_encapsulation

    目录 | 上一节 (5.1 再谈字典) | 下一节 (6 生成器) 5.2 类和封装 创建类时,通常会尝试将类的内部细节进行封装.本节介绍 Python 编程中有关封装的习惯用法(包括私有变量和私有属 ...

  4. 翻译:《实用的Python编程》04_02_Inheritance

    目录 | 上一节 (4.1 类) | 下一节 (4.3 特殊方法) 4.2 继承 继承(inheritance)是编写可扩展程序程序的常用手段.本节对继承的思想(idea)进行探讨. 简介 继承用于特 ...

  5. 翻译:《实用的Python编程》01_02_Hello_world

    目录 | 上一节 (1.1 Python) | 下一节 (1.3 数字) 1.2 第一个程序 本节讨论有关如何创建一个程序.运行解释器和调试的基础知识. 运行 Python Python 程序始终在解 ...

  6. 翻译:《实用的Python编程》03_03_Error_checking

    目录 | 上一节 (3.2 深入函数) | 下一节 (3.4 模块) 3.3 错误检查 虽然前面已经介绍了异常,但本节补充一些有关错误检查和异常处理的其它细节. 程序是如何运行失败的 Python 不 ...

  7. 翻译:《实用的Python编程》03_04_Modules

    目录 | 上一节 (3.3 错误检查) | 下一节 (3.5 主模块) 3.4 模块 本节介绍模块的概念以及如何使用跨多个文件的函数. 模块和导入 任何一个 Python 源文件都是一个模块. # f ...

  8. 翻译:《实用的Python编程》03_05_Main_module

    目录 | 上一节 (3.4 模块) | 下一节 (3.6 设计讨论) 3.5 主模块 本节介绍主程序(主模块)的概念 主函数 在许多编程语言中,存在一个主函数或者主方法的概念. // c / c++ ...

  9. 翻译:《实用的Python编程》04_01_Class

    目录 | 上一节 (3.6 设计讨论) | 下一节 (4.2 继承) 4.1 类 本节介绍 class 语句以及创建新对象的方式. 面向对象编程(OOP) 面向对象编程是一种将代码组织成对象集合的编程 ...

随机推荐

  1. flutter--Dart基础语法(一)

    一.前言 Flutter 是 Google 开源的 UI 工具包,帮助开发者通过一套代码库高效构建多平台精美应用,Flutter 开源.免费,拥有宽松的开源协议,支持移动.Web.桌面和嵌入式平台. ...

  2. Excel 一张表最多能装下多少行多少列数据?

    一个工作簿可以装下255张,那么每张工作表可以装下多少行多少列数据呢? 1.任意打开或新建一个Excel文档. 2.在文档中,找到其左上角的"文件"按钮,点击选择"选项& ...

  3. JVM 线上故障排查

    JVM 线上故障排查 Linux 1.1 CPU 1.2 内存 1.3 存储 1.4 网络 一.CPU 飚高 寻找原因 二.内存问题排查 三.一般排查问题的方法 四.应用场景举例 4.1 怎么查看某个 ...

  4. Java 复习整理day09

    package com.it.demo01_thread; /* 案例: 多线程简介. 概述: 指的是进程有多条执行路径, 统称叫: 多线程. 进程: 指的是可执行程序, 文件(例如: .exe) 大 ...

  5. cassandra权威指南读书笔记--Cassandra架构(3)

    分阶段事件驱动架构 SEDASEDA(Staged Event-Driven Architecture)的核心思想是把一个请求处理过程分成几个Stage,不同资源消耗的Stage使用不同数量的线程来处 ...

  6. PTA 乙 1001

    1001 害死人不偿命的(3n+1)猜想 题目描述 卡拉兹(Callatz)猜想: 对任何一个正整数 n,如果它是偶数,那么把它砍掉一半:如果它是奇数,那么把 (3n+1) 砍掉一半.这样一直反复砍下 ...

  7. 线程不安全(Arraylist,HashSet,HashMap)和写时复制

    package com.yangyuanyuan.juc1205; import java.util.List; import java.util.Map; import java.util.Set; ...

  8. 牛客编程巅峰赛S2第10场 - 钻石&王者 C.牛牛的路径和 (位运算,dfs)

    题意:给你节点数为\(n\)的树,每个节点都有自己的权值,求所有路径的上的点的权值按位与的和. 题解:题目给的数据很大,我们不能直接去找.因此我们可以枚举二进制\([1,20]\)的每一位,然后再枚举 ...

  9. Codeforces #640 div4 F~G (构造二连弹)

    题意:求一个只由\(01\)组成的字符串,使得它所有长度为\(2\)的子串满足:每对子串的数字和为\(0,1,2\)的个数为\(a,b,c\). 题解:我们先考虑子串数字和为\(1\)的情况,构造出一 ...

  10. Light Bulb ZOJ - 3203 三分

    三分: 和二分非常类似的一个算法,与二分不同的是 二分是单调的,而三分是一个先增后减或者先减后增 三分可以求出峰值. 注意三分一定是严格单调的,不能有相等的情况. 讲个例题: 题目 题意: 一个人发现 ...