简明Python教程学习笔记_2_函数

来源:互联网 发布:ar java技术实现 编辑:程序博客网 时间:2024/05/01 01:07

函数

为什么要有函数?因为在平时写代码时,如果没有函数的话,那么将会出现很多重复的代码,这样代码重用率就比较低。。。并且这样的代码维护起来也是很有难度的,为了解决这些问题,就出现了函数,用来将一些经常出现的代码进行封装,这样就可以在任何需要调用这段代码的地方调用这个函数就行了。

函数的定义:函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可

函数通过def关键字定义。def关键字后跟一个函数的 标识符 名称,然后跟一对圆括号。圆括号之中可以包括一些变量名,该行以冒号结尾。接下来是一块语句,它们是函数体。下面这个例子将说明这事实上是十分简单的:

#!/usr/bin/python# Filename: function1.pydef sayHello():  print 'Hello World!' # block belonging to the functionsayHello() # call the function输出$ python function1.pyHello World

函数参数

参数在函数定义的圆括号对内指定,用逗号分割。当我们调用函数的时候,我们以同样的方式提供值。

形参和实参的区别:

函数在定义的时候,函数名后面的括号中可以添加参数,这些参数就叫做形参,形参:顾名思义就是形式参数,只是一个代号。
实参是在调用函数的时候函数名后面的括号中的参数,形参和实参需要一一对应起来,否则调用函数会报错。

注意:我们使用过的术语——函数中的参数名称为 形参 而你提供给函数调用的值称为实参

前面提到函数的形参和实参要一一对应,那么参数对应有如下几种:

  1. 必须参数:必须参数必须以对应的关系一个一个传递进入函数,函数调用时传递的实参必须和函数定义时的形参一一对应,不能多也不能少,顺序也得一致。
  2. 关键字参数:关键字参数是实参里面的概念,在调用函数的时候声明某个参数是属于某个关键字的。使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
  3. 默认参数:默认参数是在函数声明的时候,可以给某个参数指定默认值,这样的参数叫做默认值参数。如果在调用函数的时候,默认参数没有接收到对应的实参,那么就会将默认值赋值给这个参数。
  4. 不定长参数 *args 。(即 python 中的 元组 类型)。在python里面,函数在声明的时候,参数中可以使用(*变量名)的方式来接受不确定长度的参数,但是在python里面大家约定俗成使用*args接受不定长参数,这样在调用函数的时候传递的参数就可以是不定长度的了。args接受了不定长参数之后,将这些参数放到一个tuple里面,可以通过访问args来获取这些不定长参数。
  5. 不定长参数 **kwargs。 (即 Python 中的 字典 类型)。假如有类似于关键字参数的不定长参数该怎么办呢?python里面使用(**变量名)来接收不定长的命名变量参数。同样,python里面也约定俗成使用**kwargs接收不定长命名参数。kwargs接收了不定长参数之后,将这些参数放到一个字典里面,可以通过key获取到相应的参数值。
# 1. 必须参数def f(name,age):    print(name,age)f("小明",18)# 2. 关键字参数def f(name,age):    print(name,age)f(name="小明",18)# 3. 命名参数def f(name,age,sex="male"):    print(name,age,sex)f(name="小明",18)# 4.  不定长参数(元组类型)def f(*args):    print(args)f("小明",18,"male")# 5.  不定长参数(字典类型)def f(**kwargs):    print(kwargs)f(name="小明",age=18,sex="male")

这些参数混合使用的情况:

假如一个函数使用了上面所有种类的参数,那该怎么办?为了不产生歧义,python里面规定了假如有多种参数混合的情况下,遵循如下的顺序使用规则:

def f(必须参数,默认参数,*args,**kwargs):    pass

如果同时存在args和kwargs的话,args在左边

默认参数在必须参数的右边,在*args的左边

关键字参数的位置不固定(ps:关键字参数也不在函数定义的时候确定)

那么,假如有一个列表想要传递进入一个不定长的未命名参数的函数中去,可以在该列表前面加上*实现,同理如果想传递一个字典进入不定长命名参数的函数中去,可以在该字典前面加上**

def f(*args,**kwargs):    print(args)    for i in kwargs:        print("%s:%s"%(i,kwargs[i]))f(*[1,2,3],**{"a":1,"b":2})

使用形参

#!/usr/bin/python# Filename: func_param.pydef printMax(a, b):  if a > b:    print a, 'is maximum'  else:    print b, 'is maximum'printMax(3, 4)# directly give literal valuesx = 5y = 7printMax(x, y) # give variables as arguments输出$ python func_param.py4 is maximum7 is maximum

在第一个printMax使用中,我们直接把数,即实参,提供给函数。在第二个使用中,我们使用变量调用函数。printMax(x, y)使实参x的值赋给形参a,实参y的值赋给形参b。在两次调用中,printMax函数的工作完全相同。

使用默认参数

#!/usr/bin/python# Filename: func_default.pydef say(message, times =1):  print message * timessay('Hello')say('World', 5)输出$ python func_default.pyHelloWorldWorldWorldWorldWorld 

名为say的函数用来打印一个字符串任意所需的次数。如果我们不提供一个值,那么默认地,字符串将只被打印一遍。我们通过给形参times指定默认参数值1来实现这一功能。

在第一次使用say的时候,我们只提供一个字符串,函数只打印一次字符串。在第二次使用say的时候,我们提供了字符串和参数5,表明我们想要 这个字符串消息5遍。

重要

只有在形参表末尾的那些参数可以有默认参数值,即你不能在声明函数形参的时候,先声明有默认值的形参而后声明没有默认值的形参。
这是因为赋给形参的值是根据位置而赋值的。例如,def func(a, b=5)是有效的,但是def func(a=5, b)无效 的。 

关键参数

如果你的某个函数有许多参数,而你只想指定其中的一部分,那么你可以通过命名来为这些参数赋值——这被称作 关键参数 ——我们使用名字(关键字)而不是位置(我们前面所一直使用的方法)来给函数指定实参。

这样做有两个 优势 ——一,由于我们不必担心参数的顺序,使用函数变得更加简单了。二、假设其他参数都有默认值,我们可以只给我们想要的那些参数赋值。

使用关键参数

#!/usr/bin/python# Filename: func_key.pydef func(a, b=5, c=10):  print 'a is', a, 'and b is', b, 'and c is', cfunc(3, 7)func(25, c=24)func(c=50, a=100)输出$ python func_key.pya is 3 and b is 7 and c is 10a is 25 and b is 5 and c is 24a is 100 and b is 5 and c is 50 

名为func的函数有一个没有默认值的参数,和两个有默认值的参数。

在第一次使用函数的时候, func(3, 7),参数a得到值3,参数b得到值7,而参数c使用默认值10。

在第二次使用函数func(25, c=24)的时候,根据实参的位置变量a得到值25。根据命名,即关键参数,参数c得到值24。变量b根据默认值,为5。

在第三次使用func(c=50, a=100)的时候,我们使用关键参数来完全指定参数值。注意,尽管函数定义中,a在c之前定义,我们仍然可以在a之前指定参数c的值。

LEGB作用域

python中的作用域分4种情况:L:local,局部作用域,即函数中定义的变量;E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;G:globa,全局变量,就是模块级别定义的变量;B:built-in,系统固定模块里面的变量,比如int, bytearray等。 搜索变量的优先级顺序依次是:域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。local和enclosing是相对的,enclosing变量相对上层来说也是local。在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如if、try、for等)不会引入新的作用域。

变量的修改(错误修改,面试题里经常出):

x=6def f2():    print(x)    x=5f2()# 错误的原因在于print(x)时,解释器会在局部作用域找,会找到x=5(函数已经加载到内存),但x使用在声明前了,所以报错:# local variable 'x' referenced before assignment.如何证明找到了x=5呢?简单:注释掉x=5,x=6# 报错为:name 'x' is not defined#同理x=6def f2():    x+=1 #local variable 'x' referenced before assignment.f2()

局部变量

当你在函数定义内声明变量的时候,它们与函数外具有相同名称的其他变量没有任何关系,即变量名称对于函数来说是 局部 的。这称为变量的 作用域 。所有变量的作用域是它们被定义的块,从它们的名称被定义的那点开始。

使用局部变量

#!/usr/bin/python# Filename: func_local.pydeffunc(x):  print 'x is', x  x = 2  print 'Changed local x to', xx = 50func(x)print 'x is still', x

使用golab语句

如果你想要为一个定义在函数外的变量赋值,那么你就得告诉Python这个变量名不是局部的,而是全局 的。我们使用global语句完成这一功能。没有global语句,是不可能为定义在函数外的变量赋值的。你可以使用定义在函数外的变量的值(假设在函数内没有同名的变量)。然而,我并不鼓励你这样做,并且你应该尽量避免这样做,因为这使得程序的读者会不清楚这个变量是在哪里定义的。使用global语句可以清楚地表明变量是在外面的块定义的。内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了,当修改的变量是在全局作用域(global作用域)上的,就要使用global先声明一下,代码如下:

#!/usr/bin/python# Filename: func_global.pydeffunc():  global x  print'x is', x  x = 2  print 'Changed local x to', xx = 50func()print 'Value of x is', x输出:$ python func_global.pyx is 50Changed global x to 2Value of x is 2 

global语句被用来声明x是全局的——因此,当我们在函数内把值赋给x的时候,这个变化也反映在我们在主块中使用x的值的时候。

一个global语句指定多个全局变量。例如global x, y, z

nonlocal关键字

global声明的变量必须在全局作用域上,不能嵌套作用域上,当要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量怎么办呢,这时就需要nonlocal关键字了

def outer():    count = 10    def inner():        nonlocal count        count = 20        print(count)    inner()    print(count)outer()
小结:1. 变量查找顺序:LEGB,作用域局部>外层作用域>当前模块中的全局>python内置作用域;2. 只有模块、类、及函数才能引入新作用域;3. 对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会使用外部作用域的变量;4. 内部作用域要修改外部作用域变量的值时,全局变量要使用global关键字,嵌套作用域变量要使用nonlocal关键字。    nonlocal是python3新增的关键字,有了这个 关键字,就能完美的实现闭包了。

return语句

#!/usr/bin/python# Filename: func_return.pydef maximum(x, y):  if x > y:    return x  else:    return yprint maximum(2,3)

注意,没有返回值的return语句等价于return NoneNone是Python中表示没有任何东西的特殊类型。例如,如果一个变量的值为None,可以表示它没有值。

除非你提供你自己的return语句,每个函数都在结尾暗含有return None语句。通过运行print someFunction(),你可以明白这一点,函数someFunction没有使用return语句,如同:

def someFunction():  pass

注意: 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,也可以理解为 return 语句代表着函数的结束 如果未在函数中指定return,那这个函数的返回值为None
return多个对象,解释器会把这多个对象组装成一个元组作为一个一个整体结果输出。

def func():    return 123, 'abc', 'efg'if __name__ == "__main__":    print type(func())    print func()    pass
结果
<type 'tuple'>
(123, 'abc', 'efg')

DocStrings

#!/usr/bin/python# Filename: func_doc.pydef printMax(x, y):  '''Prints the maximum of two numbers.  The two values must be integers.'''  x = int(x) # convert to integers, if possible  y = int(y)  if x > y:    print x, 'is maximum'  else:    print y, 'is maximum'printMax(3, 5)print printMax.__doc__输出$ python func_doc.py5 is maximumPrints the maximum of two numbers.The two values must be integers. 

在函数的第一个逻辑行的字符串是这个函数的 文档字符串 。注意,DocStrings也适用于模块和类,我们会在后面相应的章节学习它们。

文档字符串的惯例是一个多行字符串,它的首行以大写字母开始,句号结尾。第二行是空行,从第三行开始是详细的描述。 强烈建议 你在你的函数中使用文档字符串时遵循这个惯例。

你可以使用__doc__(注意双下划线)调用printMax函数的文档字符串属性(属于函数的名称)。请记住Python把每一样东西 都作为对象,包括这个函数。我们会在后面的类一章学习更多关于对象的知识。

如果你已经在Python中使用过help(),那么你已经看到过DocStings的使用了!它所做的只是抓取函数的__doc__属性,然后整洁地展示给你。你可以对上面这个函数尝试一下——只是在你的程序中包括help(printMax)。记住按q退出help

函数式编程

函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。

高阶函数英文叫Higher-order function。什么是高阶函数?下面以实际代码为例子,一步一步深入概念。

函数式编程的几个技术

1.map & reduce
基于命令式的编程语言中,如果对一个数据集进行特定操作的时候,需要使用for或者是while循环,让用户在循环语句内部进行操作,并且所有的边界条件都是用户自己定义的,所以就会出现边界溢出的bug。而map和reduce思想就是用一种更加数学化(理论化)的编程。

2.Pipeline
之前map、reduce是将一组数据放到特定的函数里面进行操作,这里的操作函数一般就一个。管道(Pipeline)就像是Linux系统中的管道一样。数据依此通过不同的管道,最后输出。

3.recuring
递归最大的好处就简化代码,他可以把一个复杂的问题用很简单的代码描述出来。注意:递归的精髓是描述问题,而这正是函数式编程的精髓。

4.currying
把一个函数的多个参数分解成多个函数, 然后把函数多层封装起来,每层函数都返回一个函数去接收下一个参数这样,可以简化函数的多个参数。而且每一层只专注最少的功能性代码,所以你的代码可维护性将大大提高,出现bug的概率也会大大降低。

5.higher order function
高阶函数:所谓高阶函数就是函数当参数,把传入的函数做一个封装,然后返回这个封装函数。现象上就是函数传进传出,就像面向对象对象满天飞一样。

6.lazy evaluation
惰性求值:这个需要编译器的支持。表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值,也就是说,语句如x:=expression; (把一个表达式的结果赋值给一个变量)明显的调用这个表达式被计算并把结果放置到 x 中,但是先不管实际在 x 中的是什么,直到通过后面的表达式中到 x 的引用而有了对它的值的需求的时候,而后面表达式自身的求值也可以被延迟,最终为了生成让外界看到的某个符号而计算这个快速增长的依赖树。

7.determinism
确定性:所谓确定性的意思就是像数学那样 f(x) = y ,这个函数无论在什么场景下,都会得到同样的结果,这个我们称之为函数的确定性。而不是像程序中的很多函数那样,同一个参数,却会在不同的场景下计算出不同的结果。所谓不同的场景的意思就是我们的函数会根据一些运行中的状态信息的不同而发生变化。

变量可以指向函数

以Python内置的求绝对值的函数abs()为例,调用该函数用以下代码:

>>> abs(-10)10
但是,如果只写abs呢?
>>> abs<built-in function abs>
可见,abs(-10)是函数调用而abs是函数本身。要获得函数调用结果,我们可以把结果赋值给变量:
>>> x = abs(-10)>>> x10
但是,如果把函数本身赋值给变量呢?
>>> f = abs>>> f<built-in function abs>
结论:函数本身也可以赋值给变量,即:变量可以指向函数。
如果一个变量指向了一个函数,那么,可否通过该变量来调用这个函数?用代码验证一下:
>>> f = abs>>> f(-10)10成功!说明变量f现在已经指向了abs函数本身。

函数名也是变量

那么函数名是什么呢?函数名其实就是指向函数的变量!对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数!

如果把abs指向其他对象,会有什么情况发生?>>> abs = 10>>> abs(-10)Traceback (most recent call last):  File "<stdin>", line 1, in <module>TypeError: 'int' object is not callable把abs指向10后,就无法通过abs(-10)调用该函数了!因为abs这个变量已经不指向求绝对值函数了!当然实际代码绝对不能这么写,这里是为了说明函数名也是变量。要恢复abs函数,请重启Python交互环境。
注:由于abs函数实际上是定义在__builtin__模块中的,所以要让修改abs变量的指向在其它模块也生效,要用__builtin__.abs = 10。

高阶函数 — 函数做为参数

既然变量可以指向函数,函数的参数能接收变量,那么一个函数可以接收另一个函数作为参数(即函数作为参数传入),这种函数就称之为高阶函数

一个最简单的高阶函数:

def add(x, y, f):    return f(x) + f(y)当我们调用add(-5, 6, abs)时,参数x,y和f分别接收-5,6和abs,根据函数定义,我们可以推导计算过程为:x ==> -5y ==> 6f ==> absf(x) + f(y) ==> abs(-5) + abs(6) ==> 11用代码验证一下:>>> add(-5, 6, abs)11编写高阶函数,就是让函数的参数能够接收别的函数。
小结:把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。

python 内建的高阶函数

map:我们在使用map函数时候,map函数需要接收两个参数,第一个参数是函数,第二个参数是序列,那么表示的含义就是map将传入的函数依次作用在序列中的每一个元素,并把结果以列表的形式返回。

def f(x):    return x*xprint map(f,[1,2,3,4,5,6,7,8,9,10])  #list里的每个元素都会走一遍f(x)方法
def doubleMe(para):    return para*2result=map(doubleMe,range(1,11))#结果是result=[2,4,6,8,10,12,14,16,18,20]#可以结合上面将的lambda函数直接写成一行result2=map(lambda x: x*2,range(1,11))#结果是result2=[2,4,6,8,10,12,14,16,18,20],和上面的一致#function函数需要同时去提取同位置的iterable data的元素,然后进行操作,最后还是推出list。result=map(lambda x,y :x+y, range(1,5),range(6,10))#结果是result=[7,9,11,13]result=map(lambda x,y :x+y, range(1,6),range(6,10))#结果是会报错误:TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'# 传递给map的是一个None的function,实际上就是做了个数据转移操作,将数据从iterable data转移到list中。map(None,range(1,3))#输出 [1,2]map(None,range(1,3),range(5,7))#输出[(1, 5), (2, 6)]#可以看到第二个有多个参数组的情况下,只是把同下标的给“打包”了
reduce:reduce函数和map函数有什么不同之处呢?reduce函数也需要两个参数:函数和序列。reduce参数中的函数必须接收两个参数,那么reduce函数表示的含义则为把返回的结果继续和序列的下一个元素做累积计算,典型例子比如对序列求和。
sum = reduce(lambda a, x: a + x, [0, 1, 2, 3, 4])print sum# => 10
def f2(x,y):    return x+y     print reduce(f2,[1,2,3,4,5,6,7,8,9,10])
def fn(x,y):    return x*10+y     print reduce(fn,[1,2,3,4,5,6,7,8])
# 计算 6!= 120import functoolsfunctools.reduce(lambda x,y:x*y,range(1,11))  
from functools import reducedef add1(x,y):    return x + yprint (reduce(add1, range(1, 101)))## 4950 (注:1+2+...+99)print (reduce(add1, range(1, 101), 20))## 4970 (注:1+2+...+99+20)
result = reduce(lambda x,y : x+y, range(1,5))#结果是result=10result = reduce(lambda x,y : x+y, range(1,5),3)#结果是result=13
filter:filter函数用于过滤序列中某些元素。和map、reduce函数一样,filter也接收一个函数和一个序列,不同的是,filter把传入的函数参数作用于序列中每一个元素,然后根据返回值判断是true还是false来决定该元素是否被丢弃。
result = filter(lambda x: x>3, range(1,5))#结果是result=[4]result = filter(None,range(1,3))#结果是result=[1,2]result=filter(None,[False,True])# 结果是result=[True]
def is_odd(x):       return x%2==1    print filter(is_odd,[1,2,3,4,5,6,7])结果是:[1, 3, 5, 7]
str_list = ['a', 'b', 'c', 'd']def fun1(s):    if s != 'a':        return sif __name__ == "__main__":    ret = filter(fun1, str_list)    print(list(ret))  # ret是一个迭代器对象    pass

sorted:sorted函数用于对序列排序,这个函数之前的Python总结里多次提到,这里我简单说一下通常的规定吧:对于两个元素x和y,如果x<y,返回-1,如果x>y ,返回1,如果x==y,返回0。这样默认的规定下,我们排序的结果是从小到大排序的,所以如果想得到从大到小的结果,我们需要重写sorted函数的规定,这个和C++是一样的道理吧。

def reversed_cmp(x, y):    if x > y:        return -1    if x < y:        return 1    return 0>>> sorted([36, 5, 12, 9, 21], reversed_cmp)[36, 21, 12, 9, 5]
我们再看一个字符串排序的例子:
>>> sorted(['about', 'bob', 'Zoo', 'Credit'])['Credit', 'Zoo', 'about', 'bob']
默认情况下,对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。
现在,我们提出排序应该忽略大小写,按照字母序排序。
要实现这个算法,不必对现有代码大加改动,只要我们能定义出忽略大小写的比较算法就可以:
def cmp_ignore_case(s1, s2):    u1 = s1.upper()    u2 = s2.upper()    if u1 < u2:        return -1    if u1 > u2:        return 1    return 0
忽略大小写来比较两个字符串,实际上就是先把字符串都变成大写(或者都变成小写),再比较。
这样,我们给sorted传入上述比较函数,即可实现忽略大小写的排序:
>>> sorted(['about', 'bob', 'Zoo', 'Credit'], cmp_ignore_case)['about', 'bob', 'Credit', 'Zoo']

lambda形式

#!/usr/bin/python# Filename: lambda.pydef make_repeater(n):    return lambda s: s*ntwice = make_repeater(2)print twice('word')print twice(5)

输出

$ python lambda.pywordword10

语法格式:lambda [arg1 [,arg2,.....argn]]:expression

如下实例:

#可写函数说明sum = lambda arg1, arg2: arg1 + arg2;#调用sum函数print "Value of total : ", sum( 10, 20 )print "Value of total : ", sum( 20, 20 )以上实例输出结果:Value of total : 30Value of total : 40

它如何工作

这里,我们使用了make_repeater函数在运行时创建新的函数对象,并且返回它。lambda语句用来创建函数对象。本质上,lambda需要一个参数,后面仅跟单个表达式作为函数体,而表达式的值被这个新建的函数返回。注意,即便是print语句也不能用在lambda形式中,只能使用表达式。

匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。

用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:

递归函数

递归函数定义:递归函数就是在函数内部调用自己

有时候解决某些问题的时候,逻辑比较复杂,这时候可以考虑使用递归,因为使用递归函数的话,逻辑比较清晰,可以解决一些比较复杂的问题。但是递归函数存在一个问题就是假如递归调用自己的次数比较多的话,将会使得计算速度变得很慢,而且在python中默认的递归调用深度是1000层,超过这个层数将会导致“爆栈”。。。所以,在可以不用递归的时候建议尽量不要使用递归。

递归函数的优点:定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
递归特性:
1. 必须有一个明确的结束条件
2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
3. 递归效率不高,递归层次过多会导致栈溢出
(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返 回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。)

def factorial(n):  # 使用循环实现求和    Sum=1    for i in range(2,n+1):        Sum*=i    return Sumprint(factorial(7))def recursive_factorial(n):  # 使用递归实现求和    return (2 if n==2 else n*recursive_factorial(n-1))print(recursive_factorial(7))def feibo(n):  # 使用递归实现菲波那切数列    if n==0 or n==1:return n    else:return feibo(n-1)+feibo(n-2)print(feibo(8))def feibo2(n):  # 使用循环实现菲波那切数列    before,after=0,1    for i in range(n):        before,after=after,before+after    return beforeprint(feibo2(300))

偏函数

Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。要注意,这里的偏函数和数学意义上的偏函数不一样。

在介绍函数参数的时候,我们讲到,通过设定参数的默认值,可以降低函数调用的难度。而偏函数也可以做到这一点。举例如下:

int()函数可以把字符串转换为整数,当仅传入字符串时,int()函数默认按十进制转换:

>>> int('12345')12345
但int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做N进制的转换:
>>> int('12345', base=8)5349>>> int('12345', 16)74565
假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,我们想到,可以定义一个int2()的函数,默认把base=2传进去:
def int2(x, base=2):    return int(x, base)
这样,我们转换二进制就非常方便了:
>>> int2('1000000')64>>> int2('1010101')85
functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2:
>>> import functools>>> int2 = functools.partial(int, base=2)>>> int2('1000000')64>>> int2('1010101')85
所以,简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

注意到上面的新的int2函数,仅仅是把base参数重新设定默认值为2,但也可以在函数调用时传入其他值:

>>> int2('1000000', base=10)1000000
最后,创建偏函数时,实际上可以接收函数对象、*args和**kw这3个参数,当传入:
int2 = functools.partial(int, base=2)
实际上固定了int()函数的关键字参数base,也就是:
int2('10010')
相当于:
kw = { base: 2 }int('10010', **kw)
当传入:
max2 = functools.partial(max, 10)
实际上会把10作为*args的一部分自动加到左边,也就是:
max2(5, 6, 7)
相当于:
args = (10, 5, 6, 7)max(*args)
结果为10。

小结:当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。




0 0
原创粉丝点击