函数的嵌套
函数嵌套就是在一个函数中定义了另外的一个函数。
def outer(): def inner(): print("inner") print("oouter") inner() outer() 结果为: oouter inner inner() 结果为: NameError: name 'inner' is not defined
由上面的例子可以看到,直接在外面调用inner函数,会抛一个异常,因为内部函数在外面不可见。这就是作用域的概念。
Python解释器在遇到函数的时候,首先是先在内存中开辟一个内存空间,这个时候只是象征性的将函数名读入内存中,表示这个函数已经存在了,也就是Python解释器遇到一个变量的时候,把变量名和值之间的对应关系记录下来。
等到函数调用的时候,这个时候解释器才会再开辟一个内存空间,用来存储这个函数里面的内容,函数中的变量会存储在这个新开辟出来的内存中,函数中的变量只在函数的内部使用,而且随着函数的执行完毕,这个内存空间中的所有内容就会被清空。
命名空间和作用域
我们给这个“存放名字与值的关系”的空间起了一个名字——叫做命名空间,代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间,在函数的运行中开辟的临时的空间叫做局部命名空间。
命名空间的本质:存放名字与值的绑定关系
命名空间一般有三种:
- 全局命名空间
- 局部命名空间
- 内置命名空间
内置命名空间也就是Python为我们提供的名字,诸如print,str等。
三种命名空间的加载顺序为:内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)
而取值顺序为:局部命名空间->全局命名空间->内置命名空间
x = 1 def f(x): print(x) f(10) print(x) 结果为: 10 1
作用域
作用域就是一个标识符的可见范围,这就是标识符的作用域。一般常说的是变量的作用域。作用域分为全局作用域和局部作用域。
全局作用域包括内置名称空间和全局名称空间,它再整个文件的任意位置都能被引用,全局有效。
而局部作用域包含局部名称空间,它只在局部范围内有效。
x = 5 def foo(): print(x) foo() 5 x = 5 def foo(): x+=1 print(x) foo() UnboundLocalError: local variable 'x' referenced before assignment
外层作用域在内层可见,但是内层作用域中,如果定义了一个相同的变量,相当于当前作用域中重新定义了一个新的变量,但是这个o并没有覆盖外层作用域outer中的变量。
def outer1(): g = 65 def inner(): print("inner{}".format(g)) print(chr(g)) print("outer{}".format(g)) inner() outer1() 结果为: outer65 inner65 A def outer2(): k = 65 def inner(): k = 97 print("inner{}".format(k)) print(chr(k)) print("outer{}".format(k)) inner() outer2() outer65 inner97 a
全局变量global
使用global关键字的变量,将函数内的变量声明为使用外部的全局作用域中定义的变量。
a = 10 def func(): global a a = 20 print(a) func() print(a) 结果为: 10 20
一旦作用域中使用global声明变量为全局的,那么这个变量相当于在为全局作用域的变量赋值。函数的目的是为了封装,所以应尽量不使用global。就算要用外部变量,也最好使用形参传参来解决。
globals()和locals()
def func(): a = 12 b = 20 print(locals()) print(globals()) func() 结果为: {'a': 12, 'b': 20} {'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': <module 'builtins' (built-in)>, '__builtins__': <module 'builtins' (built-in)>, '_ih': ['', 'def fn(*, x,y):\n print(x,y)\n fn(x=5,y=6)', 'a = 10\ndef func():\n global a\n a = 20\n\nprint(a)\nfunc()\nprint(a', 'a = 10\ndef func():\n global a\n a = 20\n\nprint(a)\nfunc()\nprint(a)', 'a = 10\ndef func():\n global a\n a = 20\n\nprint(a)\nfunc()\nprint(a)', 'def func():\n a = 12\n b = 20\n print(locals())\n print(globals())\n\nfunc()'], '_oh': {}, '_dh': ['F:\\xpccode'], 'In': ['', 'def fn(*, x,y):\n print(x,y)\n fn(x=5,y=6)', 'a = 10\ndef func():\n global a\n a = 20\n\nprint(a)\nfunc()\nprint(a', 'a = 10\ndef func():\n global a\n a = 20\n\nprint(a)\nfunc()\nprint(a)', 'a = 10\ndef func():\n global a\n a = 20\n\nprint(a)\nfunc()\nprint(a)', 'def func():\n a = 12\n b = 20\n print(locals())\n print(globals())\n\nfunc()'], 'Out': {}, 'get_ipython': <bound method InteractiveShell.get_ipython of <ipykernel.zmqshell.ZMQInteractiveShell object at 0x00000000038F0B38>>, 'exit': <IPython.core.autocall.ZMQExitAutocall object at 0x00000000058B7A90>, 'quit': <IPython.core.autocall.ZMQExitAutocall object at 0x00000000058B7A90>, '_': '', '__': '', '___': '', '_i': 'a = 10\ndef func():\n global a\n a = 20\n\nprint(a)\nfunc()\nprint(a)', '_ii': 'a = 10\ndef func():\n global a\n a = 20\n\nprint(a)\nfunc()\nprint(a)', '_iii': 'a = 10\ndef func():\n global a\n a = 20\n\nprint(a)\nfunc()\nprint(a', '_i1': 'def fn(*, x,y):\n print(x,y)\n fn(x=5,y=6)', 'fn': <function fn at 0x0000000005888510>, '_i2': 'a = 10\ndef func():\n global a\n a = 20\n\nprint(a)\nfunc()\nprint(a', '_i3': 'a = 10\ndef func():\n global a\n a = 20\n\nprint(a)\nfunc()\nprint(a)', 'a': 20, 'func': <function func at 0x0000000005DB3620>, '_i4': 'a = 10\ndef func():\n global a\n a = 20\n\nprint(a)\nfunc()\nprint(a)', '_i5': 'def func():\n a = 12\n b = 20\n print(locals())\n print(globals())\n\nfunc()'}
nonlocal关键字
使用nonlocal关键字,将变量标记为不在本地作用域定义,而在上级的某一级局部作用域中定义,但不能是全局作用域中定义。
def counter(): count = 0 def inc(): nonlocal count count +=1 return count return inc foo = counter() print(foo()) print(foo()) print(foo()) 结果为: 1 2 3 a = 50 def counter(): nonlocal a a+=1 print(a) count = 0 def inc(): nonlocal count count+=1 return count return inc foo = counter() print(foo()) print(foo()) 结果为: SyntaxError: no binding for nonlocal 'a' found
闭包
闭包是一个概念,出现在嵌套函数中,指的是内层函数引用到了外层函数的*变量,而非全剧作用域名字的引用。
def counter(): c = [0] def inc(): c[0]+=1 return c[0] return inc foo = counter() print(foo(),foo()) c = 100 print(foo()) 结果为 1 2 3
判断一个函数是不是闭包函数,可以使用方法__closure__。
#输出的__closure__有cell元素 :是闭包函数 def func(): name = 'eva' def inner(): print(name) print(inner.__closure__) return inner f = func() f() 结果为: (<cell at 0x0000000005DC3408: str object at 0x0000000005DA1DF8>,) eva #输出的__closure__为None :不是闭包函数 name = 'egon' def func2(): def inner(): print(name) print(inner.__closure__) return inner f2 = func2() f2() 结果为: None egon
默认值的作用域
def foo(xyz = 1): print(xyz) print(foo()) print(foo()) print(xyz) 结果为: 1 None 1 None --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-26-80bc3f8dd90b> in <module> 3 print(foo()) 4 print(foo()) ----> 5 print(xyz) NameError: name 'xyz' is not defined def foo(xyz=[]): xyz.append(1) print(xyz) foo() foo() print(xyz) 结果为: [1] [1,1] NameError: name 'xyz' is not defined
上面的第二个例子为什么是【1,1】,因为函数也是对象,python把函数的默认值放在了属性中,这个属性就伴随着这个函数对象的整个生命周期 。查看foo.__defaults__属性。
def foo(xyz=[], u='abc', z=123): xyz.append(1) return xyz print(foo(), id(foo)) print(foo.__defaults__) print(foo(), id(foo)) print(foo.__defaults__) 结果为 : [1] 98987816 ([1], 'abc', 123) [1, 1] 98987816 ([1, 1], 'abc', 123)
上面的例子中,函数地址并没有变,就是说函数这个对象的没有变,调用它,它的属性__defaults__中使用元组保存默认值 ,xyz默认值是引用类型,引用类型的元素变动,并不是元组的变化。
def foo(w, u='abc', z=123): u = 'xyz' z = 789 print(w, u, z) print(foo.__defaults__) foo('magedu') print(foo.__defaults__) 结果为: ('abc', 123) magedu xyz 789 ('abc', 123)
属性__defaults__中使用元组保存所有位置参数默认值,它不会因为在函数体内使用了它而发生改变 。
def fo(w, u='abc', *, z=123, zz=[456]): u = 'xyz' z = 789 zz.append(1) print(w, u, z, zz) print(fo.__defaults__) fo('magedu') print(fo.__kwdefaults__) ('abc',) magedu xyz 789 [456, 1] {'z': 123, 'zz': [456, 1]}
属性__defaults__中使用元组保存所有位置参数默认值 ,属性__kwdefaults__中使用字典保存所有keyword-only参数的默认值 。
使用可变类型作为默认值,就可能修改这个默认值,有时候这个特性是好的,有的时候这种特性是不好的,有副作用。
#使用影子拷贝创建一个新的对象,永远不能改变传入的参数
def foo(xyz=[], u='abc', z=123): xyz = xyz[:] # 影子拷贝 xyz.append(1) print(xyz) foo() print(foo.__defaults__) foo() print(foo.__defaults__) foo([10]) print(foo.__defaults__) foo([10,5]) print(foo.__defaults__) 结果为: [1] ([], 'abc', 123) [1] ([], 'abc', 123) [10, 1] ([], 'abc', 123) [10, 5, 1] ([], 'abc', 123)
def foo(xyz=None, u='abc', z=123): if xyz is None: xyz = [] xyz.append(1) print(xyz) foo() print(foo.__defaults__) foo() print(foo.__defaults__) foo([10]) print(foo.__defaults__) foo([10,5]) print(foo.__defaults__) [1] (None, 'abc', 123) [1] (None, 'abc', 123) [10, 1] (None, 'abc', 123) [10, 5, 1] (None, 'abc', 123)
通过值的判断就可以灵活的选择创建或者修改传入对象,这种方式灵活,应用广泛 ,很多函数的定义,都可以看到使用None这个不可变的值作为默认参数,可以说这是一种惯用法 。
函数名的本质
- 可以被引用。
-
可以被当作容器类型的元素
- 可以当作函数的参数和返回值。
#被引用 def func(): print('in func') f = func print(f) 结果为: <function func at 0x00000000062DEF28> #被当做容器类型的元素 def f1(): print('f1') def f2(): print('f2') def f3(): print('f3') l = [f1,f2,f3] d = {'f1':f1,'f2':f2,'f3':f3} #调用 l[0]() d['f2']() 结果为: f1 f2
函数的销毁
全局函数销毁
重新定义同名函数 ,del 语句删除函数对象 ,程序结束时。
def foo(xyz=[], u='abc', z=123): xyz.append(1) return xyz print(foo(), id(foo), foo.__defaults__) def foo(xyz=[], u='abc', z=123): xyz.append(1) return xyz print(foo(), id(foo), foo.__defaults__) del foo print(foo(), id(foo), foo.__defaults__) 结果为: [1] 103673096 ([1], 'abc', 123) [1] 103671872 ([1], 'abc', 123) --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-58-4163eb30b66f> in <module> 10 print(foo(), id(foo), foo.__defaults__) 11 del foo ---> 12 print(foo(), id(foo), foo.__defaults__) NameError: name 'foo' is not defined
局部函数销毁
重新在上级作用域定义同名函数,el 语句删除函数名称,函数对象的引用计数减1 ,上级作用域销毁时。
def foo(xyz=[], u='abc', z=123): xyz.append(1) def inner(a=10): pass print(inner) def inner(a=100): print(xyz) print(inner) return inner bar = foo() print(id(foo),id(bar), foo.__defaults__, bar.__defaults__) del bar print(id(foo),id(bar), foo.__defaults__, bar.__defaults__) 结果为: <function foo.<locals>.inner at 0x00000000062DEBF8> <function foo.<locals>.inner at 0x00000000062DED08> 103671872 103673096 ([1], 'abc', 123) (100,) --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-60-ad4eb89d71c6> in <module> 11 print(id(foo),id(bar), foo.__defaults__, bar.__defaults__) 12 del bar ---> 13 print(id(foo),id(bar), foo.__defaults__, bar.__defaults__) NameError: name 'bar' is not defined