1. 函数
(1)特点
可读性强、复用性强
(2)定义
def funcname():
funcbody
return
(3)调用
funcname()
(4)注释
def func(): ''' 这个函数实现了什么功能 参数1:解释 参数2:解释 ... :return: 返回值类型 ''' #funcbody函数体
(5)注意事项
只定义不调用就一定不执行
先定义后调用
2. 返回值
返回的重要性---不能使用函数调用结束的结果进行其他操作
函数名()——不接收返回值
返回值 = 函数名()——接收返回值
(1)没有返回值:
默认返回None
<1>不写return:函数内的代码执行完毕,自动结束
def func(): l = ['xc','gx'] for i in l: print(i) #xc gx if i=='gx': pass print('lalala') #lalala rt = func() print(rt) #None
<2>只写return:结束一个函数
def func(): l = ['xc','gx'] for i in l: print(i) #xc gx if i=='gx': return print('lalala') #return之后函数结束,不再继续执行这句话 rt = func() print(rt) #None
<3>return None——不常用
def func(): l = ['xc','gx'] for i in l: print(i) #xc gx if i=='gx': return None print('lalala') #return之后函数结束,不再继续执行这句话 rt = func() print(rt) #None
(2)返回一个值
结束了函数且返回一个值,可以是任意的值
<1>可以返回任何数据类型
<2>只要返回就可以接收到
<3>如果在一个程序中有多个return,只执行第一个
def func(): return print(func()) #None def func1(): return [1,2,3,4] print(func1()) #[1, 2, 3, 4] def func2(): return {'k','v'} return 1 #在前一个return已经结束函数了,此条语句不执行 print(func2()) #{'v', 'k'}
(3)返回多个值
多个值之间用逗号隔开,可以用一个(返回元祖)或者等量的多个变量接收
def func(): return 1,2 rt1,rt2 = func() print(rt1,rt2) #1 2 r = func() print(r) #(1, 2)
3. 参数
没有参数——定义和调用函数的时候,括号里不写内容
有一个参数——传什么就是什么
有多个参数——位置参数
(1)形参和实参
def my_len(s): #接收参数——形式参数,形参 i = 0 for k in s: i += 1 return i s = '金老板小护士' rt = my_len(s) #传递参数(传参)——实际参数,实参 rt1 = my_len('班主任星儿最好看') print(rt) #6 print(rt1) #8
(2)形参——定义函数的时候
<1>位置参数:必须传,且有几个参数传几个值
<2>默认参数:可以不传,不传就使用默认的,传什么用什么
默认参数的陷阱:
默认参数的值是可变数据类型,那么每一次调用函数的时候,如果不传值就公用这个数据类型的资源
#(1) def qqxing(l = []): l.append(1) print(l) qqxing() #[1] qqxing([]) #[1] qqxing() #[1, 1] qqxing() #[1, 1, 1] qqxing([]) #[1] #(2) def qqxing(l = {}): l['k'] = 'v' print(l) qqxing() #{'k': 'v'} qqxing({}) #{'k': 'v'} qqxing() #{'k': 'v'} qqxing({}) #{'k': 'v'} #(3) def qqxing(k,l = {}): l[k] = 'v' print(l) qqxing(1) #{1: 'v'} qqxing(5,l = {'k3':'v3'}) #{'k3': 'v3', 5: 'v'} qqxing(2) #{1: 'v', 2: 'v'} qqxing(22) #{1: 'v', 2: 'v', 22: 'v'} qqxing(6,l = {'k3':'v3'}) #{'k3': 'v3', 6: 'v'} qqxing(3) #{1: 'v', 2: 'v', 3: 'v'} qqxing(33) #{1: 'v', 2: 'v', 22: 'v', 3: 'v', 33: 'v'} qqxing(333) #{1: 'v', 2: 'v', 22: 'v', 3: 'v', 33: 'v', 333: 'v'} qqxing(7,l = {'k3':'v3'}) #{'k3': 'v3', 7: 'v'} qqxing(3,l = {'k3':'v3'}) #{'k3': 'v3', 3: 'v'} qqxing(8,l = {'k3':'v3'}) #{'k3': 'v3', 8: 'v'}
<3>动态参数:可以接收任意个参数
*args:接收的是按照位置传参的值,组织成一个元祖
**kwargs:接收的是按照关键字传参的值,组织成一个字典
args必须在kwargs之前
# 动态传参(*args) def func00(*args): #站在形参的角度,给变量加上*,就是组合所有的值 print(args) func00(1,2,3,4,5) #(1, 2, 3, 4, 5) l = [1,2,3,4,5] func00(*l) #(1, 2, 3, 4, 5) 站在实参的角度,给一个序列加上*,就是将这个序列按照顺序打散 def func1(**kwargs): #可以接收任意多个按关键字传的参数 print(kwargs) func1(a=1,b=2,c=3) #{'a': 1, 'b': 2, 'c': 3} func1(a=1,b=2) #{'a': 1, 'b': 2} func1(a=1) #{'a': 1} def func11(**kwargs): print(kwargs) func11(a=1,b=2) #{'a': 1, 'b': 2} d = {'a':1,'b':2} func11(**d) #{'a': 1, 'b': 2} def func2(*args,**kwargs): print(args,kwargs) func2(1,2,3,a = 'addc', b = 'csdsv') #(1, 2, 3) {'a': 'addc', 'b': 'csdsv'}
<4>混合使用
# 位置参数+默认参数 def classmate(name,sex='男'): print('%s:%s'%(name,sex)) classmate('二哥') #二哥:男——位置参数+默认参数(未传值) classmate('小梦','女') #小梦:女——位置参数+默认参数(传值) classmate('大猛') #大猛:男——位置参数+默认参数(未传值) # 动态参数(*args)+关键字参数 def func0(*args,l=[]): print(args,l) func0() #() [] func0(1,2,'str',['lst',1]) #(1, 2, 'str', ['lst', 1]) [] func0(1,2,'str',l = ['lst',1]) #(1, 2, 'str') ['lst', 1]
特点:
<1>位置参数:必须传
<2>*args:动态参数,可以接收任意多个按位置传的参数
<3>默认参数:可以不传——陷阱
<4>**kwargs:动态参数,可以接收任意多个按关键字传的参数
定义函数的时候
位置参数:直接定义参数
默认参数:关键字参数,参数名 = ‘默认的值’
动态参数:可以接收任意多个参数
定义方式:*变量名(习惯用*args)
**变量名(习惯用**kwargs)
顺序:位置参数,*args,默认参数,**kwargs
(3)实参——调用函数的时候
<1>按照位置传参数
<2>按照关键字传参数
def my_sum(a,b): print(a,b) #1 2 res = a+b return res rt = my_sum(1,2) #按照位置传参 rt1 = my_sum(b = 2,a = 1) #按照关键字传参 rt2 = my_sum(1,b = 2) #混合使用(位置传参在前,关键字传参在后) print(rt) #3 print(rt1) #3 print(rt2) #3
特点:
可以混用 位置必须在关键字之前
不能对一个参数重复赋值
调用函数的时候:
按照位置传:直接写参数的值
按照关键字传:关键字 = 值
4. 命名空间(3种)
(1)内置命名空间——python解释器
python解释器一启动就可以使用的名字
内置的名字在启动解释器的时候被加载进内存中
(2)全局命名空间——我们写的代码,但不是函数中的代码
是在程序从上到下执行的过程中依次加载进来的
放置了我们设置的所有变量名和函数名
(3)局部命名空间——函数
是函数内部定义的名字
当调用函数的时候才会产生这个名字空间,随着函数的结束而消失
多个函数应该拥有多个独立的名字空间,不互相共享
(4)加载与取值的顺序
加载顺序:内置命名空间 > 全 局 命 名 空 间 > 局 部 命 名 空 间
程序运行前加载 程序运行中:从上到下 程序运行中:调用时才加载
(5)名字空间的使用
在内置:不能使用全局和局部的名字
在全局:可以使用内置的名字,但不能使用局部的名字
在局部:可以使用内置和全局的名字
局部有就用局部的,局部没有就找全局的;
全局有就用全局的,全局没有就找内置的;
内置的有就用内置的,内置没有就报错
def max(): print('in max func') max() #in max func def input(): print('in input now') def func(): input = 1 print(input) #<function input at 0x0000012CB1761D08> #func() #func---->函数的内存地址 #func()---->函数的调用 #函数的内存地址()---->函数的调用
5. 作用域(2种)
(1)全局作用域
作用在全局——包含内置和全局名字空间中的名字
(2)局部作用域
作用在局部——包含局部名字空间()函数中的名字
对于不可变数据类型:在局部可以查看全局作用域中的名字
不能直接修改
想要修改,则需要在函数开始添加globle
6. 关键字:global、local、nonlocal
(1)global
如果在一个局部(函数)内声明了global变量,在局部的所有操作对这个变量有效
a = 1 def func(): global a a += 1 func() print(a) #2
尽量少使用global,涉及到安全问题——用传参替代
a = 1 def func(a): a += 1 return a a = func(a) print(a) #2
(2) global和locals的区别
查看局部作用域中的所有名字——globals()全局的——在局部和全局永远都打印全局的名字
查看全局作用域中的所有名字——locals()本地的——在局部就打印局部的名字,在全局就打印全局的名字
a = 1 b = 2 def func(): x = 'aaa' y= 'bbb' print(locals()) #局部{'y': 'bbb', 'x': 'aaa'} func() print(locals()) #全局+内置(同下) print(globals()) #全局+内置 #{'__name__': '__main__', '__doc__': ' a = 1 def func(): global a # a += 1 # a = 2 func() print(a) ', '__package__': None, # '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000002028799B518>, # '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, # '__file__': 'E:/Python/day10/03 函数的命名空间.py', '__cached__': None, 'a': 1, 'b': 2, # 'func': <function func at 0x0000020287A71D08>}
(3)nonlocal
只能用于局部变量,找最近的上一层的局部变量
声明了nonlocal的内部函数的变量的修改,回影响到上一层局部变量的值
对全局无效,对局部最近的一层有影响
def outer(): a = 1 def inner(): a = 2 def inner2(): nonlocal a print(a) inner2() inner() outer() #2
7. 函数的嵌套
(1)嵌套调用
def max(a,b): return a if a>b else b print(max(5,2)) #5 def the_max(x,y,z): c = max(x,y) return max(c,z) print(the_max(1,5,3)) #5
(2)嵌套定义
a = 1 def outer(): #定义inner a = 1 def inner(): #定义inner1 b = 2 print(a) #1 print('inner') #inner def inner2(): #定义inner2 # global a #声明了一个全局变量a,只对函数外的全局变量有效 nonlocal a #声明了一个上层的局部变量a,只找上面第一层 a += 1 #没有上面两句中任意一句,则报错:不可变数据类型的修改 print(a,b) #2 2 print('inner2') #inner2 inner2() #调用inner2 print('###a###: ',a) ####a###: 2 inner() #调用inner1——若没有这句话来调用inner(),则运行不输出任何结果 print('***a***: ',a) #***a***: 2 outer() #调用inner print(a) #1
8. 闭包
(1)闭包定义
嵌套函数,且内部函数要调用外部函数的变量
def outer(): a = 1 def inner(): print(a) inner() outer()
(2)判断是否是闭包
def outer(): a = 1 def inner(): print(a) print(inner.__closure__) #是闭包:(<cell at 0x0000017AAEAC6678: int object at 0x000000005F8E60E0>,) outer() print(outer.__closure__) #不是闭包:None
(3)使用闭包的好处
在函数的外部使用内部函数
def outer(): a = 1 def inner(): print(a) return inner inn = outer() inn() #1
(4)闭包的应用
import urllib #模块 from urllib.request import urlopen def get_url(): url = 'http://www.baidu.com' def get(): ret = urlopen(url).read() print(ret) return get get_func = get_url() get_func()