第6章 抽象
6.3. 创建函数
- 判断函数是否可调用。
Python3.0前,使用内建函数callable(func)
Python3.0后,使用表达式hasattr(func, __call__) - 定义函数,使用def,格式如下:
def func(args):
dosomething
return result
示例如下:
>>> def hello(name):
... return 'hello', name+'!'
...
>>> print hello('world')
hello world!
6.3.1. 记录函数
文档字符串:写在函数开头的字符串,作为函数的一部分进行存储。
>>> def test_doc_string():
... 'Test doc string'
... return
...
可通过__doc__方法访问文档字符串。
>>> test_doc_string.__doc__
'Test doc string'
另外,当调用内建help函数是,也可已得到响应函数的文档字符串信息。
>>> help(test_doc_string)
Help on function test_doc_string in module __main__:
test_doc_string()
Test doc string
6.4. 参数魔法
6.4.2. 我能改变参数吗
- 当参数为字符串、数字和元组时,函数内为参数赋予新值不会改变外部任何变量的值。
>>> def try_to_change(n):
... n = 'new string'
...
>>> name = 'old string'
>>> try_to_change(name)
>>> name
'old string'
若想使用函数内对参数的修改,需要将修改后的参数返回。例如:
>>> def inc(x): return x+1
...
>>> foo = 10
>>> foo = inc(foo)
>>> foo
11
- 当参数为可变的数据结构(如列表)时,以下面代码为例:
>>> names = ['aaa', 'bbb', 'ccc']
>>> def try_to_change(n):
... n[0] = 'aaa1'
...
>>> try_to_change(names)
>>> names
['aaa1', 'bbb', 'ccc']
从结果可见,实参发生了变化。在调用函数时,实参的输入相当于将实参赋值给函数内的局部参数,即实参与函数内的局部参数引用了同一个列表。
★Python中,函数只能修改参数对象本身。
6.4.3. 关键字参数和默认值
- 关键字参数:函数调用时,使用参数名提供参数。
位置参数:函数调用时,只提供参数的值。
#假设有函数声明为
function(arg1, arg2, ... argN)
#使用位置参数调用格式为
function(value1, value2, ... valueN)
#使用关键字参数调用格式为
function(arg1=value1, arg2=value2, ... argN=valueN)
- 关键字参数作用
-每个参数含义清晰。参数的顺序可以与声明不同,只要将参数名和参数值进行对应即可。
-定义函数时,可以给参数提供默认值。
def function(arg1=default_value1, arg2=default_value2, ... argN=default_valueN):
function_body
-可以位置参数和关键字参数混合使用,但是调用函数时,位置参数需要放在关键字参数之前,否则解释去无法识别。
#定义函数并指定各个参数的默认值
>>> def hello(name='world', year=2017, month=1, day=1):
... print "hello, %s! %d-%d-%d" % (name, year, month, day
...
#使用关键字参数调用函数
>>> hello(name='holly', year=1990, month=2, day=30)
hello, holly! 1990-2-30
#使用关键字参数调用函数可以修改参数的位置
>>> hello(year=1990, month=2, day=30, name='holly')
hello, holly! 1990-2-30
#使用关键字参数调用函数时,缺省的参数使用默认值
>>> hello(year=1990, month=2, day=30)
hello, world! 1990-2-30
#位置参数和关键字参数可以混合使用,要求位置参数在关键字参数之前
>>> hello('holly', year=1990, month=2, day=30)
hello, holly! 1990-2-30
>>> hello(name='holly', 1990, month=2, day=30)
File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg
6.4.4. 收集参数
定义函数时,参数名前添加”*”,表示收集其余的位置参数,作为元组输入;添加”**”,表示收集其余的关键字参数,作为字典输入。
>>> def test_collect(x, y, z=2, *param1, **param2):
... print x, y
... print param1
... print param2
...
>>> test_collect(1, 2, 3, 'a', 'b', c=1, d=2)
1 2
('a', 'b')
{'c': 1, 'd': 2}
6.4.5. 反转过程
调用函数时,在元组类型参数前添加”*”,元组的元素依次作为位置参数输入;在字典类型参数前添加”**”,字典的键-值对依次组成关键字参数输入。
#元组输入
>>> def add(x, y):
... return x+y
...
>>> numbers=(2,3)
>>> add(*numbers)
5
#字典输入
>>> def test_distribute(name, greeting):
... print "%s, %s!"%(greeting, name)
...
>>> test_distribute(**args)
Hello, world!
6.5. 作用域
- Python中执行变量赋值操作后,相当于将该变量和值形成键-值对,添加到“不可见”的字典中,这个字典即为命名空间或作用域。
这个字典可以通过var()内建函数获取。
>>>x=1
>>> vars()['x']
1
- 除全局作用域外,当调用函数时,会创建新的作用域。作用与C语言相同。
- 当局部变量与全局变量重名时,从函数内无法直接访问全局变量。有以下两种解决方案:
-可以通过内建函数globals()[‘parameter’],从函数内访问重名的全局变量。
>>> parameter='global'
>>> def test_region(parameter):
... print parameter, globals()['parameter']
...
>>> test_region('local')
local global
-在函数内将变量声明为全局变量后可直接访问。
>>> def change_global():
... global x
... x += 1
...
>>> x=1
>>> change_global()
>>> x
2
- 嵌套作用域,下例为嵌套作用域的突出应用。
#定义multiplier,用于定义并返回multiplyByFactor函数。返回的函数可以访问它的定义所在的作用域。
>>> def multiplier(factor):
... def multiplyByFactor(number):
... return number*factor
... return multiplyByFactor
...
#每次调用外部函数时,它内部的函数都被重新绑定,factor有一个新值,而内部定义的函数可以访问factor。
>>> double = multiplier(2)
>>> double(3)
6
>>> multiplier(5)(4)
20
6.6. 递归
有用的递归函数包含以下几部分:
■当函数直接返回值时有基本实例(最小可能性问题);
■递归实例,包括一个或者多个问题最小部分的递归调用。
6.6.1. 两个经典:阶乘和幂
阶乘
>>> def factorial(n):
... if n == 1:
... return 1
... return n * factorial(n-1)
...
>>> factorial(3)
6
>>> factorial(4)
24
>>> factorial(5)
120
幂
>>> def power(x, n):
... if n == 0:
... return 1
... return x * power(x, n-1)
...
>>> power(2,3)
8
>>> power(3,2)
9
6.6.2. 另外一个经典:二元查找
>>> def search(sequence, number, lower, upper):
... if lower == upper:
... if sequence[lower] == number:
... return lower
... else:
... return 'no match found'
... else:
... middle = (lower+upper)//2
... if sequence[middle] > number:
... return search(sequence, number, lower, middle-1)
... else:
... return search(sequence, number, middle, upper)
...
>>> sequence = range(1000)
>>> print search(sequence, 298, 0, len(sequence)-1)
298
函数型程序设计
- map:将序列中的元素全部传递给一个函数。
- filter:基于一个返回布尔值的函数对元素进行过滤。
- reduce:将序列前两个元素于给定函数联合使用,并将返回值和第3个元素继续联合使用,直到整个序列都处理完毕。