函数返回值
- python函数使用return语句返回"返回值"
- 所有函数都有返回值,如果没有return语句,隐式调用return None
- 一个函数可以存在多条return语句,但是只有一条可以执行,没有则执行return None
- 有时也可以显示调用return None 作为提醒
- 如果执行了return语句,当前return语句之后的其他语句就不会被执行
- 返回值的作用: 结束函数调用,返回返回值
def showvalues():
return 3,4,5
showvalues()
返回(3,4,5)
- 函数的定义,要求返回值只能是一个值
- 隐式的被python封装成 一个 元组
函数的作用域
作用域
-
定义:一个标识符的可见范围.一般常说的是变量的作用域
-
函数是一个封装,每一个函数都会开辟一个作用域,函数中的变量会被限制在这个作用域中,函数外部对函数内的变量不可见.
-
即 内部对外部不可见
作用域的分类
- 全局作用域
- 在整个程序的运行过程中都可见
- 全局作用域的变量称为全局变量
- 局部作用域
- 在函数,类内部可见
- 局部作用域中的变量称为局部变量,其范围不能超过其所在局部作用域
# 局部变量
def fn1():
x = 1 # 局部作用域,x为局部变量,适用范围在fn1内
def fn2():
print(x) #x 不能打印,因为局部变量对外部不可见
print(x)# 也不能打印, 因为x 为局部变量,在fn1中可见
#全局变量
x = 5 #全局变量,在函数外定义
def foo():
print(x) # x可以打印,因为x为全局变量,对函数内可见.
foo()
一般来讲,外部作用域的变量对函数内部可见,可以使用
反过来,函数内部的局部变量,不能在函数外部看到
函数的嵌套
在一个函数中定义另一个函数
def outer():
def inner():
print(inner)
print(outer)
inner()
outer() ##可以使用
inner() ## 不可以使用
内部函数inner 不能被外面直接使用,会抛出NameError异常,因为他在函数内部,对函数外部不可见
其实inner 不过是一个标识符,就是函数outer内部定义的一个变量而已
嵌套函数的作用域
def outer1():
o = 65 ## == 0x41 A
def inner():
o = 97 ## 0x61 a
print("inner {}".format(o)) ##使用的内部o= 97 inner 97 a
print(chr(o))
inner()
print("outer{}".format(o)) ##外部的o不受局部的o的影响, outer 65
outer1()
执行结果为 inner 97 a
outer65
- 外层变量在内部作用域可见
- 相当于内部变量在当前的inner函数中重新定义了一个新的变量o,但是这个o并不会覆盖,也不会影响到外层函数中的变量o,对于inner函数来说,只能可见自己作用域中的定义的变量o
- 总结
- 优先用内部的变量,记住赋值即定义
- 只要函数中出现了赋值语句,则这个被复制的变量的作用域就已经生成,在键入函数时就会被作用
x = 5
def foo():
x += 1 ##会报错
foo()
-
错误原因:
因为x += 1 就相当于x = x + 1 ,虽然全局变量x能够对foo可见,但是赋值既定义,从x被赋值开始,局部 变量x的作用域就已经生成,foo函数内部只被局部变量x影响, 而此时执行过程中找那个,x还未被定义所 以会找不到x -
老师的回答:
相当于在foo内部定义了一个局部变量x,那么foo内部所有的x都是这个局部变量x了
x = x + 1 相当于使用了局部变量x ,但是这个x还没有完成赋值,就被右边拿来和加1操作了
global
x = 5
def shownext():
global x #声明全局变量x
x += 5 #此时的x已经被声明为全局变量
return(x) #返回的x是全局变量x ,全局变量x的值 已经改变
shownext()
返回 10
- 使用gloal关键字的变量,将foo内的x声明为使用外部的全局作用域中定义的x
- 全局作用域中必须有x,如果没有,则会报错nameerror
- 总结 : x +=1 是先引用后赋值,而python动态语言是赋值才算定义,才能用,
- 解决办法
1.在这条语句之前增加x = 0之类赋值语句,或者使用global,声明不在内部作用域,去使用全局作用域
2.查找变量定义内部作用域使用x = 10之类的赋值语句会重新定义局部作用与的变量x,但是,一旦这个作用域中使用glabal声明x为全局的,那么x += 5相当于对全局作用域的变量x赋值 - global使用原则
- 外部作用域的变量会在内部作用域可见,但也不要在这个内部的局部作用给予中直接使用,因为函数的目的就是封装,尽量与外界隔离
- 如果函数需要使用外部全局变量,请尽量使用函数的形参定义,并在调用传实参解决
闭包
-
自由变量 :未在本地作用域中定义的变量.例如定义在内层函数外的外层函数中的作用域中的变量
-
闭包 : 就是一个概念,出现在嵌套函数中,指的是内层函数引用到了外层函数的自由变量,就形成了闭包.很多语言都有这个概念,最收悉的就是JavaScript
def counter (): #1定义函数,直接跳过
c = [0] #2 c={[0]
def inc(): #1同上 #2 内部的定义并没执行 过
c[0] += 1
return c[0]
return inc #2 counter的返回值是inc函数体
foo = counter() # 1函数counter的返回值给foo赋值,这时回看counter函数 .2 foo现在是函数体inc
print(foo(),foo()) #1 此时打印的是每次调用函数inc的返回值 第一次1,第二次2
c = 100 #1 此时的c是全局变量c 而不是counter内部的c 局部变量c不会变
print(foo()) # 再次打印调用函数inc的返回值
代码分析:
- foo 会执行counter函数并返回inc对应的函数对象,注意这个函数对象并不释放,因为有foo记着
- c[0] += 1不会报错,c已经在counter函数中定义过了,而且inc中的使用方式是为c的元素修改至,而不是重新定义c的变量
- 最后一行打印3
因为c和counter中的c不一样,而inc引用的是自由变量正是counter中的变量c
nonlocal
- 定义: 将变量标记为不在本地作用域定义,而是在上一级的某一集局部作用域中定义,但不能是全局作用域中的定义
def counter():
count = 0
def inc():
nonlocal count #声明变量count不是本局变量,就会往上级非全局变量寻找变量count
count += 1
return count
return inc
foo = counter() # foo指向函数对象inc
print(foo(),foo())
- count 是外层函数的局部变量,被内层函数引用
- 内层函数使用nonlocal关键字声明count变量在上级的作用域而非本地作用域中的定义
- 代码中内层函数引用外部局部作用域中的自由变量,形成闭包
- 当在最外层局部作用域中使用nonlocal时,其无法引用全局变量,会报错
默认值的作用域
def foo (xyz=1):
print(xyz)
foo() #没有传参,则缺省值代替 xyz=1
foo() #同样
print(xyz) #xyz是foo函数的形参,作用域是函数体内
##输出 1 1 打印时会报错
def foo (xyz=[]):
xyz.append(1)
print(xyz)
foo() # [1] xyz是引用类型,引用xyz这个列表,函数体操作是对列表进行操作
foo() # [1,1]
print(xyz) #报错 xyz不在作用域内
- 为什么第二次调用foo函数打印的是[1,1]?
1.因为函数也是对象,每个函数定义被执行后,就会生成一个函数对象和函数名这个标志符关联
2.python把函数的默认值放在了函数对象的属性中,这个属性就伴随这个函数对象的整个生命周期 - __ defaults__
- 查看foo.__defaults_属性,他是个元组
- 函数地址没有变就说明foo这个函数对象没有变过,调用它,他的属性__defaults__中使用元祖保存默认值
- xyz默认值是引用类型,引用类型的元素变动,并不是元组的变化
__defaults __
def foo (xyz=[], m=123,n='abc'):
m = 456
n = 'def'
print(xyz)
print(foo.__defaults__) # ([],123,'abc')
foo('magedu') # magedu
print(foo.__defaults__) #([],123,'abc')
- 属性 __ defaults __ 中使用 元组保存所有位置参数的默认值,
- 他不会因为在函数体内改变了局部变量(形参)的值而发生改变
#keyword-only参数的缺省值
def foo (xyz, m=123, *, n='abc', t=[1,2]):
m = 456
n = 'def'
t.append(300)
print(xyz,m,n,t)
print(foo.__defaults__,foo.__kwdefaults__) # (123){'n':'abc' , 't':[1,2]}
foo('magedu') # magedu,456,'def',[1,2,300]
print(foo.__defaults__,foo.__kwdefaults__) # (123){'n':'abc' , 't':[1,2,300]}
- 属性__defaults__中使用元组保存所有位置参数的默认值
- 属性__kwdefaults__中使用字典保存所有keyword-only参数的默认值