Python函数的返回值和作用域 函数返回值有什么用( 二 )


# python 2 实现闭包def counter():c = [0]def inc():c[0] += 1# 是赋值即定义嘛?不是!是修改值return c[0]return inc# 返回标识符,即函数对象m = counter()m()# 调用函数 inc(),但是 c 消亡了嘛?没有,内层函数没有消亡,c 不消亡(闭包)m()m()print(m())
# 不推荐使用 global !def counter():global cc = 0def inc():global cc += 1# 不是闭包!return creturn incm = counter()m()m()m()print(m())
# 推荐使用 nonlocal,python 3 实现闭包def counter():c = 0def inc():nonlocal c# 非当前函数的本地变量,当前函数之外的任意层函数的变量,绝非 globalc += 1# 是闭包吗?是!return creturn incm = counter()m()m()m()print(m())
nonlocal 语句:将变量标记为不在本地作用域定义,而是 在上级的某一级局部作用域 中定义,但 不能是全局作用域中。
8、默认值的作用域def foo(x=1):x += 1print(x)foo()# 2foo()# 2def bar(x=[]):# x = [],引用类型x.append(1)# [1]print(x)bar()# [1]bar()# [1, 1]
为什么上列 bar 函数第二次调用打印的是 [1, 1]?
因为函数也是对象,每个函数定义被执行后,就生成了一个函数对象和函数名这个标识符关联 。
python 把函数的默认值放在了函数对象的属性中,这个属性就伴随着这个函数对象的整个生命周期 。
# 查看 foo.__defaults__ 属性,它是个元组def bar(x=[]):x.append(1)print(x)print(bar.__defaults__)bar()# [1]print(bar.__defaults__)bar()# [1, 1]print(bar.__defaults__)# 执行结果:([],)[1]([1],)[1, 1]([1, 1],)# 元组不变,记录的是地址,引用类型变化
def foo(x, m=123, n='abc'):m=456n='def'print(x)print(foo.__defaults__)# (123, 'abc')foo('yang')print(foo.__defaults__)# (123, 'abc')
def foo(x, m=123, *, n='abc', t=[1,2]):m=456n='def't.append(12)#t[:].append(12)# t[:],全新复制一个列表,避免引用计数print(x, m, n, t)print(foo.__defaults__, foo.__kwdefaults__) #(123,) {'n': 'abc', 't': [1, 2]}foo('yang')print(foo.__defaults__, foo.__kwdefaults__) #(123,) {'n': 'abc', 't': [1, 2, 12]}
def x(a=[]):a = a + [5]# 加法的本质:返回新列表、新地址;赋值即定义print(x.__defaults__)# ([],)x()x()print(x.__defaults__)# ([],)def y(a=[]):a += [5]# += 即 extend => a.extend([5])print(y.__defaults__)# ([],)y()y()print(y.__defaults__)# ([5, 5],)# 列表的 + 和 += 的区别:# + 表示两个列表合并并返回一个全新的列表 。# += 表示,就地修改前一个列表,在其后追加后一个列表,就是 extend 方法 。# 例:l1 = [1, 2]l2 = [3, 4]l3 = l1 + l2print(id(l1), id(l2), id(l3))l3 += l2# 就地修改print(id(l3))执行结果:2279905055304 2279905055816 22799063342162279906334216
9、变量名解析原则 LEGB**Local, 本地作用域 、局部作用域的 local 命名空间,函数调用时创建,调用结束消亡 。
Enclosing,Python2.2 时引入了 嵌套函数,实现了闭包,这个就是 嵌套函数的外部函数的命名空间。
Global, 全局作用域 ,即一个模块的命名空间 。模块被 import 时创建, 解释器退出时消亡 。
Build-in, 内置模块的命名空间 ,生命周期从 python 解释器启动时创建到解释器退出时消亡 。例如 print(open),print 和 open 都是内置的变量 。
所以一个名词的查找顺序就是 LEGB

Python函数的返回值和作用域 函数返回值有什么用

文章插图
10、函数的销毁定义一个函数就是生成一个函数对象,函数名指向的就是函数对象 。可以使用del语句删除函数,使其引用计数减 1 。可以使用同名标识符覆盖原有定义,本质上也是使其引用计数减 1 。Python程序结束时,所有对象销毁 。函数也是对象,也不例外,是否销毁,还是看引用计数是否减为 0 。