对于函数的定义就不必再说了。
函数的返回值及类型:python中对于没有返回值的函数,解释器会隐式的返回默认值None。python里的函数可以返回一个值或者对象,可是在返回一个容器对象的时候,可能给人一种能返回多个对象的错觉。例如
def bar():
.... return 'abc', [42, 'python'], "Guido"

bar()函数其实返回的是一个元组,由于元组语法上不需要一定加上括号,所以真的让人认为返回多个对象。
python是动态的确定类型,而且函数能返回不同类型的值,所以没有直接类型关联。

关键字参数:先给个例子说明:
def foo(x, y):
.... print x,
.... print y*y

标准调用:foo(‘answer is: ‘, 6)
输出: answer is: 36
关键字参数:foo(y=6, x=’answer is: ‘)
输出: answer is: 36
关键字参数的概念仅仅针对函数的调用,关键字参数也就是通过指定参数的名称来区分参数,这样可以允许参数缺失或不按顺序,因为解释器能通过关键字来分配参数的值。
默认参数这个概念很简单,默认参数就是在函数定义的时候指定了参数的值,如果调用的时候没有指定这个参数的值,那么函数就会使用默认参数,例子:
def foo(y, x='answer is:'):
.... print x,
.... print y*y

调用:foo(6)
输出:answer is: 36
由于指定了x的默认值,所以在调用函数的时候可以缺失,而且你已经看到了,函数定义的时候y出现在x的前面,这是因为python规定所以的默认参数必须放在参数列表的后面。如果这样定义def foo(x=’answer is: ‘, y): 是会引发语法错误的。

参数组 python允许程序执行一个没有显式定义参数的函数,相应的方法是通过一个元组或字典作为参数组传递给函数,语法如下:
func(*tuple_grp_nonkw_args, **dict_grp_kw_args)
其中的tuple_grp_nonkw_args 是以元组的形式体现的非关键字参数组,dict_grp_kw_args是装有关键字参数的字典。
在一个函数定义中可以混合使用标准的位置参数,关键字参数和元组字典参数。不过如果确实要混合使用,请特别注意。要自己尝试一下。例子:
def print_params(*params):
.... print params

调用:print_params(1,2,3)
输出:(1,2,3)
参数前的*将所有参数值放置在同一个元组中。
def print_params(**params):
.... print params

调用:print_params(x=1,y=2,z=3):
输出:{‘z’:3, ‘x’:1, ‘y’:2}
如果混合使用一定要自己尝试,很容易出错。
内部/内嵌函数先来个例子:
def foo():
.... def bar():
.... .... print 'This is bar!'
.... bar()

调用:foo()
输出:This is bar!
调用:bar()
输出:错误信息。这个应该好理解,bar属于foo的内部函数其作用域在foo的内部。
这种内部函数屁用没有,再来个有用的例子:
def multiplier(factor):
.... def multiplyByFactor(number):
.... .... return number*factor
.... return multiplyByFactor

这个例子中外层函数返回内层函数但是没有调用内层函数,也就是内层函数没有运行,重要的是返回的内层函数还可以访问它的定义所在的作用域。换句话说,它带着它的环境。
调用:
double = multiplier(2)
double(5)
输出:10
调用:multiplier(5)(4)
输出:20
类似multiplyByFactor函数存储子封闭作用域的行为叫做闭包。
匿名函数与lambda
python允许用lambda关键字创造匿名函数,一个完整的lambda语句代表了一个表达式,这个表达式的定义体必须和声明放在同一行,语法如下:
lambda [arg1[,arg2,…argn]]:expression
lambda表达式返回可调用的函数对象,它们和那些返回等价表达式计算值相同的函数是不能区分的,例子:
def add(x, y): return x+y 等价于 lambda x, y: x+y
def usuallyAdd2(x, y=2): return x+y 等价于 lambda x, y=2: x+y
def showAllTuple(*z): return z 等价于 lambda *z: z
内建函数filter()
filter()函数为已知的序列的每个元素调用给定布尔函数。每个filter返回的非零值元素添加到一个列表中。例子:
from random import randint
def odd(n):
.... return n%2
allNums = []
for eachNum in range(9):
... allNums.append(randint(1, 99))
print filter(odd, allNums)

这次我们用lambda代替odd函数:
from random import randint
allNums = []
for eachNum in range(9):
.... allNums.append(randint(1, 99))
print filter(lambda n: n%2, allNums)

看我们用列表[]代替filter
from random import randint
allNums = []
for eachNum in range(9):
.... allNums.append(randint(1, 99))
print [n for n in allNums if n%2]

这次我们把allNums的获取也放入列表
from random import randint as ri
print [n for n in [ri(1, 99) for i in range(9)] if n%2 ]

内建函数map()
map()和filter()函数相似,不同之处是map将函数调用映射到每个序列的元素上(这里是每个序列的元素而不是序列的每个元素,之所以这样是因为map可以一次处理多个序列),并返回一个含有所有返回值的序列。例子:
map((lambda x: x+2), [0, 1, 2, 3])
输出:[2, 3, 4, 5]
用列表代替map
[x+2 for x in range(4)]
输出:[2, 3, 4, 5]
下面是map带有多个序列的例子:
map(lambda x, y: x+y, [1, 3, 5], [2, 4, 6])
输出:[3, 7, 11]
map(lambda x, y: (x+y, x-y), [1, 3, 5], [2, 4, 6])
输出:((3, -1), (7, -1), (11, -1))
下面看看map是如何实现zip函数的
map(None, [1, 3, 5], [2, 4, 6])
输出:[(1, 2), (3, 4), (5, 6)]
zip([1, 3, 5], [2, 4, 6])
输出:[(1,2), (3, 4), (5, 6)]
内建函数reduce
reduce使用一个二元函数,一个序列和一个可选的初始化器。它通过取出序列的前两个元素,并将它们传入二元函数来获得一个单一的值。然后用这个值和序列的下一个元素来获得又一个值,然后继续直到整个序列的元素都遍历完毕。
reduce(func, [1, 2, 3]) = func(func(1, 2), 3)
print ‘the total is:’, reduce((lambda x, y: x+y), range(5))
输出: the total is:10
迭代器与生成器
迭代器为类序列对象提供了一个类序列的接口。python的迭代无缝的支持序列对象,而且它还允许程序员迭代非序列类型,包括用户定义对象。
迭代器的定义:
*提供了可扩展的迭代器接口
*对序列迭代带来了性能上的增强
*在字典迭代中性能提升
*创建真正的迭代接口,而不是原来的随机访问
*与所有已经存在的用户定义的类以及扩展的模拟序列和映射的对象向后兼容。
*迭代非序列集合时,可创建更简洁的可读代码
根本上说,迭代器就是一个有next()方法的对象,当你或是一个循环机制需要下一个项是,调用迭代器next()方法可以获得它。条目全部取出后,会引起StopIteration异常,这并不表示错误发生,只是告诉外部调用者,迭代完成。
reverse()内建函数返回一个反序的迭代器。
如何创建迭代器,对一个对象调用iter()就可以得到它的迭代器,语法如下:
iter(obj)
iter(func, sentinel)
第一个方法是传递一个序列到iter(),另一个创建迭代器的方法是使用类,一个实现了iter()和next()方法的类可以作为迭代器使用。
如果传递两个参数给iter(),它会重复调用func直到迭代器的下一个值等于sentinel。
对列表来说,序列现在自动地生成它们自己的迭代器,字典的迭代器会遍历它的键。python还引进了三个新的内建字典方法来定义迭代:myDict.iterkeys() myDict.itervalues() myDict.iteriterms()
文件对象生成的迭代器会自动调用readline()方法,只要对象实现了iter方法就能对对象进行迭代,iter方法会返回一个迭代器。
生成器
从语法上讲,生成器是一个带yield语句的函数,一个函数或子程序只返回一次,但一个生成器能暂停执行并且返回一个中间结果—那就是yield语句的功能,返回一个值给调用者并暂停执行。当生成器的next()方法被调用的时候,它会准确地从离开的地方继续执行。
生成器是一种用普通的函数语法定义的迭代器。举个例子:
nested = [[1,2], [3, 4], [5]]
def flatten(nested):
…. for sublist in nested:
………for element in sublist:
…………….yield element
任何包含yield语句的函数成为生成器。生成器每产生一个值(使用yield语句), 函数会被冻结,即函数停在那点等待被重新唤醒。
生成器方法:
*外部作用域访问生成器的send方法,就像访问next方法一样,只不过前者使用一个参数。
*在内部则挂起生成器,yield现在作为表达式使用,换句话说,当生成器重新运行的时候,yield方法返回send方法发送的值。如果next方法被使用则yield方法返回None
注意:使用send方法,只有在生成器挂起后才有意义,即yield函数被第一次执行后。例子:
def repeater(value):
…. while True:
…….. new = (yield value)
…….. if new is not None:
………… value= new
r = repeater(42)
r.next()
输出:42
r.send(“Hello world!”)
输出:”Hello world!”

///5月26日
函数和函数式编程
函数会向调用者返回一个值, 而实际编程中大偏函数更接近过程,不显示地返回任何东西。把过程看待成函数的语言通常对于“什么都不返回”的函数设定了特殊的类型或者值的名字。这些函数在 c 中默认为“void”的返回类型,意思是没有值返回。 在 python 中, 对应的返回对象类型是none。
简而言之,当没有显式地返回元素或者如果返回 None 时, python 会返回一个 None.那么调用者接收的就是 python 返回的那个对象,且对象的类型仍然相同。如果函数返回多个对象,python 把他们聚集起来并以一个元组返回。
关键字参数
关键字参数的概念仅仅针对函数的调用。这种理念是让调用者通过函数调用中的参数名字来区分参数.
参数组
Python 同样允许程序员执行一个没有显式定义参数的函数,相应的方法是通过一个把元组(非关键字参数)或字典(关键字参数)作为参数组传递给函数。我们将在本章中讨论这两种形式。基本上,你可以将所有参数放进一个元组或者字典中,仅仅用这些装有参数的容器来调用一个函数,而不必显式地将它们放在函数调用中:
func(*tuple_grp_nonkw_args, **dict_grp_kw_args)
其中的 tuple_grp_nonkw_args 是以元组形式体现的非关键字参数组, dict_grp_kw_args 是装有关键字参数的字典。
函数属性
函数属性是 python 另外一个使用了句点属性标识并拥有名字空间的领域。注意我们是如何在函数声明外定义一个文档字串。然而我们仍然可以就像平常一样,在运行时刻访问它。然而你不能在函数的声明中访问属性。换句话说,在函数声明中没有’self‘这样的东西让你可以进行诸如dict[‘version’] = 0.1 的赋值。这是因为函数体还没有被创建,但之后你有了函数对象,就可以按我们在上面描述的那样方法来访问它的字典。另外一个自由的名字空间!
内部/内嵌函数
在函数体内创建另外一个函数(对象)是完全合法的。这种函数叫做内部/内嵌函数。因为现在python 支持静态地嵌套域.
传递函数
当学习一门如 C 的语言时,函数指针的概念是一个高级话题,但是对于函数就像其他对象的python 来说就不是那么回事了.函数是可以被引用的(访问或者以其他变量作为其别名),也作为参数传入函数,以及作为列表和字典等等容器对象的元素函数有一个独一无二的特征使它同其他对象区分开来,那就是函数是可调用的。
形式参数
python 函数的形参集合由在调用时要传入函数的所有参数组成,这参数与函数声明中的参数列表精确的配对。这些参数包括了所有必要参数,关键字参数,以及所有含有默认值,函数调用时不必要指定的参数。局部命名空间为各个参数值,创建了一个名字。一旦函数开始执行,即能访问这个名字。
位置参数
这些我们都是熟悉的标准化参数。位置参数必须以在被调用函数中定义的准确顺序来传递。另外,没有任何默认参数(见下一个部分)的话,传入函数(调用)的参数的精确的数目必须和声明的数字一致。可以不按位置地将关键字参数传入函数,给出关键字来匹配其在参数列表中的合适的位置是被准予的
.默认参数
对于默认参数如果在函数调用时没有为参数提供值则使用预先定义的的默认值。这些定义在函数声明的标题行中给出。python 中用默认值声明变量的语法是所有的位置参数必须出现在任何一个默认参数之前。
可变长度的参数
可能会有需要用函数处理可变数量参数的情况。这时可使用可变长度的参数列表。变长的参数在函数声明中不是显式命名的,因为参数的数目在运行时之前是未知的(甚至在运行的期间,每次函数调用的参数的数目也可能是不同的),这和常规参数(位置和默认)明显不同,常规参数都是在函数声明中命名的。由于函数调用提供了关键字以及非关键字两种参数类型,python 用两种方法来支持变长参数,我们了解了在函数调用中使用和*符号来指定元组和字典的元素作为非关键字以及关键字参数的方法。在这个部分中,我们将再次使用相同的符号,但是这次在函数的声明中,表示在函数调用时接收这样的参数。这语法允许函数接收在函数声明中定义的形参之外的参数。
非关键字可变长参数(元组)
当函数被调用的时候,所有的形参(必须的和默认的)都将值赋给了在函数声明中相对应的局部变量。剩下的非关键字参数按顺序插入到一个元组中便于访问。可变长的参数元组必须在位置和默认参数之后,带元组(或者非关键字可变长参数)的函数普遍的语法如下:
def function_name([formal_args,] *vargs_tuple):
“function_documentation_string”
function_body_suite
星号操作符之后的形参将作为元组传递给函数,元组保存了所有传递给函数的”额外”的参数(匹配了所有位置和具名参数后剩余的)。如果没有给出额外的参数,元组为空。
关键字变量参数(Dictionary)
在我们有不定数目的或者额外集合的关键字的情况中, 参数被放入一个字典中,字典中键为参数名,值为相应的参数值。为什么一定要是字典呢?因为为每个参数-参数的名字和参数值–都是成对给出—用字典来保存这些参数自然就最适合不过了。这给出使用了变量参数字典来应对额外关键字参数的函数定义的语法:
def function_name([formal_args,][*vargst,] **vargsd):
function_documentation_string function_body_suite
为了区分关键字参数和非关键字非正式参数,使用了双星号()。 是被重载了的以便不与幂运算发生混淆。关键字变量参数应该为函数定义的最后一个参数,带**。
函数式编程
匿名函数与 lambdapython 允许用 lambda 关键字创造匿名函数。匿名是因为不需要以标准的方式来声明,比如说,使用 def 语句。(除非赋值给一个局部变量,这样的对象也不会在任何的名字空间内创建名字.)然而,作为函数,它们也能有参数。一个完整的 lambda“语句”代表了一个表达式,这个表达式的定义体必须和声明放在同一行。我们现在来演示下匿名函数的语法:
lambda [arg1[, arg2, … argN]]: expression
lambda 表达式返回可调用的函数对象。
用合适的表达式调用一个 lambda 生成一个可以像其他函数一样使用的函数对象。它们可被传入给其他函数,用额外的引用别名化,作为容器对象以及作为可调用的对象被调用(如果需要的话,可以带参数)。当被调用的时候,如过给定相同的参数的话,这些对象会生成一个和相同表达式等价的结果。它们和那些返回等价表达式计算值相同的函数是不能区分的。,我们可以把 lambda 表达式赋值给一个如列表和元组的数据结构,其中,基于一些输入标准,我们可以选择哪些函数可以执行,以及参数应该是什么。
内建函数 apply()、filter()、map()、reduce()
我们将看看 apply(),filter(), map(), 以及 reduce()内建函数并给出一些如
何使用它们的例子。这些函数提供了在 python 中可以找到的函数式编程的特征。正如你想像的一样,lambda 函数可以很好的和使用了这些函数的应用程序结合起来,因为它们都带了一个可执行的函数对象,lambda 表达式提供了迅速创造这些函数的机制。
apply(func[, nkw][, kw]) 描述用可选的参数来调用 func,nkw 为非关键字参数,kw关键字参数;返回值是函数调用的返回值。
filter(func, seq) 调用一个布尔函数 func 来迭代遍历每个 seq 中的元素; 返回一个使 func 返回值为 ture 的元素的序列。
map(func, seq1[,seq2…]) 将函数 func 作用于给定序列(s)的每个元素,并用一个列表来提供返回值;如果 func 为 None, func 表现为一个身份函数,返回一个含有每个序列中元素集合的 n 个元组的列表。
reduce(func, seq[, init]) 将二元函数作用于 seq 序列的元素,每次携带一对(先前的结果以及下一个序列元素),连续的将现有的结果和下雨给值作用在获得的随后的结果上,最后减少我们的序列为一个单一的返回值;如果初始值 init 给定,第一个比较会是 init 和第一个序列元素而不是序列的头两个元素。
filter()
给定一个对象的序列和一个“过滤”函数,每个序列元素都通过这个过滤器进行筛选, 保留函数返回为真的的对象。filter 函数为已知的序列的每个元素调用给定布尔函数。每个 filter 返回的非零(true)值元素添加到一个列表中。
如果我们想要用纯 python 编写 filter(),它或许就像这样:
def filter(bool_func, seq):
filtered_seq = []
for eachItem in seq:
if bool_func(eachItem):
filtered_seq.append(eachItem)
return filtered_seq

map()
map()内建函数与 filter()相似,因为它也能通过函数来处理序列。然而,不像 filter(), map()将函数调用“映射”到每个序列的元素上,并返回一个含有所有返回值的列表。
在最简单的形式中,map()带一个函数和队列, 将函数作用在序列的每个元素上, 然后创建由每次函数应用组成的返回值列表。

reduce()
reduce 使用了一个二元函数(一个接收带带两个值作为输入,进行了一些计算然后返回一个值作为输出),一个序列,和一个可选的初始化器,卓有成效地将那个列表的内容“减少”为一个单一的值,如同它的名字一样。在其他的语言中,这种概念也被称作为折叠。
它通过取出序列的头两个元素,将他们传入二元函数来获得一个单一的值来实现。然后又用这个值和序列的下一个元素来获得又一个值,然后继续直到整个序列的内容都遍历完毕以及最后的值会被计算出来为止。
reduce(func, [1, 2, 3])=func(func(1, 2), 3)
偏函数应用
currying 的概念将函数式编程的概念和默认参数以及可变参数结合在一起。一个带 n 个参数,curried 的函数固化第一个参数为固定参数,并返回另一个带 n-1 个参数函数对象,你可以通过使用 functional 模块中的 partial()函数来创建 PFA:
/// from operator import add, mul
/// from functools import partial
/// add1 = partial(add, 1)
/// add1(x) == add(1, x)
/// mul100 = partial(mul, 100) # mul100(x) == mul(100, x)
///
/// add1(10)
11
/// add1(1)
2

globa 语句
如果将全局变量的名字声明在一个函数体内的时候,全局变量的名字能被局部变量给覆盖掉。我们局部的 bar 将全局的 bar 推出了局部作用域。为了明确地引用一个已命名的全局变量,必须使用 global 语句。global 的语法如下:
global var1[, var2[, … varN]]]
闭包
如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是 closure。定义在外部函数内的但由内部函数引用或者使用的变量被称为自由变量。闭包将内部函数自己的代码和作用域以及外部函数的作用结合起来。闭包的词法变量不属于全局名字空间域或者局部的–而属于其他的名字空间,带着“流浪”的作用域。(注意这不同于对象因为那些变量是存活在一个对象的名字空间但是闭包变量存活在一个函数的名字空间和作用域),Closurs 对于安装计算,隐藏状态,以及在函数对象和作用域中随意地切换是很有用的。closurs在 GUI 或者在很多 API 支持回调函数的事件驱动编程中是很有些用处的。以绝对相同的方式,应用于获取数据库行和处理数据。回调就是函数。闭包也是函数,但是他们能携带一些额外的作用域。它们仅仅是带了额外特征的函数……另外的作用域。
现在,在很多情况下,类是最适合使用的。闭包更适合需要一个必需有自己的作用域的回调函数情况,尤其是回调函数是很小巧而且简单的,通常也很聪明。跟平常一样,如果你使用了闭包,对你的代码进行注释或者用文档字符串来解释你正做的事是很不错的主意.
生成器
什么是 python 式的生成器?从句法上讲,生成器是一个带 yield 语句的函数。一个函数或者子程序只返回一次,但一个生成器能暂停执行并返回一个中间的结果—-那就是 yield 语句的功能, 返回一个值给调用者并暂停执行。当生成器的 next()方法被调用的时候,它会准确地从离开地方继续.
简单的生成器特性
与迭代器相似,生成器以另外的方式来运作:当到达一个真正的返回或者函数结束没有更多的值返回(当调用 next()),一个 StopIteration 异常就会抛出。
一些加强特性加入到生成器中,所以除了 next()来获得下个生成的值,用户可以将值回送给生成器[send()],在生成器中抛出异常,以及要求生成器退出[close()],由于双向的动作涉及到叫做 send()的代码来向生成器发送值(以及生成器返回的值发送回来),生成器带有一个初始化的值,对每次对生成器[next()]调用以 1 累加计数。用户已可以选择重置这个值,如果他们非常想要用新的值来调用 send()不是调用 next()。这个生成器是永远运行的,现在 yield 语句必须是一个表达式,因为当回到生成器中继续执行的时候,你或许正在接收一个进入的对象。所以如果你想要终结它,调用 close()方法。

更多推荐

python学习09-函数和函数式编程