1、函数的返回值
- python函数使用return语句返回"返回值";
- 所有函数都有返回值,如果没有return,则会隐式调用return None;
- return语句并不一定是函数语句块的最后一条语句
- 一个函数可以存在多个return语句,但只有一条是可以被执行的,如果没有一条return语句被执行,则隐式调用return None;
- 如果有必要,可以显示调用return None,也可以简写为return
- 如果函数执行了return语句,函数就会返回,当前被执行的return语句之后的其他语句都不会执行了;
- 返回值的作用:
- 结束函数的调用
- 返回值
- 可以返回多个值?
# 函数不能同时返回多个值,return 1,3,5 表示返回了一 个元组(1,3,5) # 返回值被python隐式封装成了一个元组;
def showlist(): return 1,3,5 res=showlist()=(1,3,5) x,y,z=showlist=(1,3,5) # 使用解构可以取值方便
# 函数的返回值可以使任意数据类型,但是能且只能返回一个;
2、函数的嵌套
# 函数嵌套:在一个函数内部定义了另外一个函数;
# 内部函数对于外部不可见,不能直接对其调用,否则会抛异常;
def outer(): def inner(): print('from inner') print('from outer') inner() outer() ''' ==> from outer from inner ''' inner() # NameError: name 'inner' is not defined
3、作用域
3.1、 变量的作用域
作用域其实指的是变量的作用范围;变量并不是在哪个位置都可以访问的,访问权限取决于这个变量是在哪赋值的;也就是在哪个作用域内;
通常来说,在编程语言中,变量的作用域从代码的结构形式来看的,有块级,函数,类,模块,包等由小到大;
python中没有块级作用域,也就是类似if-else-if,for语句,with上下文管理,他们不存在作用域关系;
3.2、python的作用域分层
- L(Local) 局部作用域
- E(Enclosing)嵌套作用域
- G(Global) 全局作用域
- B(Build-in) 内建作用域
查找原则:L==>E==>G==>E
3.3、作用域关系实例解析
(1)下面代码运行结果:
x=5 def foo() x+=1 print(x) foo()
#代码分析: def foo(): x+=1 ''' x+=1 ==> x=x+1 python赋值即定义,x=x+1 相当于从新定义一个变量x, 然后拿到一个未赋值的变量x,(这里的x指的是等式右边的x)给(等式左边的x)x赋值, 相当于: x x=x+1 ''' print(x) # UnboundLocalError: local variable 'x' referenced before assignment ''' 函数在定义阶段并没有报错,因为在定义阶段只检查语法; ''' foo()
(2)下面代码运行结果?
name ='jack' def f1(): print(name) def f2(): name = 'eric' f1() f2()
# 代码分析:
常规错误理解:在f2内部定义一个变量name,然后调用f1(),执行打印print(name),这个name按照LEGB的查找关系应该找f2()内部的name,所以打印'eric';
关键点:函数的作用域关系在函数的定义阶段就已经形成,跟调用函数的位置无关;上面代码执行f1(),f1内部没有name变量,则向全局查找name,所有打印'jack'
总结:作用域关系,在函数定义时候就已经固定,与调用位置无关,在调用函数时候,必须一定要回到函数原来定义的位置去找作用域关系;
4、闭包函数
# 自由变量:未在本地作用域中定义的变量,例如:定义在内层函数外的外层函数的作用域中的变量;
# 闭包:就是一个概念,出现在嵌套函数中,指的是内层函数引用了外层函数的自由变量,就形成了闭包;
# 闭包作用?
一般情况下,如果一个函数调用结束,函数内部的所有东西都会释放,还给内存,函数的局部变量消失;
但是闭包是一种特殊情况,如果外部函数在调用结束后发现自己的临时变量会被内部函数引用,就会把这个临时变量绑定给内部函数,然后再结束;
# 理解闭包函数 # 1、简单闭包函数 def counter(): c = [0] def inc(): c[0] += 1 #应用的是自由变量正式counter的变量c return c[0] print(c[0]) return inc #counter()() foo = counter() # counter()调用结束将,变量c=[0]并不销毁; print(foo(),foo()) #调用的是inner() print(foo()) # 2、分析函数打印结果 def count(): fs=[] for i in range(1,4): # for循环3次相当于在count函数内定义了3次函数f def f(): return i*i fs.append(f) return fs f1,f2,f3=count() # count函数调用结束,for循环在调用结束后 变量i=3 最后内存中i记录的值为3; print(f1()) # 调用f1()相当于调用f(),return i*i 所有返回9 print(f2()) print(f3())
# 3、将上面函数稍作修改
def count1():
fs=[]
for i in range(1,4):
def f():
return i*i
fs.append(f())
return fs
print(count1())
f1,f2,f3=count()
print(f1,f2,f3)
# 上面代码在for循环定义f函数后直接对f函数进行调用,然后将返回值i*i 添加到列表fs,由于i的取值是发生在for循环一次之后所以对应的i取值为1,2,3
5、nonlocal关键字
# 使用了nonlocal关键字的变量 ,将变量标记为不在本地作用域定义,而在上级的某一级局部作用域中定义,但不能是在全局作用域中定义;
def bar(): x=0 def foo(): nonlocal x # 将x标记为非本地变量 x+=1 # x=x+1 此时x向外层函数找,x=0,x=x+1 ,形成闭包; return x return foo res=bar()() print(res)
6、默认值的作用域
- 函数也是对象,python 把函数的默认值放在了属性中,这个属性就伴随着这个函数对象的整个生命周期;
# 参数为引用类型实例
def foo(l:list=[]): l.append(1) return l print(foo(),id(foo)) print(foo.__defaults__) print('#'*30) print(foo(),id(foo)) print(foo.__defaults__) ''' [1] 1966940858160 ([1],) ############################## [1, 1] 1966940858160 ([1, 1],) foo地址并没有变,就是说foo这个对象没有变,调用它,它的属性__defaults__ 中使用元组保存默认值;
l是列表,__defaults__ 中元组记录的是l引用地址,列表元素的增加,列表的地址不会发生改变; '''
# 参数为非引用类型
def foo(w,u='abc',z=123):
u = 'xyz'
z = 789
print(w,u,z)
print(foo.__defaults__)
foo('test')
print(foo.__defaults__)
''’
运行结果:
('abc', 123)
test xyz 789
('abc', 123)
test xyz 789
('abc', 123)
'''
分析:属性__defaults__中使用元组保存所有位置参数默认值,它不会因为在函数体内使用了它而发生改变;
# 带keyword-only参数
def foo(w,u='abc',*,z=123,zz=[456]):
u = 'xyz'
z = 789
zz.append(1)
print(w,u,z,zz)
print(foo.__defaults__)
foo('tes')
print(foo.__kwdefaults__)
'''
运行结果:
('abc',)
tes xyz 789 [456, 1]
{'z': 123, 'zz': [456, 1]}
属性__kwdefaults__ 中使用字典保存所有keyword-only 参数的默认值;
使用可变类型作为形参默认值的时候,就可能修改这个默认值;
'''
运行结果:
('abc',)
tes xyz 789 [456, 1]
{'z': 123, 'zz': [456, 1]}
属性__kwdefaults__ 中使用字典保存所有keyword-only 参数的默认值;
使用可变类型作为形参默认值的时候,就可能修改这个默认值;
'''
- 使用可变类型作为形参默认值的时候,就可能修改这个默认值;
(1)函数体内,不改变默认值;
def foo(xyz=[],u='abc',z=123): xyz = xyz[:] #影子拷贝 xyz.append(1) print(xyz) print(foo.__defaults__) foo() print(foo.__defaults__) foo([10]) foo() print(foo.__defaults__) foo([10,5]) print(foo.__defaults__) ''' 运行结果: ([], 'abc', 123) [1] ([], 'abc', 123) [10, 1] [1] ([], 'abc', 123) [10, 5, 1] ([], 'abc', 123) ''' # 函数中的xyz 都是传入参数或者默认参数的副本,如果就想修改原参数,不可以;
(2)使用不可变类型的默认值;
def foo(xyz=None,u='abc',z=123): if xyz is None: xyz = [] xyz.append(1) print(xyz) print(foo.__defaults__) foo() foo() print(foo.__defaults__) foo([10]) print(foo.__defaults__) foo([10,5]) print(foo.__defaults__) ''' (None, 'abc', 123) [1]
[1] (None, 'abc', 123) [10, 1] (None, 'abc', 123) [10, 5, 1] (None, 'abc', 123) ''' # (1)如果使用缺省值None就创建一个列表 # (2)如果传入一个列表,就修改这个列表