1、函数的返回值:
例子:
def show(x):
print(x)
return x+1
return x+2
show(5)
>>>5
Out[37]: 6
#打印5 返回值6 只执行第一个return
python函数使用return语句返回'返回值'
所以函数都有返回值,如果没有return语句,隐式调用return None
return语句并不一定是函数的语句块的最后一句语句
一个函数可以存在多个return语句,但只有一条可以执行
如果函数执行了return语句,函数就会返回,当前执行的return语句后的其他语句就不执行了
return 等价于 return None
return 作用
结束调用 返回值
例子:
1.
def showlist():
return[1,3,5]
2.
def showlist():
return 1,3,5
1. showlist函数返回一个列表[1,3,5]
2. showlist函数返回一个元组(1,3,5)
函数不能返回多个值
return[1,3,5] 返回的是一个列表对象
return 1,3,5 看似返回多个值,隐式被python封装成了元组
2、作用域:
一个标识符的可见范围,这就是标识符的作用域;也可以说变量的作用域
1.
x=5
def fn():
print(x)
fn()
>>>5
2.
x=50
def fn2():
x+=1
print(x)
fn2()
>>>(x+=1)Error:local variable 'x' referenced before assignment 在赋值前引用了变量x
解析:
调用fn2()->x+=1 等同 x= x+1 ,在这里右边x+1 赋值给x ,相当于x被赋值重新定义,
在内部定义了一个局部变量x,这个时候x只是个标识符,但是x并没有完成赋值就被右边执行+1操作。
如果在本地作用域中,先给x赋一个新值就不会报错
全局作用域:
整个程序环境都可见
局部作用域:
在函数、类等内部可见
局部变量使用范围不能超过其所在的局部作用域
1.
def fn1():
x = 1 #local 变量x 在外部不可见
def fn2():
print(x)
fn2()
>>> Error name'x' is not defined
2. y = 10#y是全局变量 可见于内部
def fn():
print(y)
fn()
>>>10
嵌套结构:
1.
def outer1():
o = 65
def inner():
print('inner:{}'.format(o))
print(chr(o))
print('outer:{}'.format(o))
inner()
outer1()
>>>outer:65
>>>inner:65
>>>A
outer1(1)-> o =65 ->输出outer-> inner()->inner:65->chr(o) = A
2.
def outer2():
o = 65
def inner():
o = 97
print('inner:{}'.format(o))
print(chr(o))
print('outer:{}'.format(o))
inner()
outer2()
>>>outer:65
>>>inner:97
>>>a
outer2()->o = 65 -> 输出outer -> inner() o =97 -> inner:97 -> chr(o)=a
外层变量作用域在内层作用域可见
内层作用域inner中,定义了o=97,重新定义了一个新的变量o,但是o没有覆盖外层的o=65
例:
x = 5
def foo():
y = x+1
x +=1
print(x)
foo()
>>>错误指向y=x+1 Error local variable 'x' referenced before assignment
在赋值前引用了变量x
x+=1 等同 x = x+1,x+1 赋值定义x 本地作用域中,x被重新定义赋值,但此时的x只是一个标识符,
还没有完成赋值,就被执行+1操作。如果在本地作用域中,在x+=1 前给x赋值 就可以继续往下进行。
所以这里会报错,在x赋值前引用了变量x。但是,当没有x+=1 时,只有y=x+1 ,
这时,x引用的是外部的x=5,调用foo()时,可以正常打印x
全局变量(Global):
1.
x = 5
def foo():
global x
x+=1
print(x)
使用global关键字的变量后,自动将foo()内的x声明为使用外部的全局作用域定义的变量x
这里x+=1就不报错了
foo()
>>>6
2.
def foo():
global z
z = 10
z +=1
print(z)
>>>foo() ->11
>>>print(z)->11
这里使用了global,z的作用域为全局,但是外部并没有z变量。内部z=10赋值即定义,内部作用域为一个外部作用域
的变量赋值,所以z+=1 不会报错。这里的z作用域还是全局,所以print(z) 不会报错并正常打印
以上总结:
(1)x+=1 原则上是引用赋值,但python动态语言特殊,先赋值才算是定义然后才可以被引用。
当然,可以在这条语句前 添加一个x=0类似赋值语句或者使用global将变量x指定为全局作用域
(2)内部作用域使用了x=0这样的赋值语句后会重新定义变量x的作用域为局部。但是使用了global
后,变量x作用域就变成了全局,x=0 就相当于对全局作用域的变量x赋值
'global'使用:
(1)在函数中,使用了global,外部作用域变量会在内部作用域中可见,但是这样会破坏函数的封装,所以局部和
外部应尽量隔离;
(2)如果函数需要使用外部全局变量,应使用函数传参的方式解决
闭包:
(1) 自由变量:未在局部作用域中定义的变量。
(2) 闭包;出现在嵌套函数中,指的是内层函数引用到了外层函数的自由变量,就形成了闭包。
def counter():
c = [0] #对c赋值
def inc():
c[0] += 1#c是一个列表,对c列表的元素赋值 并不是对c赋值 所以不会出错
return c[0]
return inc #返回函数的对象(callable) inc()函数的调用
>>>foo = counter() #counter() -> return inc->inc() ,foo就变成了可调用对象 (callable)
>>>print(foo(),foo())#调用foo
#1、foo() -> c=[0] -> inc()->c[0]+=1->1
#2、foo() -> c=[0] -> inc()->c[1]+=1->2
>>>c = 100#外部c赋值定义100
>>>print(foo())
#foo() -> c=[0] -> inc()->c[2]+=1->3
在counter函数中,定义了变量c,下面嵌套了一个inc函数; inc函数中使用了变量c。
对于inc函数来讲,c是一个自由变量,inc函数引用了counter函数的自由变量,这里就产生了闭包。
每次调用inc函数,c的值随着c[0]+=1产生累加。
c=100,c相当于全局变量,但在函数中c作为局部变量,两者没有关系,inc函数引用的是counter()下的
自由变量,所以再次调用foo还是累加1
nonlocal 关键字
使用了nonlocal关键字,将变量标记在上级的局部作用域中定义但不能是全局作用域中定义
def counter():
count = 0
def inc():
nonlocal count
count+=1
return count
return inc
foo = counter()
foo()#1
foo()#2
count 是外层函数的局部变量,被内部函数引用
内部函数使用nonlocal关键字声明count变量在上一级作用域中
形成闭包
默认值的作用域:
1.
def foo(xyz = 1):
print(xyz)
foo()#1
foo()#1
print(xyz)#Error "Name 'xyz' is not defined"
xyz虽然是形参,但属于局部变量,只能在内部打印
2.
def foo(xyz=[]):
xyz.append(1)
print(xyz)
foo() #[1]
foo()#[1,1]
foo()第二次[1,1]:
因为函数也是对象,python把函数的默认值放在了属性中,这个属性伴随着这个函数对象整个生命周期
3.引用类型:
def foo(xyz = [], u='abc', z=123):
xyz.append(100)
print(xyz)
print(foo.__defaults__)
>>>([],'abc',123)
print(foo(),id(foo))
print(foo.__defaults__)
>>>[100]
>>>None 2502614720296
>>>([100], 'abc', 123)
print(foo(),id(foo))
print(foo.__defaults__)
>>>[100,100]
>>>None 2502614720296
函数地址并没有变,调用它,它的属性__defaults__中使用元组保存所有默认值
xyz默认值是引用类型,引用类型元素的变化不等同元组变化
可变类型默认值,如果使用默认值,就可能修改这个默认值(list)
4.非引用类型:
def foo(w, u='abc', z=123):
u = 'xyz'
z = 123
print(w,u,z)
print(foo.__defaults__)
>>>('abc',123)
foo('test')
>>>test xyz 123
print(foo.__defaults__)
>>>('abc',123)
__defaults__属性中使用元组保存所有默认值,它不会因为在函数体内使用了它而改变
5.函数体内不改变默认值
def foo(xyz=[], u='abc', z=123):
xyz = xyz[:]
xyz.append(1)
print(xyz)
foo()
print(foo.__defaults__)
>>>[1]
>>>([],'abc',123)
foo(10)
print(foo.__defaults__)
>>>[10,1]
>>>([],'abc',123)
xyz都是传入参数或者默认参数的副本,无法修改原参数
使用影子拷贝的方法创建一个新的对象,永远不能改变传入的参数
6.None作为默认值:
def foo(x = None, y='abc', z = 123):
if x is None:#如果是None
x = []#创建一个列表
x.append(1)#否则追加1
print(x)
foo()
print(foo.__defaults__)
>>>1
>>>(None, 'abc', 123)
foo()
print(foo.__defaults__)
>>>1
>>>(None, 'abc', 123)
foo([10])
print(foo.__defaults__)
>>>[10,1]
>>>(None, 'abc', 123)
1.通过值的判断就可以灵活的选择创建或者修改传入对象
2.这种方式灵活,应用广泛
3.许多函数的定义,都可以看到使用None这个不可变的值作为默认参数(贯用法)。
全局函数的销毁:
1.重新定义同名函数
2.del语句删除函数对象
3.程序结束掉
def foo(x= [], y='zz', z = 123):
x.append(1)
return x
print(foo(),id(foo),foo.__defaults__)
def foo(x= [], y='zz', z = 123):
x.append(1)
return x
print(foo(),id(foo),foo.__defaults__)
del foo
print(foo(),id(foo),foo.__defaults__)
>>>[1] 2603035412272 ([1], 'zz', 123)
>>>[1] 2603035412136 ([1], 'zz', 123)
>>>Error name'foo' is not defined #foo未定义
局部函数销毁:
1.重新在上级作用域定义同名函数
2.del语句删除函数对象
3.上级作用域销毁时
def foo(x=[], y='zz', z=123):
x.append(1)
def inner(a=10):
pass
def inner(a=100):
print(x)
print(id(inner))
return inner
bar = foo()
print(id(foo),id(bar), foo.__defaults__, ba
r.__defaults__)
del bar
print(id(foo),id(bar), foo.__defaults__, bar.__defaults__)
>>>2551461446248
>>>2551461445704 2551461446248 ([1], 'zz', 123) (100,)
>>>Error name 'bar' is not defined