函数和函数式编程
函数会向调用者返回一个值, 而实际编程中大偏函数更接近过程,不显示地返回任何东西。把过程看待成函数的语言通常对于“什么都不返回”的函数设定了特殊的类型或者值的名字。这些函数在 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()方法。