传入函数
形参
变长参数
函数式编程
变量的作用域
递归
生成器
1 什么是函数
函数是对程序逻辑进行结构化或过程化的一种编程方法。能将整块代码巧妙地隔离成易于管理 的小块,把重复代码放到函数中而不是进行大量的拷贝--这样既能节省空间,也有助于保持一致性,因为你只需改变单个的拷贝而无须去寻找再修改大量复制代码的拷贝。
1.1 过程 vs 函数
在C++里我不记得有过程这种东西,但是在一些其它的语言比如PL/SQL里面会有过程。过程和函数一样是可以调用的代码块,但是过程没有返回值。在pyhon里,面其实过程和函数是一个东西,因为如果你在函数里面不指定返回值,python的interpreter会返回None.
1.2 返回值和函数类型
python的函数会返回一个返回值。
如果你显示的指定了这个值,那么返回的就是你想要的类型,否则python的interpreter默认返回none。比如下面的例子就是:
>>> deffoo():
...pass...>>> printa
NoneView Code
但是,有人声称python的函数可以返回多个值。其实事实是,python把这些返回值打包到了一个元组中。比如下面的例子:
>>> deffoo():
...return 1,2,‘a‘,‘b‘...>>>foo()
(1, 2, ‘a‘, ‘b‘)View Code
python 可以通过返回一个容器,比如 list
,dict,tuple等来间接的实现返回多个值的目的,上面的例子中返回的是一个元组,因为元组的语法不强制要求写括号,所以
return语句就把后面逗号分隔的内容当做一个元组一起返回。
2 调用函数
2.1 关键字参数
关键字参数指的是如下调用函数的形式,可以看到在函数调用的时候采用的是(x= , y=
)。这样的形式。这种理念是让调用者通过函数调用中的参数名字来区分参数。这样规范允许参不按顺序,因为解释器能通过给出的关键字来匹配参数的值。
>>> deffoo(x,y):
...printx,y
...>>> foo(y=2,x=1)1 2View Code
但要注意的是关键字参数的概念仅仅是针对函数调用来说的,也就是说在定义函数的时候不用做任何的特别设定。我们再看一个例子,假设现在有一个函数conn()需要两个参数
host 和 port。 定义如下:
>>> defconn(host,port):
... hostname=host
... portnumber=port
...print hostname,portnumberView Code
你在调用的时候可以采用正常的调用方式,按顺序输入恰当的参数,如:
>>> conn(‘server_A‘,22)
server_A22View Code
你也可以用关键字调用的方式,这时候就不用管输入参数的顺序了,如:
>>> conn(port=22,host=‘server_B‘)
server_B22View Code
2.2 默认参数
这个很简单,在c/c++里都有,就是在函数定义的时候提供一个默认的参数,这样未来调用的时候如果不显示提供参数,函数就会使用默认的参数。
>>> def foo(x=4,y=4):
...return x+y
...>>>foo()8View Code
在后面的章节我们还会更详细的降到默认参数。
2.3 参数组
python的函数在调用时,可以把参数放入一个元组或者是字典,然后把这个元组或者字典传递给函数进行调用。当然元组中存放的是非关键字参数而字典中存放的是关键字参数。
>>> deffoo(x,y):
...return x+y
...>>> a=4,4
>>> foo(*a)8
>>> d={‘y‘:5,‘x‘:3}>>> foo(**d)8View Code
注意在元组前加一个* 而在字典前要加**。他们表示把字典或者元组解开的意思,也就是把a还原成 4,4 而 d还原成 y=3,x=5
3 创建函数
3.1 用def语句创建函数
创建函数的语法非常简单,如下:
deffunction_name(arguments):"function documentation string"function bodyView Code
文档字符串是可选的。
3.2 声明和定义的比较
有些语言比如C/C++中,函数的声明和定义是分开的。声明只包括函数的名字和参数列表,而函数的定义则要包括函数的内部逻辑。函数声明和定义有区别的语言往往是因为他们要把函数声明和函数定义放到不同的文件里,而在python里面两者是一体的。
3.3 向前引用
情况下面一段代码:
deffoo():print "in foo()"bar()defbar():print "in bar()"foo()View Code
如果放在python解释器中运行这段代码,也许你会认为会出错,因为在调用bar()函数的时候,bar函数还没有定义。
但实际上不会出错,因为虽然foo中bar调用在bar的定义之前,但是foo()本身的调用并不是在bar的定义之前
3.4 函数属性
python中函数其实也是一个实例或者说对象。而python中的对象可以当做一个名称空间。换个说法就是python中的对象,比如这里的函数,可以用来存储名字。看一下下面的实例那就明白了:
>>> deffoo():
...‘this is the doc string of foo‘...>>> foo.attr1=1
>>> foo.attr2=2
>>>foo.attr11
>>>foo.attr22View Code
我们创建了一个函数,这个函数什么都没做,然后我们用句点符号来为函数创建了两个属性attr1,attr2并且赋值,这样我们就可以通过句点符号来访问这两个属性了。
关于名称空间的概念后面会有详细介绍,这里我们只要理解为一个存储名字的空间就可以了。要注意的另外一点是,上面例子中在定义foo的时候创建了一个文档字符串。所谓文档字符串就是函数里面第一个没有赋给变量的字符串,这个字符串可以通过
函数名.__doc__来访问,也可以通过help来访问
>>>help(foo)
Help on function fooin module __main__:
foo()
thisisthe doc string of foo>>> foo.__doc__
‘this is the doc string of foo‘View Code
3.5 内嵌函数
内嵌函数就是在函数体的内部创建的一个函数,比如下面的代码:
>>> deffoo():
...print ‘foo is called‘...defbar():
...print ‘bar is called‘... bar()
...>>>foo()
fooiscalled
baris calledView Code
但要注意的是,内嵌函数的作用域只能是在外部函数内,所以下面的代码就会出错
>>>bar()
Traceback (most recent call last):
File"", line 1, in?
NameError: name‘bar‘ is notdefined>>>View Code
因为你调用bar的地方是在foo的函数体外部,这里识别不到bar。
3.6 装饰器
装饰器是python中的一个比较独特的东西,至少在c/c++中是没有这个东西的。装饰器存在的意义