洗礼灵魂,修炼python(22)--自定义函数(3)—函数作用域,闭包

前面你看到嵌套两层的函数,也许你有感而发,想来点刺激的对不?那么如果每层内的变量名如果相同会怎样?拿个例子看下就知道:

报错了,报错信息大意是,本地变量‘num’引用前没有被赋值定义。这咋回事,我外层不是定义了吗?这个问题就涉及到今天要说的话题——函数作用域

函数作用域

1.作用域分三个:本地作用域(local),函数内作用域(enclosing),全局作用域(global)

2.三个作用域的优先级规则是这样:L>E>G>B

这里的B是什么呢?是Built-in,眼熟不,好像在哪里用过对吧?我们查看内置函数的命令是什么?dir(__builtins__)对吧?对了,就是这个,这个是内置作用域,知道就行,一般不会拿出来与前面三个作用域做比较

3.包含关系:

 4.函数定义了本地作用域,模块定义了全局作用域:

1):每个模块都是一个全局作用域,所以全局作用域的范围是单个程序文件

2):每次对函数的调用都会创建一个新的本地作用域,赋值的变量除非声明为全局变量,否则均为本地变量

3):所有的变量名都可以归纳为本地,全局或内置(由__builtin__模块提供)

4):任何的函数在调用结束后,其内的变量都是消失

 

5.因为有了三个作用域,则会产生全局变量,局部变量

1)全局变量:指除了函数内的外层变量

2)局部变量:指函数内部的变量

 

6.局部变量和全局变量互不影响,即使变量名相同也如此。

看完以上的概念,上面的例子为何出错应该很好理解了。

在函数内修改外部的全局变量是不可行的,因为python会自动屏蔽掉,但函数内部都可以访问全局变量。

怎么屏蔽的呢?在函数内部创建一个与全局变量相同的局部变量来屏蔽,当你调用时因为优先级关系会自动的调用函数内部的那个变量,而外层的同名变量则永远也不会被调用

例:

 

结果显而易见,num并没有被改掉,这就是屏蔽的效果。num=100这个变量为全局变量,而函数temp内的num=50是局部变量。

那么我确实想改呢?方法是有的,加入一个global参数即可,例:

 

注意:global关键词不能写成  【global num=50 】,必须分开写,不然报错

 

改了对吧?但其实你有没有发现,其实是局部变量把全局变量改了,调用函数后,全局变量就变了

那假如我不给global是什么结果:

 

由此可以看出,在python中,函数内调用函数外的变量是可以的,当内部没有此变量时,会自动由优先级高到低一一审查,如果这个变量事先确实没有定义,才抛出一个错误【此变量没有被定义】。同时,我还要补充一点的是,代码运行方式是由上而下的,由上面两个例子可以看出。

 

那么你也许会想,因为上面的例子是局部变量改全局变量,我能不能全局变量改函数内的局部变量呢?这个问题留着,自己下去测试体会

 

那么既然有global,有没有和global类似的另一个关键词呢?有的——nonlocal,这里暂且不说,下面在合适的地方提到

 7.内嵌函数:内嵌函数前面其实也提到了一点,就是函数内再嵌套一层或者多层函数,这种就叫内嵌函数

例:

注意:

1).在最后必须加入inn()这段代码,这才是调用inn函数的命令

2).调用函数只能调用外层函数,不能直接调用内层函数,不然报错,因为内部函数整作用域都在外部函数之内

 

3).inn()的调用并且是在out函数层内加入,而非在inn函数层加,如果不加入,则:

 

 

再看一个例子:

这里例子也很经典,符号‘.’是类的方法的调用【类在后面会讲到】,而此时是函数,函数的调用时是使用符号‘()’,所以以上代码就好解释了

注意,由于前面说的【任何函数调用结束时,其内的变量消失】,但此时的a1是对f1的调用,f1调用完变量就消失,而此时的a1调用时变量还在,因为在函数嵌套时,还有一层函数,所以并没有消失。

所以,总结:如果调用函数是直接返回内层函数(比如这里的a1函数调用),内层会自动记忆外层函数变量,这种行为就是函数的闭包(也叫闭合),也就是所谓的工厂函数

 

下一篇博文详细介绍闭包问题

posted @ 2017-10-10 18:12  Eeyhan  阅读(194)  评论(0编辑  收藏  举报