函数
1、函数的基本知识
1.1 、break
跳出当前执行的循环体;
1.2 、return
跳出当前执行的函数,结束一个函数;
return
return None 基本不用
return 返回1个值
return 多个值----:return value1,value2....,多个返回值用多个变量进行接收,有多少个返回值就得用多少个变量接收
多个变量可以用一个变量进行接收,这时候这个变量接收值以后就是元组,其实返回值就是一个元组
元组是可以解包的: a,b,c = (1,3,4),那么a = 1,b = 2,c = 4
1.3 、站在形参角度上
按照位置传参,按照参数的位置一一对应去赋值传实参
按照关键字传参,就是形参里面是什么名称,调用函数传实参的时候就用什么名称
混着用可以:但是 必须先按照位置传参,再按照关键字传参数
不能给同一个变量传多个值
1.4、站在实参角度上
位置参数:必须传,且有几个参数就传几个值
默认参数: 可以不传,如果不传就是用默认的参数,如果传了就用传的,默认的参数就是函数的形参里面已经定义实参的那种
先定义位置参数,后定义默认参数
动态参数 : 可以接受任意多个参数,
参数名之前加*,习惯参数名args,但关键字参数接收不了,接收的是按位置传参数的值,组织成一个元组
参数名之前加**,习惯参数名kwargs,接收的是按关键字传参数的值,组织成一个字典
1.5、函数放参数的顺序
函数放参数顺序:位置参数,*args,默认参数,**kwargs
1.6、动态参数的另一种传参方式
第一种:可变位置参数*args
1 def sum(*args): 2 n = 0 3 for i in args: 4 n+=i 5 return n
结果:
1 3 2 6 3 10
第二种:可变关键字参数**kwargs
1 def func2(**kwargs): 2 print(kwargs) 3 func2(a = 1, b = 2, c = 3)
结果:
1 {'a': 1, 'b': 2, 'c': 3}
下面代码结果是
1 def func3(*args,**kwargs): #组合式动态形参函数,那么先args后kwargs,且传实参也得是这个顺序 2 print(args,kwargs) 3 4 func3(1,2,3,a = 5,c = 9) 5 func3(1,2,3,a = 5,c = 9)
结果:
1 (1, 2, 3) {'a': 5, 'c': 9} 2 (1, 2, 3) {'a': 5, 'c': 9}
下面代码结果是
1 def func4(*args): 2 print(args) 3 L = [1,2,3,4] 4 func4(*L)
结果:
1 #从实参角度上看,给一个序列加上*,就是将这个序列按照顺序打散 2 (1, 2, 3, 4) 3 {'a': 2, 'b': 8}
下面代码结果是:
1 def func5(**kwargs): 2 print(kwargs) 3 l5 = {'a':2,'b':8} 4 5 func5(**l5)
结果是:
1 {'a': 2, 'b': 8}
1.7、函数的注释
1 def func(): 2 ''' 3 这个函数是实现什么功能 4 参数1: 5 参数2: 6 参数3: 7 :return: 返回的是什么 8 '''
1.8、默认参数的陷阱问题
总体原则:当默认参数是一个可变数据类型(可哈希)时候,那么每一次调用函数的时候,如果不传值就共用这个数据类型的资源
下面代码结果是:
1 def func2(l = []): 2 l.append(1) 3 print(l) 4 func2() 5 func2([]) 6 func2() 7 func2()
1 def func2(l = []): 2 l.append(1) 3 print(l) 4 func2() #不传参数,那么用默认的参数 5 func2([]) #传了个自己的空列表,那么不用默认的空列表 6 func2() #不传参数,那么接着用默认的参数 7 func2() #不传参数,那么接着用默认的参数 8 9 结果: 10 [1] 11 [1] 12 [1, 1] 13 [1, 1, 1]
下面代码的结果是:
1 def func2(l = {}): 2 l['k'] = 'v' 3 print(l) 4 func2() 5 func2() 6 func2()
结果:
1 #如果是空字典,那么里面其实是重复把key的值固定为v 2 {'k': 'v'} 3 {'k': 'v'} 4 {'k': 'v'}
下面代码的结果是:
1 def func2(k,l = {}): 2 l[k] = 'v' 3 print(l) 4 func2(1) 5 func2(2) 6 func2(3)
结果:
1 {1: 'v'} 2 {1: 'v', 2: 'v'} 3 {1: 'v', 2: 'v', 3: 'v'}
1.9 函数的命名空间
#命名空间 有三种
#内置命名空间 —— python解释器
# 就是python解释器一启动就可以使用的名字存储在内置命名空间中
# 内置的名字在启动解释器的时候被加载进内存里
#全局命名空间 —— 我们写的代码但不是函数中的代码
# 是在程序从上到下被执行的过程中依次加载进内存的
# 放置了我们设置的所有变量名和函数名
#局部命名空间 —— 函数
# 就是函数内部定义的名字
# 当调用函数的时候 才会产生这个名称空间 随着函数执行的结束 这个命名空间就又消失了
#在局部:可以使用全局、内置命名空间中的名字
#在全局:可以使用内置命名空间中的名字,但是不能用局部中使用
#在内置:不能使用局部和全局的名字的
#依赖倒置原则:(内置的名字空间 (全局的名字空间 (局部的名字空间) ) ):内部的可以到上层找,但上层不能到更里层找
#在正常情况下,直接使用内置的名字
#当我们在全局定义了和内置名字空间中同名的名字时,会使用全局的名字
#当我自己有的时候 我就不找我的上级要了
#如果自己没有 就找上一级要 上一级没有再找上一级 如果内置的名字空间都没有 就报错
# 多个函数应该拥有多个独立的局部名字空间,不互相共享
1 def input(): 2 print('in input now') 3 def func(): 4 # input = 1 5 print(input) 6 func()
1 <function input at 0x00000000004D2F28>
# 对于不可变数据类型 在局部可是查看全局作用域中的变量
# 但是不能直接修改
# 如果想要修改,需要在程序的一开始添加global声明
# 如果在一个局部(函数)内声明了一个global变量,那么这个变量在局部的所有操作将对全局的变量有效
# 作用域两种
# 全局作用域 —— 作用在全局 —— 内置和全局名字空间中的名字都属于全局作用域 ——globals()
# 局部作用域 —— 作用在局部 —— 函数(局部名字空间中的名字属于局部作用域) ——locals()
1 a = 1 2 def func(): 3 global a 4 a += 1 5 func() 6 print(a)
代码的结果是;
1 结果就是:2
1 a = 1 2 b = 3 3 def func2(): 4 x = 'aaa' 5 y = 'ccc' 6 print(locals()) 7 func2()
1 {'y': 'ccc', 'x': 'aaa'}
1 a = 1 2 def func3(): 3 global a #这里会代替外面全局的变量a,这样名字实际被改了 4 a = 2 5 func3() 6 print(a) 7 print(globals()) #globals永远打印全局的名字 8 print(locals()) #locals放在全局,那么它的局部就是全局;如果放在局部,那么就是局部
1 2 2 {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000000000275BBA8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/Python Project/05/Day10/函数的命名空间.py', '__cached__': None, 'a': 2, 'func3': <function func3 at 0x00000000020B2F28>} 3 {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000000000275BBA8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/Python Project/05/Day10/函数的命名空间.py', '__cached__': None, 'a': 2, 'func3': <function func3 at 0x00000000020B2F28>}
1.10 函数的嵌套
函数的嵌套:内部函数可以使用外部函数的变量
1 def outer(): 2 def inter(): 3 print(2) 4 inter() 5 outer()
1 2
1 def outer(): 2 b = 5 3 def inter(): 4 print(b) 5 print('in the inter') 6 inter() 7 outer()
1 5 2 in the inter
定义为gloabl的函数中的变量,那么该变量是全局的变量,局部变量如果同名那么是没有修改的
不可变数据类型在它的下级函数不能被修改,只能查看
1 b = 2 2 def outer(): 3 b = 5 4 def inter(): 5 print(b) 6 c = 4 7 print('in the inter') 8 def interb(): 9 #c += 1 #不可变数据类型在它的下级函数不能被修改,只能查看 10 global b 11 b += 1 12 print(c) 13 print(b) 14 print('in the interb') 15 interb() 16 inter() 17 outer() 18 print(b)
1 5 2 in the inter 3 4 4 3 5 in the interb 6 3
1.10.1 nonlocal的使用
onlocal声明了上面一层的局部变量,离被使用b最近的局部变量,即往外找找到最近一层的那个变量b,只能用于局部变量
如果往上找没找到局部的变量那么报错,全局的变量是不行的,即nonlocal对全局变量无效。
1 b = 2 2 def outer(): 3 b = 5 4 def inter(): 5 print(b) 6 print('in the inter') 7 def interb(): 8 nonlocal b 9 b +=1 10 print(b) 11 print('in the interb') 12 interb() 13 inter() 14 print(b) 15 outer() 16 print(b)
1 5 2 in the inter 3 6 4 in the interb 5 6 6 2
1.10.2 一个函数的函数名就是一个内存地址
1 def fun(): 2 print(33) 3 fun2 = fun 4 fun2() 5 lis = [fun,fun2] #函数名可以作为容器类型的元素 6 print(lis) 7 for i in lis: 8 i()
1 33 2 [<function fun at 0x00000000003E2F28>, <function fun at 0x00000000003E2F28>] 3 33 4 33
1.10.3 函数可以作为另一个函数的参数
1 def func(): 2 print(44) 3 def func2(f): 4 f() 5 func2(func)
1 44
1.10.4 函数名可以作为另一个函数的返回值
1 def func(): 2 print(44) 3 def func2(f): 4 f() 5 return f #函数名可以作为函数的返回值 6 a = 0 7 a = func2(func) #函数名可以作为函数的参数 8 print(a) 9 a()
1 44 2 <function func at 0x0000000001D22F28> 3 44
1.10.5 第一类对象(first-class objet)指
1 1、可在允许期创建 2 2、可用作函数参数或返回值 3 3、可存入变量的实体
1.11 闭包
闭包:嵌套函数,内部函数调用外部函数的变量;其实就是一个函数A里面嵌套了另一个函数B,并且函数B里面使用了函数A的变量,那么函数B为闭包; 如果函数B没有使 用函数A里面的变量,但使用了全局变量,那么函数B就不是闭包。
检测一个函数是否为闭包的形式:
print(inner.__closure__) #这是检测inner()是否为闭包
1 def outer(): 2 b = 2 3 def inner(): 4 print(b) 5 inner() 6 print(inner.__closure__) 7 outer() 8 print(outer.__closure__)
1 2 2 (<cell at 0x00000000003FEEB8: int object at 0x000000001D8E60C0>,) #表示inner()函数是闭包 3 None #表示outer()函数不是闭包 4 5 函数中如果没有print(b)这句话,即调用上一层变量这么个操作的 话,那么inner()函数就不是闭包
1 b = 1 2 def outer(): 3 def inner(): 4 print(b) #这里调用的是全局表里的b,所以不是闭包 5 print() 6 outer()
1.11.1 闭包的常用形式:
在函数外部使用函数内部的函数,在在函数外部使用嵌套的函数。
闭包的作用:在多次调用同一函数时候,可以节省空间的作用
1 def func(): 2 a = 77 3 def func2(): 4 print(a) 5 return func2 6 func3 = func() 7 func3()
1 77 2 分析:实际上func3()就等价于调用了func2()
1 from urllib.request import urlopen 2 def func(): 3 url = 'http://www.ifeng.com/' 4 def get(): 5 ret = urlopen(url).read() 6 print(ret) 7 return get 8 get_net = func() 9 get_net()
1 b'<!DOCTYPE html>\n<html xmlns:wb="http://open.weibo.com/wb">\n<head>\n <meta charset="utf-8">\n 2 ........... 3 ........... 4 还有很多,不再写出来 5 其实结果就是网页的源码 6 这里如果我们多次调用嵌套函数get()时候就可以起到调用的时候才生成ural这个常量的空间,用完就释放空间的作用,这样起到节省空间的作用