Python 函数的学习笔记

1 函数调用

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

>>> type(32)
<type 'int'>
这个函数的名称是 type,括号中的表达式我们称之为函数的参数。这个函数调用的结果是参数的类型。

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

2 类型转换函数
Python提供了一些可将某个值从一种类型转换为另一种类型的内置函数。int函数可以把任何可以转换为整型的值转换为整型;如果转换失败,则会报错:

>>> int('32')
32
>>> int('Hello')
ValueError: invalid literal for int(): Hello
int可以将浮点数转换为整数,但不会做四舍五入操作,而是直接舍弃小数部分。

>>> int(3.99999)
3
>>> int(-2.3)
-2
float函数将整数和字符串转换为浮点数:

>>> float(32)
32.0
>>> float('3.14159')
3.14159
最后,str函数将参数转换为字符串:

>>> str(32)
'32'
>>> str(3.14159)
'3.14159'
3 数学函数
Python有一个数学计算模块,提供了大多数常用的数学函数。模块是指包含一组相关的函数的文件。

要想使用一个模块,需要先将它导入(import)运行环境:

>>> import math
这个语句将会建立一个名为math的模块对象(module object)。如果打印这个对象,可以看到它的一些信息:

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

>>> ratio = signal_power / noise_power
>>> decibels = 10 * math.log10(ratio)

>>> radians = 0.7
>>> height = math.sin(radians)
上面第一个例子使用了log10来计算以分贝为单位的信号/噪声比(假设signal_power和noise_power都已经事先定义好了)。math模块也提供了log函数,用来计算底为e的自然对数。

第二个例子计算radians的正弦值。这个变量名已经暗示了,sin以及cos、tan等三角函数接受的参数是以弧度(radians)为单位的。若要将角度转换为弧度,可以除以360再乘以2π:

>>> degrees = 45
>>> radians = degrees / 360.0 * 2 * math.pi
>>> math.sin(radians)
0.707106781187
表达式math.pi从math模块中获得变量pi。这个变量的值是π的近似值,大约精确到15位数字。

如果你了解三角函数,可以把上面的结果和2的平方根的一半进行比较:

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

程序设计语言最有用的特性之一就是可以将各种小的构建块(building block)组合起来。比如,函数的参数可以是任何类型的表达式,包括算术符号:

x = math.sin(degrees / 360.0 * 2 * math.pi)
甚至还包括函数调用:

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

>>> minutes = hours * 60        # 正确
>>> hours * 60 = minutes        # 错误!
SyntaxError: can't assign to operator
5 添加新函数
至此,我们都只是在使用Python提供的函数,其实我们也可以自己添加新的函数。函数定义指定新函数的名称,并提供一系列程序语句。当函数被调用时,这些语句会顺序执行。

下面是一个例子:

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。函数名称的书写规则和变量名称一样:字母、数字和某些标点是合法的,但第一个字符不能是数字。关键字不能作为函数名,而且我们应尽量避免函数和变量同名。

函数名后的空括号表示它不接收任何参数。

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

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

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

 >>> def print_lyrics():
 ...   print "I'm a lumberjack, and I'm okay."
 ...   print "I sleep all night and I work all day."
 ...
想要结束这个函数的定义,需要输入一个空行(在脚本文件中则不需要如此)。

定义一个函数后,会创建一个同名的变量。

 >>> print print_lyrics
 <function print_lyrics at 0xb7e99e9c>
 >>> type(print_lyrics)
 <type 'function'>
变量print_lyrics的值是一个函数对象,其类型是'function'。

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

>>> 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.
当然,这首歌其实并不是这么唱的。

6 定义和使用
将前面一节零散的代码整合起来,整个程序就像下面这个样子:

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
将程序的最后一行移动到首行,于是函数调用会先于函数定义执行。运行程序并查看会有什么样的错误信息。

练习2
将函数调用那一行放回到末尾,并将函数print_lyrics的定义放到函数repeat_lyrics定义之后。这时候运行程序会发生什么?

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

执行总是从程序的第一行开始。从上到下,按顺序,每次执行一条语句。

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

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

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

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

前面这段枯燥的描述,寓意何在?当你阅读代码时,并不总是应该一行行按照书写顺序阅读。有时候,按照执行的流程来阅读代码,可能理解效果更好。

8 形参和实参①
我们已经看到,有些内置函数需要传入参数。比如,当调用math.sin时,需要传入一个数字作为实参。有的函数需要多个实参:math.pow需要两个,分别是基数(base)和指数(exponent)。

在函数内部,实参会被赋值给形参。下面的例子是一个用户自定义的函数,接收一个实参:

def print_twice(bruce):
   print bruce
   print bruce
这个函数在调用时会把实参的值赋到形参bruce上,并将其打印两次。

这个函数对任何可以打印的值都可用。

>>> print_twice('Spam')
Spam
Spam
>>> print_twice(17)
17
17
>>> print_twice(math.pi)
3.14159265359
3.14159265359
内置函数的组合规则,在用户自定义函数上也同样可用,所以我们可以对print_twice使用任何表达式作为实参:

>>> print_twice('Spam '*4)
Spam Spam Spam Spam
Spam Spam Spam Spam
>>> print_twice(math.cos(math.pi))
-1.0
-1.0
作为实参的表达式会在函数调用之前先执行。所以在这个例子中,表达式'Spam'*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。

9 变量和形参是局部的
当你在函数体内新建一个变量时,它是局部的(local),即它只存在于这个函数之内。比如:

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_twicebruce这个变量。

10 栈图
要跟踪哪些变量在哪些地方使用,有时候画一个栈图(stack diagram)会很方便。和状态图一样,栈图可以展示每个变量的值,不同的是它会展示每个变量所属的函数。

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

 

图1 栈图

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

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

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

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

Traceback (innermost last):
  File "test.py", line 13, in __main__
    cat_twice(line1, line2)
  File "test.py", line 5, in cat_twice
    print_twice(cat)
  File "test.py", line 9, in print_twice
    print cat
NameError: name 'cat' is not defined
上面这个函数列表被称为回溯(traceback)。它告诉你错误出现在哪个程序文件,哪一行,以及哪些函数正在运行。它也会显示导致错误的那一行代码。

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

11 有返回值函数和无返回值函数
我们使用过的函数中,有一部分函数,如数学函数,会产生结果。因为没有想到更好的名字,我称这类函数为有返回值函数(fruitful function)。另一些函数,如print_twice,会执行一个动作,但不返回任何值。我们称这类函数为无返回值函数(void function)。

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

x = math.cos(radians)
golden = (math.sqrt(5) + 1) / 2
在交互模式中调用函数时,Python会直接显示结果:

>>> math.sqrt(5)
2.2360679774997898
但是在脚本中,如果只是直接调用这类函数,那么它的返回值就会永远丢失掉!

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

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

>>> result = print_twice('Bing')
Bing
Bing
>>> print result
None
值None和字符串'None'并不一样。它是一个特殊的值,有自己独特的类型:

>>> print type(None)
<type 'NoneType'>
到目前为止,我们自定义的函数都是无返回值函数。

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

新建一个函数,可以让你有机会给一组语句命名,这样可以让代码更易读和更易调试。
函数可以通过减少重复代码使程序更短小。后面如果你需要修改代码,也只要修改一个地方即可。
将一长段程序拆分成几个函数后,可以对每一个函数单独进行调试,再将它们组装起来成为完整的产品。
一个设计良好的函数,可以在很多程序中使用。书写一次,调试一次,复用无穷。
13 使用from导入模块
Python提供了两种导入模块的方式;我们已经见过其中一种:

>>> import math
>>> print math
<module 'math' (built-in)>
>>> print math.pi
3.14159265359
如果你导入math,则会得到名为math的模块对象。模块对象包含了pi这样的常量以及诸如sin和exp这样的函数。

但是如果直接访问pi,则会发生错误。

>>> print pi
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'pi' is not defined
这时候,你可以像下面这样来导入模块中的某个对象:

>>> from math import pi
现在就可以直接访问pi,而不需要使用句点表示法math.pi了。

>>> print pi
3.14159265359
或者,也可以使用星号来导入一个模块的所有成员:

>>> from math import *
>>> cos(pi)
-1.0
用这种方式导入模块内所有的成员,好处是可以使你的代码更简洁,但缺点是不同模块的同名成员之间,或者和自定义的变量之间,可能发生名字冲突。

14 调试
如果你使用文本编辑器来编写脚本,则可能会遇到缩进时空格和制表符混淆的问题。避免这种问题的最好办法是只使用空格(不用制表符)。大部分识别Python的文本编辑器都默认这么处理,不过也有一些不支持。

制表符和空格都是不可见的,因而会很难调试,所以应尝试找一个能帮你处理缩进的编辑器。

另外,不要忘了在运行程序前保存它。有的开发环境会自动保存,但也有不自动保存的。如果不保存,则你写好的代码和运行的代码并不一样。

如果运行的报错的代码和你写的不一样,调试时会浪费很多时间!

所以一定要确保你眼前所看的代码和所运行的代码是一致的。如果不确定,可以在程序开头写一句print 'hello'并再运行一次。如果没有看到hello输出,则你运行的不是正确的程序!

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

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

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

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

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

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

函数调用(function call):执行一个函数的语句。它由函数名称和参数列表组成。

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

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

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

有返回值函数(fruitful function):返回一个值的函数。

无返回值函数(void function):没有返回值的函数。

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

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

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

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

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

执行流程(flow of execution):程序运行中语句执行的顺序。

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

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

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

16 练习
练习3 Python提供了一个内置函数len,返回一个字符串的长度。所以len('allen')的值是5。

编写一个函数right_justify,接收一个字符串形参s,并打印出足够的前导空白,以达到最后一个字符显示在第70列上。

>>> right_justify('allen')
                                   allen
练习4 函数对象是一个值,你可以将它赋值给变量,或者作为实参传递。例如do_twice是一个函数,接收一个函数对象作为实参,并调用它两次:

def do_twice(f):
   f()
   f()
下面是一个使用do_twice来调用一个print_spam函数两次的示例:

def print_spam():
   print 'spam'

do_twice(print_spam)
1.将这个示例存入脚本中并测试它。

2.修改do_twice,让它接收两个实参,一个是函数对象,另一个是一个值,它会调用函数对象两次,并传入那个值作为实参。

3.编写一个更通用的print_spam,叫做print_twice,接收一个字符串形参,并打印它两次。

4.使用修改版的do_twice来调用print_twice两次,并传入实参'spam'。

5.定义一个新的函数do_four,接收一个函数对象与一个值,使用这个值作为实参调用函数4次。这个函数的函数体应该只有两个语句,而不是四个。

练习5 这个练习可以只用语句和我们已经学过的其他语言特性实现。

1.编写一个函数,绘制如下的表格:

 

提示

要在同一行打印多个值,你可以使用逗号分隔不同的值:

print '+','-'
如果值序列的结尾有一个逗号,Python不会换行,所以后面的打印语句会出现在同一行。

print '+',
print '-'
这两个语句的输出是'+ -'。 2.编写一个函数绘制类似的表格,但有4行4列。

时间: 2024-10-11 13:47:44

Python 函数的学习笔记的相关文章

python 函数的学习笔记使用例子

定义一个函数 你可以定义一个由自己想要功能的函数,以下是简单的规则: 函数代码块以def关键词开头,后接函数标识符名称和圆括号(). 任何传入参数和自变量必须放在圆括号中间.圆括号之间可以用于定义参数. 函数的第一行语句可以选择性地使用文档字符串-用于存放函数说明. 函数内容以冒号起始,并且缩进. Return[expression]结束函数,选择性地返回一个值给调用方.不带表达式的return相当于返回 None. ---------------------------------------

Python os模块学习笔记

  这篇文章主要介绍了Python os模块学习笔记,本文总结了OS模块的常用方法.实用方法,并给出了两个使用实例,需要的朋友可以参考下 一.os模块概述 Python os模块包含普遍的操作系统功能.例如文件的复制.创建.修改.删除文件及文件夹... 二.常用方法 1.os.listdir() 返回指定目录下的所有文件和目录名. 2.os.remove() 删除一个文件. 3.os.system() 运行shell命令. 4.os.path.split() 函数返回一个路径的目录名和文件名 5

python网络编程学习笔记(二):socket建立网络客户端_python

1.建立socket 建立socket对象需要搞清通信类型和协议家族.通信类型指明了用什么协议来传输数据.协议的例子包括IPv4.IPv6.IPX\SPX.AFP.对于internet通信,通信类型基本上都是AF_INET(和IPv4对应).协议家族一般表示TCP通信的SOCK_STREAM或者表示UDP通信的SOCK_DGRAM.因此对于TCP通信,建立一个socket连接的语句为:s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)对于UDP通

JavaScript权威设计--JavaScript函数(简要学习笔记十一)

1.函数调用的四种方式 第三种:构造函数调用 如果构造函数调用在圆括号内包含一组实参列表,先计算这些实参表达式,然后传入函数内.这和函数调用和方法调用是一致的.但如果构造函数没有形参,JavaScript构造函数调用的语法是允许省略实参列表和圆括号的. 如: var o=new Object(); //->等价于 var o=new Object;   第四种:使用call()与apply()间接调用(放在后面详细说明)   2.函数的实参与形参--可选形参 先看一个例子: function g

JavaScript权威设计--JavaScript函数(简要学习笔记十)

1.函数命名规范 函数命名通常以动词为前缀的词组.通常第一个字符小写.当包含多个单词时,一种约定是将单词以下划线分割,就像"like_Zqz()". 还有一种就是"likeZqz()".有些些函数是用作内部用的或者为私有函数通常以一条下划线为前缀,就像"_zqzName()".   2.以表达式方式定义的函数 如: var zqz=function (){ return "zhaoqize"; } 在使用的时候必须把它赋值给一

Python核心编程学习笔记之映射类型(上)

 根据核心编程第二版学习Python3.x的内容,可能有些欠缺,有些方法在3.x中已经不提供了,就暂时先略过了.等以后再对比2.x和3.x的区别,作下笔记吧 1.    Python中字典的定位: a)      字典是python中唯一的映射类型,通常被认为是可变的哈希表. b)     字典对象是可变的,能存储任意多个python对象. c)      字典是Python中最强大的数据类型之一 2.    字典(dict)和序列类型容器类(列表和元组)的区别: a)      存储和访问数据

python面向对象的学习笔记

创建类: #!/usr/bin/python # Filename: simplestclass.py   class Person:     pass # An empty block   p = Person() print p $ python simplestclass.py <__main__.Person instance at 0xf6fcb18c> 我们使用类名后跟一对圆括号来创建一个对象/实例.为了验证,我们简单地打印了这个变量的类型.它告诉我们我们已经在__main__模块

python网络编程学习笔记(三):socket网络服务器_python

1.TCP连接的建立方法 客户端在建立一个TCP连接时一般需要两步,而服务器的这个过程需要四步,具体见下面的比较. 步骤 TCP客户端 TCP服务器 第一步 建立socket对象  建立socket对象 第二步 调用connect()建立一个和服务器的连接 设置socket选项(可选) 第三步 无 绑定到一个端口(也可以是一个指定的网卡) 第四步 无 侦听连接 下面具体来讲这四步的建立方法: 第一步,建立socket对象:这里与客户端一样,依然是: s=socket.socket(socket.

python网络编程学习笔记(一)_python

学习用书:<python 网络编程基础>作者John Goerzen 第一部分底层网络学习         Python提供了访问底层操作系统Socket接口的全部方法,需要的时候这些接口可以提供灵活而强有力的功能. (1)基本客户端操作         在<python 网络编程基础>一书中,作者列出了一个简单的Python客户端程序,具体如下: 复制代码 代码如下: import socket,sysport =70host=sys.argv[1] filename=sys.a