一个函数中存在另一个函数(定义/调用),这种方式咱们称之为函数嵌套。因此:函数的嵌套主要分为嵌套调用
,以及嵌套定义
。编程
函数的嵌套调用 def max2(a,b): # 判断两个变量的最大值 return a if a > b else b def max4(a,b,c,d): # 判断四个变量的最大值 res1 = max2(a,b) # 函数的嵌套调用 res2 = max2(res1,c) res3 = max(res2,d) print(res3) max4(10,100,21,99) 函数的嵌套定义 def func1(): print('from func1') def func2(): print('from func2') def func3(): print('from func3') func3() # 只有在func2中才能调用内部定义的函数func3 func2() func1()
注意:在函数的内部定义函数,只能在函数内部进行调用,在其余地方是没法进行调用,强行调用就会提示NameError异常,因此说函数是有可见范围的,这就涉及到了做用域了闭包
一个标识符的可见范围,叫作标识符的做用域。通常常说的是变量的做用域。根据做用的范围主要分为全局做用域
和局部做用域
。app
x = 1 # 全局变量 def outer(): def inner(): y = 100 # 局部变量 print(x) inner() outer() print(y)
观察下面的例子:dom
x = 1 def outer(): def inner(): x += 1 return x inner() outer()
代码是从上到下执行的,所欲这样写也没什么毛病,可是这里这个例子是没法执行的,为何呢?编程语言
函数是做为一个总体一块儿被解释的
。x += 1
),那么它就不会在调用全局变量x,而是标识x是局部定义的变量如何解决呢?有两种方法:更换变量名称
、声明当前变量非本地变量(global
)函数
x = 1 def outer(): def inner(): y = x + 1 # 这里定义的y是局部变量,而x来自于全局变量 return y return inner() print(outer())
咱们经过在函数内部使用global关键字来声明一个变量不是局部变量,而是一个全局变量。ui
def outer(): def inner(): global x # 在函数内部声明一个全局变量,全局不存在时新建全局变量x,全局变量x存在时,则使用全局变量x x = 10 # 修改全局变量x的值 inner() outer() print(x)
虽然全局变量x,在全局没有被定义,可是因为在函数内部使用了global关键字,因此x就变成了全局变量了。使用了global关键字,那么以前的例子就能够进行以下修改了指针
x = 1 def outer(): def inner(): global x # 使用全局变量x x += 1 # 这里的x是全局变量,那么对x的修改必然会做用域全局 return x inner() outer() print(x) # 2 , 在函数内部把全局变量x给修改了!!!
针对global的总结:code
不要使用global
。 在不少编程语言中都存在闭包的概念,那什么是闭包呢?闭包其实就是一个概念,出如今嵌套函数中,指的是:内层函数引用到了外层函数的自由变量
,就造成了闭包
自由变量:未在本地做用域中定义的变量,好比在嵌套函数的外层定义的变量(非全局变量),对内层来讲,这个变量就叫作自由变量。
def outer(): c = [1] def inner(): c[0] = 1 return c return inner() a = outer() print(a)
注意:上面这个例子比较特殊,首先它是一个闭包,在inner函数内引用了外层函数的自由变量C。由于这里的c是一个引用类型,咱们能够直接经过c来操做c中的元素,可是没办法对c自己进行修改,即c += [1,3]
。看似是列表拼接,可是它会从新对c进行声明,这就引起了以前的问题,内部函数inner没有定义c,因此会报错!因此当c不是引用类型的话,咱们就没办法修改了吗?固然不是,可使用global把c声明为全局变量,可是这就不是闭包了,因此这里就须要使用nonlocal
了(python 3 特有)。
疑问?咱们都说函数执行完毕后,函数的内部变量将会被回收,这里的outer执行完毕后,那么变量c应该会被回收啊,为何还能被内层的inner找到呢?这是由于在定义阶段,解释器解释到inner函数时,因为函数是做为一个总体被解析的,因此解释器知道在inner内部引用了外部的变量,因此在执行函数outer时,并不会回收已被内部函数inner引用的自由变量c。
使用了nonlocal关键字,将变量标记为不在本地做用域定义,而在上一级局部做用域中定义,但不能是全局做用域中定义。
nonlocal只能用在嵌套函数的内部
def outer(): c = 100 def inner(): nonlocal c # 声明不是本地的c(引用上级目录的c) c += 200 # 对c进行修改 return c print('内',c) # 100 c = 1000 return inner a = outer() print('外',a) # 1200
在Python中,一切皆对象,函数也不列外,当咱们给函数定义默认值时,Python会把它存放在函数的属性中,这个属性值就伴随这个函数对象的整个生命周期。
foo.__defaults__属性查看函数的默认值属性
In [77]: import random ...: ...: def add(x=set(),y=[]): ...: x.add(random.randint(1,10)) ...: y.append(1) ...: # print(x) ...: # print(y) ...: ...: print(add(),id(add)) ...: print(add.__defaults__) ...: ...: print(add(),id(add)) ...: print(add.__defaults__) None 2721081985904 ({1}, [1]) None 2721081985904 ({1, 10}, [1, 1])
仔细查看输出结果,发现函数地址没有变,也就是说函数这个对象没有变,可是咱们发现每次它的__default__属性都会发生变化,这是为何呢?这是由于sed和list的默认值都是引用类型,它们引用的都是函数在定义时定义的默认值中。 虽然函数执行完就释放了内存空间,也是因为引用类型,指向默认空间的指针没了,可是已经在调用时改变了默认值空间的对象中的元素,因此在下一次再次调用时此时默认值空间的元素已经被改变了。因此当函数的默认值为引用类型时,这点要特别的注意了
解决办法:
变量的解析原则,也能够理解为变量的查找顺序:
Local
): 本地做用域、局部做用域的local命名空间。函数调用是建立,调用结束消亡Enclosing
): Python 2.2时引入嵌套函数,实现了闭包,这个就是嵌套函数的外部函数的命名空间Global
): 全局做用域,即一个模块的命名空间。模块被import时建立,解释器退出时消亡B(Build-in
): 内置模块的命名空间,生命周期从Python解释器启动时建立到解释器退出时消亡。例如print函数、open函数等。
变量查找的规则为 L > E > G > B,即:先本地后嵌套再全局最后是内置函数中
全局函数:
局部函数: