函数返回值和作用域1、返回值defguess(x):ifx>3:return">3"else:return"<=3"print(guess(10)) 1>Python函数使用return语句返回“返回值” 2>所有函数都有返回值,如果没有return语句,则隐式调用returnNone 3>return语句不一定是函数语句块的最后一条语句 4>一个函数可以有多个返回语句,但只能执行一个。如果没有执行return语句,则隐式调用returnNone 5>如果需要,可以显式调用returnNone,可以简写为return 6>如果函数执行了return语句,函数就会返回,当前执行的return语句之后的其他语句将不会被执行 7>返回值的作用:结束函数调用,返回“返回值”2.一次可以返回多个值吗?defshowvalues():return1,3,5print(showvalues())#return(1,3,5) 函数不能同时返回多个值 return1,3,5好像returnmultiplevalues,被python隐式封装成元组 x,y,z=showvalues()使用解构提取返回值的作用域更方便,这个是标识符的作用域,一般来说变量的范围。x=20deffn():x\=100#x的作用域:当前函数fn()print(x)#x=20 注:每个函数都会开辟一个作用域。3.2作用域分类 全局作用域: 可以看到整个程序运行环境 全局作用域内的变量称为全局变量 局部作用域: 可见于函数、类等内部。局部变量deffn1():x\=1#局部作用域,x是局部变量,使用范围在fn1之内deffn2():print(x)#x可以打印吗?Can'tprint(x)#可以打印x吗?cannotglobalvariablex=5#全局变量,也在函数外定义deffoo():print(x)#可见?可以使用foo() 一般来说,外部作用域变量在函数内部是可见的,可以使用 反之,函数内部的局部变量在函数外部是看不到的4.函数嵌套 在函数中在defouter()中再定义一个函数:definner():print('inner')print('outer')inner()outer()inner()#没有 内部函数inner不能在外面直接使用,将抛出NameError异常,因为它在函数外部不可见。 其实inner只是一个标识符,一个定义在outer函数内部的变量。5.嵌套函数的作用域defouter():o\=65#Localvariables,局部局部变量,临时变量definner():o\=97print('inner',o)print('outer1',o)inner()print('outer2',o)outer()#1:outer1652:inner973:outer265 外部变量在内部作用域中可见。 在内层作用域中,如果定义了与外层作用域相同的变量,相当于在当前函数作用域中重新定义了一个新的变量,并且这个内层变量不能覆盖外层作用域中的变量。6.赋值语句有问题x=100deffn():y\=x+200print(y)fn()x=100deffn():x+=1#错误!赋值即定义,即x=x+1(局部变量=局部变量+1)!print(x)fn()x=100deffn():print(x)#报错!这一步不能执行!x+=1#只要局部变量在这个作用域内被赋值和定义('='),这个作用域内的所有变量都是局部变量!print(x)fn() 能解决吗?是的,使用全局语句x=100deffn():globalx#声明全局变量print(x)#100x+=1print(x)#101fn()print(x)#101 注:全局变量一般情况下不建议修改。一旦在作用域内使用global声明了一个全局变量,就相当于对全局变量进行了赋值和定义。 global使用原则: 1>外部作用域变量在内部作用域内是可见的,但是不要直接在这个内部局部作用域中使用,因为函数的目的是为了对外进行封装和隔离世界尽可能。 2>如果函数需要使用外部全局变量,请尽量使用函数的形参定义,在调用中传递实参即可解决。 3>一句话:不要用global,学习它就是深刻理解变量的作用域。不建议直接传入全局变量!y=\[\]deffoo():#x是一个标识符,一个变量,或者一个局部变量y.append(1)foo()foo()print(y)推荐使用传参的方式并在函数中使用它全局变量y=\[\]deffoo(x):#x是一个标识符,一个变量,一个局部变量x.append(1)foo(y)foo(y)print(y)7、闭包** 自由变量:不在局部作用域定义的变量。例如,在内部函数外部的外部函数范围内定义的变量。 闭包:是出现在嵌套函数中的一个概念,意思是内层函数引用外层函数的自由变量,形成一个闭包。很多语言都有这个概念,最熟悉的就是JavaScript。Python2实现闭包defcounter():c\=\[0\]definc():c\[0\]+=1#赋值是指定义吗?不!就是修改值returnc\[0\]returninc#返回标识符,即函数对象m\=counter()m()#调用函数inc(),但是c死了吗?不行,内函数不死,c不死(闭包)m()m()print(m())不推荐使用global!defcounter():globalcc\=0definc():globalcc+=1#不是闭包!returncreturnincm\=counter()m()m()m()print(m())推荐nonlocal,python3实现闭包defcounter():c\=0definc():nonlocalc#Localvariablesofnon-currentfunction,当前函数之外任意层函数的变量,绝对不是globalc+=1#是闭包吗?是的!returncreturnincm\=counter()m()m()m()print(m()) nonlocal语句:将变量标记为在局部范围内没有定义,但是局部在上层的某个层级级别范围,但不是全局范围。8.默认值的范围deffoo(x=1):x+=1print(x)foo()#2foo()#2defbar(x=\[\]):#x=\[\],引用类型x.append(1)#\[1\]print(x)bar()#\[1\]bar()#\[1,1\] 为什么第二次调用上面的bar函数会打印[1,1]? 因为函数也是对象,每执行一次函数定义后,都会生成一个函数对象,并与函数名的标识相关联。 python把函数的默认值放在了函数对象的属性中,这个属性伴随着函数对象的整个生命周期。查看foo.__defaults__属性,它是一个元组defbar(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\],)#元组不变,记录地址,引用类型改变deffoo(x,m=123,n='abc'):m\=456n\='def'print(x)print(foo.\_\_defaults\_\_)#(123,'abc')foo('yang')print(foo.\_\_defaults\_\_)#(123,'abc')deffoo(x,m=123,\*,n='abc',t=\[1,2\]):m\=456n\='def't.append(12)#t\[:\].append(12)#t\[:\],复制一个新列表以避免引用计数打印(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\]}defx(a=\[\]):a\=a+\[5\]#加法的本质:返回新列表,新地址;赋值是定义print(x.\_\_defaults\_\_)#(\[\],)x()x()打印(x.\_\_defaults\_\_)#(\[\],)defy(a=\[\]):a+=\[5\]#+=即扩展=>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))的执行结果:22799050553042279905055816227990633421622799063342169,变量名解析原理LEGB**Local,局部作用域,局部作用域的局部命名空间,函数调用时创建,调用结束后消失完全的。 封闭,Python2.2引入了嵌套函数来实现闭包。这是嵌套函数的外部函数的命名空间。 Global,全局作用域,即一个模块的命名空间。模块在导入时创建,在解释器退出时销毁。 Build-in,内置模块的命名空间,生命周期在python解释器启动时创建,在解释器退出时消亡。例如print(open),print和open是内置变量。 所以一个名词的查找顺序是LEGB10,函数的销毁 定义一个函数就是生成一个函数对象,函数名指向函数对象。 可以使用del语句删除函数,将其引用计数减1。 可以使用同名标识符覆盖原始定义,本质上是将其引用计数减1。 当Python程序结束,所有对象都被销毁。 函数也是对象,也不例外。是否销毁取决于引用计数是否减为0。
