Python高级编程——6.函数
递归、函数变量赋值、参数中的函数、匿名函数、闭包、偏函数
一、递归:
函数的递归,就是让在函数的内部调用函数自身的情况,这个函数就是递归函数。
注意:递归一定要有结束条件,否则就会成为一个死循环。
以下是两个递归函数的例子:
执行结果:
二、函数变量赋值:
我们可以将一个函数赋值给一个变量(注意,不是调用哦,下图t1的例子),这时这个变量也会指向该函数。
运行结果:
以上例子说明t1是复制的函数体,与printMsg不同名但是同一地址的函数,两个引用同时指向同一地址。
三、参数中的函数:
函数作为一个对象,我们同样可以将函数当成一个实际参数传递给另一个函数进行处理,这个是动态语言才用的特性,也使得动态语言变得相当的灵活和好用哦~~~
这里将add()函数作为一个参数fn1传递给了fn()函数,在fn()函数内部调用了fn1()函数,也就是add()函数。
四、匿名函数:
例如:前面我们使用的在一个函数中,参数是另外一个函数。常规方式当我们调用的时候,需要创建一个函数,昨晚调用函数的参数。但是我们也可以使用匿名函数来完成。
匿名函数的格式如下:
无参匿名函数 lambda://代码 带参匿名函数 lambda x,y :x+y |
这样写的好处就是,代码简化,坏处就是降低了阅读性,且不能写多行。
匿名函数的调用:
五、偏函数:
常规函数操作中,我们在函数的参数中可以添加参数的默认值来简化函数的操作,偏函数也可以做到这一点,偏函数可以在一定程度上更加方便的管理我们的函数操作。
偏函数通过内置模块functools的partial函数进行定义和处理。
# functools.partial()函数语法结构 新函数名称 = functools.partial(函数名称, 默认赋值参数) # 原始的2进制数据转换 int("111", base=2) ~执行结果:7 # 引入我们需要的模块functools import functools # 通过偏函数扩展一个新的函数int2 int2 = functools.partial(int, base=2) # 使用新的函数,新的函数等价于上面的 int("111", base=2) int2("111") ~执行结果:7 |
我们可以通过以下例子再次阐明: 求任意数和一个默认赋值参数的和
以上代码运行结果:
也就说sum()函数和new_sum()函数的两次调用结果是一样的,前者需要每次调用时都重复写默认参数y=5,而后者通过functools.partial(函数名,默认参数=默认值),每次调用时不必重复写默认参数,只需要给其他参数值即可。这就是偏函数,很大程度上简化了代码。
六、闭包:
在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
javascript也是解释型语言,也有闭包的概念 。请看下面的例子:
执行结果:
我们主要看一下res中保存内容,实则是inner函数在内存中的地址。分析函数运行的顺序:当外函数被调用时首先执行的是print(“外函数”),而inner()函数因为只是定义了,没有被调用,所以不会打印。return inner,即返回的是inner函数的地址,将此地址赋值给res。请看内存分析图:
我们知道在堆内存中,释放内存是依据GC(垃圾回收),在堆内存中保存的数据如果没有引用指向它,GC检测到便会将其回收,如以上例子,若是嵌套函数,回收顺序是倒着回收,也就是先回收内函数。若将函数变成闭包,内存函数的地址重新被res指向,则导致不会被回收,整个函数将不会被释放,函数内的变量也会一直存在。
闭包思考:
1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成
2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
6.1 看一个闭包的实际例子:
def line_conf(a, b):
def line(x):
return a*x + b
return line
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5))
print(line2(5))
这个例子中,函数line与变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。
如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。