在程序设计中,函数是指用于进行某种计算的一系列语句的有名称的组合。定义一个函数时,需要指定函数的名称并写下一系列程序语句。之后,就可以使用名称来“调用”这个函数

3.1函数调用

一个函数调用的例子

>>> type(42)

这个函数的名称是type,括号中的表达式我们称之为函数的参数。这个函数调用的结果是求得参数的类型。

我们通常说函数“接收”参数,并“返回”结果。这个结果也称为返回值

3.2数学函数

Python有一个数学计算模块,提供了大多数常用的数学函数。模块是指包含一组相关的函数的文件。

要想使用模块中的函数,需要先使用import语句将它导入运行环境

>>> import math

这个语句将会创建一个名为math的模块对象。如果显示这个对象,可以看到它的一些信息:

>>> math

模块对象包含了该模块中定义的函数和变量。若要访问其中一个函数,需要同时指定模块名称和函数名称,用一个句点(.)分隔。这个格式称为句点表示法(dot notation)

>>> radians=0.7

>>> height=math.sin(radians)>>>height0.644217687237691

3.3组合

到现在为止,我们已经分别了解了程序的基本元素——变量、表达式和语句,但还没有接触如何将它们有机地组合起来。

程序设计语言最有用的特性之一就是可以将各种小的构建块(building

block)组合起来。例如,函数的参数可以是任何类型的表达式,包括算术操作符:

>>> x = math.sin(degrees / 360.0 * 2 * math.pi)

甚至还包括函数调用:

>>> math.exp(math.log(x+1))

基本上在任何可以使用值的地方,都可以使用任意表达式,只有一个例外:赋值表达式的左边必须是变量名称,在左边放置任何其它的表达式都语法错误(后面还会看到这条规则的例外情况)

3.4添加新函数

我们可以自己添加新的函数。函数定义指定新函数的名称,并提供一系列程序语句,当函数被调用时,这些语句会顺序运行。

下面是一个例子:

>>>def print_lyrics():

... print("I'm a lumberjack,and I'm okay")

... print("I sleep all night and I Work all day.")

def是一个关键字,表示接下来是一个函数定义。这个函数的名称是print_lyrics。函数名称的书写规则与变量名称一:字母、数字、下划线是合法的,但第一个字符不能是数字。关键字不能作为函数名,而且我们应用尽量避免函数和变量同名。函数名后的空括号表示它不接收任何参数。

有所思:如果函数名与变量名一样会发生什么情况呢?

假如有一个变量a=1,后面又定一个函数也叫a,执行1个print语句,这时候变量名a指向的是函数对象呢,还是int类型的1呢?我猜肯定指向的是函数a,因为python是按顺序执行的语句,如果同一个变量名被重复定义,则以最后一次定义的内容为准。

>>> a =1

>>>type(a)

>>>def a():

... print("函数a")

...>>>type(a)

函数定义的第一行称为函数头(header),其它部分称为函数体(body)。函数头应该以冒号结束,函数体则应当整体缩进一级。依照惯例,缩进总是使用4个空格,函数体的代码语句行数不限。

本例中的print语句里的字符串使用双引号括起来。单引号和双引号的作用相同。大部分情况下,人们都使用单引号,只在本例中这样的特殊情况下才使用双引号。本例中的字符串里本身就存在单引号(这里的单引号作为缩略符号用)。

代码中所有的引号(包括双引号和单引号)都必须是”直引号”,通常在键盘上Enter键附近。而”斜引号”,在Python中是非法的。

如果在交互模式里输入函数定义,则解释器会输出省略号(...)提示用户当前的定义还没有结束:

>>>def print_lyrics():

... print("I'm a lumberjack,and I'm okay")

... print("I sleep all night and I Work all day.")

...

想要结束这个函数的定义,需要输入一个空行。

定义一个函数会创建一个函数对象,其类型是”function”。

>>>print(print_lyrics)

>>>type(print_lyrics)

调用新创建的函数的方式,与调用内置函数是一样的:

>>>print_lyrics()

I'm a lumberjack,and I'm okay

I sleep all night and I Work all day.

定义好一个函数之后,就可以在其它函数中调用它。例如,若想重复上面的歌词,我们可以写一个repeat_lyrics函数:

>>>def repeat_lyrics():

... print_lyrics()

... print_lyrics()

然后调用repeat_lyrics:

>>>repeat_lyrics()

I'm a lumberjack,and I'm okay

I sleep all night and I Work all day.

I'm a lumberjack,and I'm okay

I sleep all night and I Work all day.

3.5 定义和使用

将前面一节的代码片段整合起来,整个流程就像下面这个样子:

def print_lyrics():

print("I'm a lumberjack,and I'm okay")

print("I sleep all night and I Work all day.")

def repeat_lyrics():

print_lyrics()

print_lyrics()

repeat_lyrics()

这个程序包含两个函数定义:print_lyrics和repeat_lyrics。函数定义的执行方式和其他语句一样,不同的是执行后会创建函数对象。函数体里面的语句并不会立即运行,而是等到函数被调用时才执行。函数定义不会产生任何输出。

你可能已经猜到,必须先创建一个函数,才能运行它。换言之,函数定义必须在函数被调用之前先运行。

练习1:

作为练习,将程序的最后一行移动到行首,于是函数调用会先于函数定义执行。运行程序并查看会有什么样的错误信息。

repeat_lyrics()

def print_lyrics():

print("I'm a lumberjack,and I'm okay")

print("I sleep all night and I Work all day.")

def repeat_lyrics():

print_lyrics()

print_lyrics()

运行程序报错如下:

NameError: name 'repeat_lyrics' is not defined

练习2:

现在将函数调用那一行放回到末尾,并将函数print_lyrics

的定义移到函数repeat_lyrics 定义之后。这时候运行程序会发生什么?

def repeat_lyrics():

print_lyrics()

print_lyrics()

def print_lyrics():

print("I'm a lumberjack,and I'm okay")

print("I sleep all night and I Work all day.")

repeat_lyrics()

我以为会报错,提示print_lyrics函数没有定义,为什么没有报错呢?

打开程序的debug模式调试程序,发现程序的执行顺序是这样的

def repeat_lyrics(): #1print_lyrics()#4print_lyrics()#7def print_lyrics():#2print("I'm a lumberjack,and I'm okay")#5 #8print("I sleep all night and I Work all day.")#6 #9repeat_lyrics()#3

为什么练习1会报错,而练习2却能正常运行呢?

原因就是:函数定义必须在函数被调用之前先运行。函数体里面的语句并不会立即运行,而是等到函数被调用时才执行

在练习2中,在repeat_lyrics函数的函数体中,调用print_lyrics函数,而在执行repeat_lyrics函数前(#3),print_lyrics已经被定义过了(#2),所以程序不会出错。

而在练习1中,程序自上而下运行,首先执行调用repeat_lyrics函数的语句,而在此条语句之前又没有执行定义repeat_lyrics函数的语句所以会出错。

举一反三,下面这样执行程序也会报错,因为print_lyrics函数还没有被定义,就已经被调用。

def repeat_lyrics():

print_lyrics()

print_lyrics()

repeat_lyrics()

def print_lyrics():

print("I'm a lumberjack,and I'm okay")

print("I sleep all night and I Work all day.")

NameError: name'print_lyrics' is not defined

3.6执行流程

为了保证函数的定义先于其首次调用执行,需要知道程序中语句运行的顺序,即执行流程。

执行总是从程序的第一行开始。语句按照从下到下的顺序逐一运行。

函数定义并不会改变程序的执行流程,但应注意函数体中的语句并不立即运行,而是等到函数被调用时运行。

函数调用可以看作程序运行流程中的一个迂回路径。遇到函数调用时,并不会直接继续运行下一条语句,而是跳到被调用函数的函数体的第一行,继续运行完函数体的所有语句,再跳回到原来离开的地方。

这样看似简单,但马上你就会发现,函数体中可以调用其他函数。当程序流程运行到一个函数之中时,可能需要运行其他函数中的语句。而后,当运行那个函数中的语句时,又可能再需要调用运行另外一个函数语句!

幸好Python对于它运行到哪里有很好的记录,所以每个函数执行结束后,程序都能跳回到它离开的地方。直到执行到整个程序的结尾,才会结束程序。

总之,在阅读代码时,并不总应该按照代码书写顺序一行行阅读;有时候,按照程序执行的流程来阅读代码,理解的效果可能会更好。

3.7形参和实参

在函数内部,实参会赋值给称为形参(parameter)的变量。下面的例子是一个函数的定义,接收一个实参

def print_twice(bruce):

print(bruce)

print(bruce)

这个函数在调用时会把实参的值赋到形参bruce上,并将其打印两次。这个函数对任何可以打印的值都可用。

print_twice('span'*4)

span span span span

span span span span

print_twice(42)42

42print_twice(math.pi)3.141592653589793

3.141592653589793

内置函数的组合规则,在用户自定义函数上同样可用,所以我们可以对print_twice使用任何表达式作为实参:

>>> print_twice('span'*4)

span span span span

span span span span>>>print_twice(math.cos(math.pi))-1.0

-1.0

作为实参的表达式会在函数调用之前先执行。所以在这个例子中,表达式'span

'*4和math.cos(math.pi)都只执行一次。

也可以使用变量作为实参:

>>> michael = 'Eric,the half a bee'

>>>print_twice(michael)

Eric,the half a bee

Eric,the half a bee

作为实参传入到函数的变量的名称(michael)和函数定义里形参的名称(bruce)没有关系。函数内部只关心形参的值,而不用关心它在调用前叫什么名字;在print_twice函数内部,大家都叫bruce。

3.8变量和形参是局部的

在函数体内新建一个变量时,这个变量是局部的(local),即它只存在于这个函数之内。例如:

def print_twice(bruce):

print(bruce)

print(bruce)

def cat_twice(part1,part2):

cat= part1+part2

print_twice(cat)

这个函数接收两个实参,将它们拼接起来,并将结果打印两遍。下面是一个使用这一函数的例子:

line1= 'Bing tiddle'line2= 'tiddle bang.'

>>>cat_twice(line1,line2)

Bing tiddle tiddle bang.

Bing tiddle tiddle bang.

当cat_twice结束时,变量cat会被销毁。这时再尝试打印它的话,会得到一个异常:

>>>print(cat)

NameError: name'cat' is not defined

形参也是局部的。例如,在print_twice函数之外,不存在bruce这个变量。

3.9栈图

要跟踪哪些变量在哪些地方使用,有时候画一个栈图(stack

diagram)会很方便。和状态图一样,栈图可以展示每个变量的值,不同的是它会展示每个变量所属的函数。

每个函数使用一个帧包含,帧在栈图中就是一个带着函数名称的盒子,里面有函数的参数和变量。前面的函数示例的栈图如图3-1所示。

图中各个帧从上到下安排成一个栈,能够展示出哪个函数被哪个函数调用了。在这个例子里,print_twice被cat_twice调用,而cat_twice被__main__调用。__main__是用于表示整个栈图的图框的特别名称。在所有函数之外新建变量时,它就是属于__main__的。

每个形参都指向与其对应的实参相同的值,所以part1和line1的值相同,part2和line2的值相同,而bruce和cat的值相同。

如果调用函数的过程中发生了错误,Python会打出函数名、调用它的函数的名称,以及调用这个调用者的函数名,依此类推,一直到__main__。

例如,如果在print_twice中访问cat变量,则会得到一个NameError:

Traceback (most recent call last):

File"", line 1, in File"", line 3, incat_twice

File"", line 4, inprint_twice

NameError: name'cat' is not defined

上面这个函数列表被称为回溯(traceback)。它告诉你错误出现在哪个程序文件,哪一行,以及哪些函数正在运行。它也会显示导致错误的那一行代码。

回溯中函数的顺序和栈图中图框的顺序一致。当前正在执行的函数在最底部。

这里插个题外话,说到形参与实参的值相同,突然想到一个点,如果a与b的值相同,a与b可以视作是同个对象吗?神奇的是如果a与b都指同一个整数(指向同一个对象地址),python会认为它们是同一个对象,而如果a与b是浮点型,则python不认为它们是同一个对象(指向不同的对象地址)

>>> a=2

>>> b=2

>>> a isb

True>>>id(a)10943040

>>>id(b)10943040

>>> a=2.3

>>> b=2.3

>>> a isb

False>>>id(a)139801075219456

>>>id(b)139801075219648

3.10有返回值函数和无返回值函数

在我们使用过的函数中,有一部分函数,如数学函数,会返回结果。因为没有想到更好的名字,我称这类函数为有返回值函数(fruitful

function)。

另一些函数,如print_twice,会执行一个动作,但不返回任何值。我们称这类函数为无返回值函数(void

function)。

当调用一个有返回值的函数时,大部分情况下你都想要对结果做某种操作。例如,你可能会想把它赋值给一个变量,或者在一个表达式中:

x =math.cos(radians)

golden= (math.sqrt(5)+1)/2

在交互模式中调用函数时,Python会直接显示结果:

>>> math.sqrt(5)2.23606797749979

但是在脚本中,如果只是直接调用这类函数,那么它的返回值就会永远选择掉!

math.sqrt(5)

这个脚本计算5的平方根,但由于并没有把计算结果存储到某个变量中,或显示出来,所以其实没什么实际作用。

无返回值函数可能在屏幕上显示某些东西,或者有其他的效果,但是它们没有返回值。如果把该结果赋值给某个变量,则会得到一个特殊的值None。

>>> result = print_twice('Bing')

Bing

Bing>>>print(result)

None

值None和字符串’None’并不一样。它是一个特殊的值,有自己独特的类型:

>>>type(None)

到目前为止,我们自定义的函数都是无返回值函数。再过几章我们就会开始写有返回值的函数了。

3.11 为什么要有函数

为什么要花功夫将程序拆分成函数呢?也许刚开始编程的时候这其中的原因并不明晰。下面这些解释都可作为参考。

新建一个函数,可以让你有机会给一组语句命名,这样可以让代码更易读和更易调试。

函数可以通过减少重复代码使程序更短小。后面如果需要修改代码,也只要修改一个地方即可。

将一长段程序拆分成几个函数后,可以对每一个函数单独进行调试,再将它们组装起来成为完整的产品。

一个设计良好的函数,可以在很多程序中使用。书写一次,调试一次,复用无穷。

3.12调试

你将会掌握的一个最重要的技能就是调试。虽然调试可能时有烦恼,但它的确是编程活动中最耗脑力、最有挑战、最有趣的部分。

在某种程度上,调试和刑侦工作很像。你会面对一些线索,而且必须推导出事情发生的过程,以及导致现场结果的事件。

调试也像是一种实验科学。一旦猜出错误的可能原因,就可以修改程序,再运行一次。如果猜对了,那么程序的运行结果会符合预测,这样就离正确的程序更近了一步。如果猜错了,则需要重新思考。正如夏洛克.福尔摩丝所说的:“当你排除掉所有的可能性,那么剩下的,不管多么不可能,必定是真相。”

对某些人来说,编程和调试是同一件事。也就是说,编程正是不断调试修改直到程序达到设计目的的过程。这种想法的要旨是,应该从一个能做某些事的程序开始,然后做一点点修改,并调试改正,如此迭代,以确保总是有一个可以运行的程序。

例如,Linux是包含了数百万行代码的操作系统,但最开始只是Linus

Torvalds编写的用来研究Intel

80386芯片的简单程序。据Larry

Greenfield所说:“Linus最早的一个程序是交替打印AAAA

和BBBB。后来这些程序演化成了Linux”

3.13术语表

函数(function):一个有名称的语句序列,可以进行某种有用的操作。函数可以接收或者不接收参数,可以返回或不返回结果。

函数定义(function

definition):一个用来创建新函数的语句,指定函数的名称、参数以及它包含的语句序列。

函数对象(function

object):函数定义所创建的值。函数名可以用作变量来引用一个函数对象。

函数头(header):函数定义的第一行。

函数体(body):函数定义内的语句序列。

形参(parameter):函数内使用的用来引用作为实参传入的值的名称。

函数调用(function

call ):运行一个函数的语句。它由函数名称和括号中的参数列表组成。

实参(argument):当函数调用时,提供给它的值。这个值会被赋值给对应的形参。

局部变量(local

variable):函数内定义的变量。局部变量只能在函数体内使用。

返回值(return

value):函数的结果。如果函数被当作表达式调用,返回值就是表达式的值。

有返回值函数(fruitful

function):返回一个值的函数

无返回值函数(void

function):总是返回None的函数

None:由无返回值函数返回的一个特殊值。

模块(module):一个包含相关函数以及其他定义的集合的文件

import语句(import

statement):读入一个模块文件,并创建一个模块对象的语句。

模块对象(module

object):使用import语句时创建的对象,提供对模块中定义的值的访问。

句点表示法(dot

notation):调用另一个模块中的函数的语法,使用模块名加上一个句点符号,再加上函数名。

组合(composition):使用一个表达式作为一个更大的表达式的一部分,或者使用语句作为更大的语句的一部分。

栈图(stack

diagram):函数栈的图形表达形式,也展示它们的变量,以及这些变量引用的值。

图框(frame):栈图中的一个图框,表达一个函数调用。它包含了局部变量以及函数的参数。

回溯(traceback):当异常发生时,打印出正在执行的函数栈。

像科学家一样思考python列表_像计算机科学家一样思考python-第3章 函数相关推荐

  1. 全国计算机等级考试python试题_全国计算机等级考试二级Python真题及解析(5)

    全国计算机等级考试二级 Python 真题及解析( 5 ) 一.选择题 1. 关于二叉树的遍历,以下选项中描述错误的是 A 二叉树的遍历可以分为三种:前序遍历.中序遍历.后序遍历 B 前序遍历是先遍历 ...

  2. 像科学家一样思考python答案_像计算机科学家一样思考python-第4章 案例研究:接口设计...

    系统环境 ubuntu18 4.1turtle模块 模块一开始导入turtle模块就报错了 1 Python 3.6.5 (default, Apr 1 2018, 05:46:30)2 [GCC 7 ...

  3. python列表生成式和if语句、if...else语句、zip函数结合使用

    python列表生成式和if语句.if...else语句.map结合使用 文章目录: 1 python列表生成式 2 python列表生成式和if语句结合使用 3 python列表生成式和if...e ...

  4. python列表添加元素、输出_python公开课|Python列表添加元素的方法可是python的基础之一,如果不会怎么行呢...

    [摘要]在这个科学技术高速发展的时代,越来越多的人都开始选择学习编程软件,那么首先被大家选择的编程软件就是python,也用在各行各业之中,并被大家所熟知,所以也有越来越多的python学习者关注py ...

  5. 像计算机科学家一样思考python 百度网盘_像计算机科学家一样思考Python PDF_Python教程...

    资源名称:像计算机科学家一样思考Python PDF 第1章 程序之道 1 1.1 Python编程语言 1 1.2 什么是程序 3 1.3 什么是调试 3 1.4 语法错误 3 1.5 运行时错误 ...

  6. 任意长度的python列表_任意长度的Python列表、元组和字符串中最后一个元素的索引为...

    任意长度的Python列表.元组和字符串中最后一个元素的索引为 答:-1 什么是软件工程? 答:百度定义:软件工程是一门研究用工程化方法构建和维护有效的.实用的和高质量的软件的学科.它涉及程序设计语言 ...

  7. python 列表 换行_python基础语法学习——参考Python Crash Course

    第一章:python在各个系统之间的安装和使用(略) 第二章: 变量 2.1.每个变量都存储了一个值 --与变量相关联的信息.在程序中可随时修改变量的值,而 Python将始终记录变量的最新值. 2. ...

  8. ai python 面试_面试分享系列 | 17道Python面试题,让你在求职中无往不利

    今天给大家分享的是Python面试题系列的第一篇文章,后续我也会陆续整理Python相关的问题给大家,无论是求职者还是新人都可以通过面试题来考察自己的能力缺陷. 1.Python中pass语句的作用是 ...

  9. 计算机二级考试python考试大纲_全国计算机等级考试二级Python语言程序设计考试大纲(2018年版)...

    随着Python的快速发展,Python已经纳入了计算机的等级考试,如下介绍一下Python语言程序设计考试大纲. 基本要求 掌握Python语言的基本语法规则; 掌握不少于2个基本的Python标准 ...

最新文章

  1. Qt Creator创建项目
  2. Flask实战1-轻博客
  3. Windows下visual studio code搭建golang开发环境
  4. virt-v2v 使用指南
  5. SAP Spartacus delivery mode页面设计
  6. One order search的框架代码里,硬编码了要去取Interaction object相关的数据
  7. oracle不能访问管理页面,Oracle Grid Control CONSOLE无法打开9i数据库的管理维护页面...
  8. 2017年闰秒linux_2017年Linux专业人员的4个热门技能
  9. 使用脚本 Nodesource快速安装nodejs环境
  10. JMX实现远程服务器Tomcat系统监控之二
  11. 使用虚拟机win7安装_STEP7 V5.6
  12. html5读delphi数据,Delphi HTML解析器
  13. 用R语言进行Cox回归生存分析
  14. Chrome 科研神器!去谷歌学术搜到文章,代码链接就能自动展示
  15. <小甲鱼>C++实例练习6—华氏温度与摄氏温度转换程序
  16. Ubuntu 14.04.2 系统无线网络不稳定问题
  17. 全家都是博士是一种什么样的体验?
  18. leetcode 1567. 乘积为正数的最长子数组长度python
  19. spark解析csv文件_Spark:解析CSV文件并按列值分组
  20. 微信小程序 仿朋友圈

热门文章

  1. 【附源码】Python计算机毕业设计烹饪课程预约系统
  2. 基于STM32 Cortex-M3内核F103制作的智能小车项目
  3. php新浪微博 登录接口文档,最新新浪微博网站接入API接口的具体步骤wordpress
  4. 前10节的理解和总结
  5. 文件上传upload-labs第四关
  6. SAP MM模块常用表总结
  7. 深入浅出 - Android系统移植与平台开发(五)- 定制手机模拟器ROM
  8. ERC721:全生命周期精析,妈妈再也不用担心我不会玩NFT合约啦
  9. 一名SM是怎么炼成的(之一锻炼领导力)
  10. JAVA编写文件格式转换UTF-8