函数返回值;

python函数 使用 return 返回语句 来 返回 ‘返回值’

所有函数都有返回值,但是返回值可根据需求来判断是否需要 返回,没有return语句,会隐式返回 return None

一个函数可以 写入多个 return语句,但是只能有一个会被执行,且执行过后,函数调用完毕,会跳出函数,若有返回值则返回,每返回值,则隐式返回None

return None 可以缩写为return 都是返回空,所以可以不写

return 和 break一样,可以终止当前函数调用,并返回值

在判断条件下 ,return 语句不一定是最后一句(执行判断条件未满足,会继续向后执行其他条件)

 

返回多个值

def showlist():

     return [1,3,5]

showlist() 会返回 [1,3,5]这样的一个列表

 

def showlist():

     return 1,3,5

showlist() 会返回一个(1,3,5)这样的一个元组

 

返回多个值

函数不能返回多个值

return[1,3,5]是指明返回一个列表, 是 一个 列表 对象

return 1,3,5 从外观上看起来返回单值, 实际上python将它们隐式封装未了一个元组

def showlist():

     return 1,3,5

x,y,z = showlist() ####使用解构提取更为方便,亦6可搭配*取固定值

 

函数嵌套

函数没有加( )只是一个函数标识符,调用函数必须加上()

(1-1),在一个函数中嵌套一个函数

def outer():

     def inner():

          print('inner')

     inner()

     print('outer')

使用outer()进行函数调用;

会打印出 inner outer

 

因为使用 函数调用时,函数会从上到下依次执行,所以会先执行本地模块中的inner( )函数,因为inner( )函数模块中含有一个打印条件,所以会打印inner,在执行完inner()函数后会继续向下执行 print(outer),所以会在 inner下打印outer ,并向下执行,若所有函数块都执行完毕后,没有return返回语句,会隐式的返回一个None值,若想得到这个值,可以使用return返回他,一般自定义函数知道其中的结果,所以会省略return来减少代码量

 

函数有可见范围,称之为函数的作用域的概念:

在(1,1)中,

inner是outer的内建函数,只能在outer中运行,在outer外,inner函数没有定义,所以他的作用域只在outer中,内部函数不能被外部函数直接使用,否则会返回 Nameerror异常,

 

举例(1,2)

def outer():

     out=123

     print('outer')

outer()

print(out)

执行这个函数就会打印出'outer'并会打印错误项(Nameerror),outer()函数没有 出错,而打印out 出错,因为out在outer内部定义,而内部变量对外部不可见,所以会报出名称错误

 

作用域

一个函数的可见范围,就是标识符的作用域,一般常说的是变量的作用域

 

举例

( 2,1 ):::

 

x=5                    x=5

def foo():          def foo():

     print(x)               x+=1

                               print(x)

 

 

foo()                 foo()

 

在上面两组对比代码中:

左边代码调用后会 打印出x的值,也就是5

右侧代码分析:

x还没有完成赋值就被右边拿来操作加1(赋值即定义)

右边代码调用后会提示未绑定本地变量的错误,因为在其代码中,x没有被赋值,为什么不会取使用外部的全局变量呢?虽然内部函数可以调用外部函数,但是函数调用在执行时会优先使用本地变量,而执行本地变量会发现, x=x+1, x 被 -->x+1 赋值了,但是没有指出x的值,所以就会提示x的本地变量未绑定错误

 

 

全局作用域:

     在整个程序运行环境中都可见

 

局部作用域:

     在函数、类等内部可见

 

不能超过其所在的局部作用域中(也就是说外部无法调用)

 

变量作用域示例2:

(2,2,1)

def outer1():                                             def outer2():

     o =65                                                        o=65

     def inner():                                                 def inner():

           print('inner {}'.format(o))                             o=97

           print(chr(o))                                                print('inner {}'.format(o))

     print('outer {}'.format(o)                                    print(chr(o))

     inner()                                                        print('outer {}'.format(o))

                                                                       inner()   

 

左右两边代码在调用过后的区别为

 

outer():                                                     outer():

outer 65                                                   outer 65

inner 65                                                    inner 97

A                                                              a 

之前已经说过,函数执行时会优先执行内部的变量,若内部没有变量,则会去外部查找变量

 

如何利用global解决变量作用域的问题(只需了解,使用需考虑后果,最好不使用)

 

全局变量global:

x=5

def foo():

     global x

     x+=1

 

使用了 global关键字 的 变量 ,会声明 关键字的变量来自于全局变量中

全局作用域中必须有x的定义,否则会Nameerror

使用了    global  关键字 的变量的值,会被函数内相关的执行代码块改变

x=5

def foo():

     global x

     x = 10

     x+=1

     print(x)

print(x)

使用全局变量时应在pychram中运行,jupyter和ipython中,有可能已经定义过x的变量,所以会干扰

在外部定义一个变量为x ,内部将 x的变量  利用global x(声明变量作用域) 之后 对其进修修改,所做的修改也是全局性的,所以也会修改外部的x的变量值

 

global总结:

global的作用域 只在当前声明的作用域与最外层作用域中有效,

x+=1  先引用后赋值,python动态语言时赋值才算定义,所以若没有声明变量作用域,或提前定义变量,就一定会报错

内部作用域使用x=5之类的赋值语句会重新定义局部作用域使用的变量x,但是,一旦这个作用域中使用 global声明x是全局的,那么x=5 就相当于是在为全局作用域的变量从新定义

 

global使用原则:

外部作用域变量会对内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是为了封装,尽量与外部隔离

如果函数需要使用外部全局变量,请使用函数的形参解决

global只需要了解其作用,生产中尽量不要使用这个参数

 

闭包:

自由变量 未在本地作用域中定义的变量 ----->定义在内层函数外的外层函数的作用域中的变量

闭包: 出现在嵌套函数中,内层函数应用到了外层函数的自由变量,就形成了闭包(闭包不会改变上层的值,可以利用他的值引用在下层嵌套中)

举例:

(2,3)

def counter():

     c=[0]

     def inc():

          c[0]+=1      ???会报错吗?

          return c[0]

      return inc

foo=counter( )          此时foo就是founter执行完毕后的inner函数对象       

c=100

foo()                     就是函数对象加上括号,所以foo( ) 就是内部函数inner()的调用

(2,3)分析:

在上列代码中 c[0] 若没有被下面的代码或进程所引用到,则称其为自由变量;反之则称其为 闭包,闭包只会出现在嵌套函数中

c[0]+=1 没有报错! c已经在 counter中定义过了,而且inc中的使用方式是为c的元素修改值,而不是重新定义变量

c=100 但是最后输出的结果却跟100没有关系的原因是,这个c实在外部定义的,并没有对函数内的c做出改变

 

使用nonlocal语句改变 变量的作用域范围

将变量标记在上级的局部作用域中定义,但不能是全局作用域中定义

例子(3,1)

def counter():                                             a=50

     count=0                                                def counter( ):                           

     def inc():                                                      nonlocal a

          nonlocal count                                        a+=1

          count+=1                                                print(a)

          return count                                            count=0                

     return inc                                                      def inc( ):

                                                                                nonlocal count

                                                                                count+=1

                                                                                return count

                                                                           return count

 例子(3,1)分析:

count是外层函数的局部变量,被内部函数调用

内部函数使用nonlocal关键字声明count变量在上一级作用域中

左边代码可以正常是以使用,且形成闭包

右边代码不能正常运行,变量a不能在全局作用域中

 

右边代码在ipython环境中只能敲到第三行就会被提示nonlocal使用错误的方法,因为他的作用域改变到全局作用域去了

 

 

默认值的作用域:

例子(3,2)

def foo(xyz=[]):

     xyz.append(1)

     print(xyz)

foo( ) -->[1]

foo( ) -->[1,1]

print(xyz) -->NameError 当前作用域没有xyz变量

 

例子(3,2)分析:

第二次调用函数 打印的是[1,1]的原因是,函数也是对象,python把函数的默认值放在了属性中,这个属性就伴随着这个函数对象的整个生命周期

 

怎么查看一个函数默认属性

使用 foo.__defaults__  查看某个函数对象的默认属性

 

例子(3,3):

def foo(xyz=[],u='abc',z=[]):

     xyz.append(1)

     return xyz

print(foo(),id(foo))

print(foo.__defaults__)

print(foo(),id(foo))

 print(foo.__defaults__)

输出结果为:

[1] 1404757266776

([1], 'abc', [])

[1, 1] 1404757266776

([1, 1], 'abc', [])

例子(3,3)分析:

函数地址没有发生改变,证明函数这个对象没有变,调用它,他的属性__defaults__中使用元组保存所有默认值

原值是不可变变的,可以用来存储默认值,但是xyz的默认值是引用类型,引用类型的元素变动,而不是元组的变化

 

默认值的作用域:

 

非引用类型例子

例子(3,4)

def foo(w,u='abc',z=123):

     print(w,u,z)

     u='xyz'

     z=789

print(w,u,z)

print(foo.__defaults__)

foo('magedu')

print(foo.__defaults__)

 

('abc', 123)

magedu xyz 789

('abc', 123)

 

例子(3,4)分析:

不会因为在函数体内使用了它而发生改变

 

默认值的作用域:

可变类型默认值,如果使用默认值,可能会修改这个默认值;

这个特性具有双面性,有时好,有时坏!

可以按需改变,两种方法

使用切片复制,修改复制的那一份

例子(3,5,1):

def foo(xyz=[],u='abc',z=123):

影子拷贝

     xyz.append(1)

     print(xyz)

foo()

print(foo.__defaults__)

foo([10])

print(foo.__defaults__)

foo([10,5])

print(foo.__defaults__)

[1]

([], 'abc', 123)

[10,1]

([], 'abc', 123)

[10, 5, 1]

([], 'abc', 123)

 

例子(3,5,1)分析:

函数体内,不改变默认值

xyz都是传入参数或者默认参数的副本,如果想修改源参数,无能为力

 

常用方法二:

例子(3,5,2):

def foo(xyz=None,u='abc',z=123):

if xyz is None:

xyz=[]

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)

 

例子(3,5,2)分析:

使用不可变类型默认值

     如果使用缺省值None就创建一个列表

     如果传入一个列表就修改这个列表

 

默认值的作用域:

第一种方法

     使用影子拷贝创建一个新的对象,永远不能改变传入的参数;

第二种方法:

     通过值的判断就可以灵活的选择创建或者修改传入对象

     这种方式灵活,应用广泛

     很多函数的定义,都可以看到使用None这个不可变的值作为默认参数,可以说这是一种惯用法

 

函数的销毁:

全局函数:

例子(3,5,3):

初次定义

def foo(xyz=[],u='abc',z=123):

     xyz.append(1)

     return xyz

print(foo(),id(foo),foo.__defaults__)

 

例子(3,5,4):

覆盖它:

def foo(xyz=[],u='abc',z=123):

     xyz.append(1)

     return xyz

print(foo(),id(foo),foo.__defaults__)

[1] 1404758182088 ([1], 'abc', 123)

[1] 1404758181544 ([1], 'abc', 123)

 

比较(3,5,3)与(3,5,4)之间的区别;

会发现 他们的ID发生了变化;说明已经不是在原来的位置上修改,而是新建的一个位置

这种内存销毁,成为覆盖再启用,

 

直接销毁再启用:

del foo

再id(foo) 就会返回foo没有被定义

 

总结:

全局函数销毁

     重新定义同名函数

     del语句删除函数对象

     程序结束时 

 

局部函数销毁:

     重新再上级作用域定义同名函数

     del语句删除函数对象

     上级作用域销毁时