一、认识函数

1 什么是函数
函数就是具备某一功能的工具


2 为什么要用函数
1 程序的组织结构不清晰,可读性差
2 代码冗余
3 可扩展性差

3 如何用函数
函数的使用必须遵循的原则:先定义,后调用
修理工事先准备工具的过程即函数的定义
修理工遇到应用场景拿来工具就用即函数的调用

语法:
def 函数名(参数1,参数2,...):
"""
文档注释
"""
code1
code2
code3
......
return 返回值

def:定义函数的关键字
函数名: 就相当于一个变量名,指向函数的内存地址,
注意:函数的内存地址()就可以出发函数体代码的执行

参数: 参数是函数的调用者为函数体代码传值的媒介,在python中函数的参数无需声明类型
"""文档注释""" : 推荐写上
代码块:就是函数体功能的具体实现
return 返回值 :函数体代码块运行的成果


函数的使用分为两个阶段:
定义阶段: 只检测语法,不执行代码
调用阶段:运行函数体代码
def foo():
xxx
print(

# 先定义

deffoo():print("fromfoo")

# 后调用

# 定义时无参,意味着调用时也无需传入参数

print(foo)foo()

# 先定义

defbar(x,y):print(x)print(y)

# 后调用

# 定义时无参,意味着调用时也必须传入参数

bar('a',2)

# 定义无参函数:当函数体的代码逻辑不依赖任何传入的值就能执行,就不需要定义参数

defprint_msg():print('='*50)print('welecome.....'.center(50,''))print('='*50)print_msg()print_msg()-----------------------------------------------------------------==================================================welecome.....====================================================================================================welecome.....==================================================

#定义有参函数:当函数体的代码逻辑依赖于外部调用者传入的值才能执行,必须定义参数用来接收外部传入的值

defmax2(x,y):#x=1#y=3ifx>y:print(x)else:print(y)max2(1,4)---------------------------------4

# 定义阶段

deffoo():print('fromfoo')bar()defbar():print('frombar')

# 调用阶段

foo()

# 定义阶段

deffoo():print('fromfoo')bar()

# 调用阶段

foo()

defbar():print('frombar')

二、定义函数的三种形式

1 无参函数

deffoo():print('fromfoo')foo()

2 有参函数

defbar(x,y):print(x,y)bar(1,2)

3 空函数

deffunc():pass

三、调用函数的三种形式

1 #语句形式

deffoo():print('fromfoo')foo()

2 表达式形式

deffoo(x,y):res=x+yreturnresres=foo(1,2)#表达式形式res1=foo(1,2)*100print(res1)

3 可以当作参数传给另外一个函数

defmax2(x,y):ifx>y:returnxelse:returnyres=max2(max2(1,2),3)print(res)

四、函数的返回值

#一 函数的返回值需要注意:
#1 返回值没有类型限制
#2 返回值没有个数限制
# 返回1个值:调用函数拿到的结果就是一个值
# 返回多个值:调用函数拿到的结果就是一个元组
# 返回0个值,或者不写return:调用函数拿到的结果就是None

#二 return关键字:return是函数结束的标志,函数内可以有多个return,但只要执行一次,整个函数就结束

deff1():print('first')return1print('second')return2print('third')return3res=f1()print(res)----------------------------------------first1deffoo():count=1whileTrue:print(count)ifcount==3:returncount+=1foo()------------------------------------------------------123defbar():passdeffoo():return[1,2],1,1.3,{'x':1},barres=foo()print(res)----------------------------------------------------------------([1,2],1,1.3,{'x':1},<functionbarat0x000002478D872EA0>)deffunc():print('fromfoo')#returnres=func()print(res)----------------------------------------------------------------------------fromfooNone

五、函数参数的使用

#函数的参数分为两大类:
#1 形参: 指的是在定义函数阶段括号内指定变量名,即形参本质就是"变量名"

#2 实参: 指的是在调用函数阶段括号内传入的值,即实参本质就是"值"

# 形参与实参的关系:在调用函数时,会将实参(值)赋值(绑定)给形参(变量名),
# 这种绑定关系在函数调用时临时生效,在调用结束后就失效了

deffoo(x,y):#x=1y=2#x=1#y=2print(x,y)foo(1,2)

# 形参与实参的具体分类
# 一 位置参数
# 1.1 位置形参: 在定义函数阶段按照从左到右的顺序依次定义的形参,称之为位置形参
# 注意:但凡按照位置定义的形参,必须被传值,多一个不行,少一个也不行(一一对应)

deffoo(x,y):print(x,y)foo(1,2)

# 1.2 位置实参: 在调用函数阶段按照从左到右的顺序依次传入的值,称之为位置实参
# 注意:但凡按照位置定义的实参,会与形参一一对应

二 关键字参数
#关键字实参: 在调用函数阶段,按照key=value的形式指名道姓地为形参传值
#注意:
#1. 可以完全打乱顺序,但仍然能指名道姓为指定的形参传值
#2. 可以混合使用位置实参与关键字实参,但是必须注意:
# 2.1 位置实参必须放到关键字实参前面
# 2.2 不能对一个形参重复赋值

deffoo(name,age):print(name,age)foo('egon',18)foo(18,'egon')foo(age=18,name='egon')foo('egon',age=18)

# 三 默认参数
#默认参数:指的是在定义函数阶段,就已经为某个形参赋值了,改形参称之为有默认值的形参,简称默认形参
#注意:
#1. 在定义阶段就已经被赋值,意味着在调用阶段可以不用为其赋值
#2. 位置形参应该放到默认形参前面
#3. 默认参数的值在函数定义阶段就已经固定死了
#4. 默认参数的值通常应该是不可变类型

deffoo(x,y=2):print(x,y)foo(1)foo(1,3)foo(y=3,x=1)----------------------------121313


-

m=10##必须要放在上面才可以被赋值到deffoo(x,y=m):print(x,y)m=20#定义时不能定义在函数后面,否则会报m没有定义foo(1)-------------------------110

defregister(name,hobby,l=[]):l.append(hobby)print('%s的爱好为%s'%(name,l))register('yxx','不洗澡')register('egon','read')-----------------------------yxx的爱好为['不洗澡']egon的爱好为['不洗澡','read']defregister(name,hobby,l=None):iflisNone:l=[]l.append(hobby)print('%s的爱好为%s'%(name,l))register('yxx','不洗澡',[])register('egon','read',[])--------------------------------------------------yxx的爱好为['不洗澡']egon的爱好为['read']

# 位置形参vs默认形参
# 对于大多情况下传的值都不相同的,应该定义成位置形参
# 对于大多情况下传的值都相同的,应该定义成默认形参

defregister(name,age,sex='男'):print(name,age,sex)register('李铁蛋',18,)register('刘二丫',19,'女')

# 四 可变长度的参数
# 站在实参的角度,参数长度可变指的是在调用函数时,传入的实参值的个数不固定
#而实参的定义方式无法两种:位置实参,关键字实参,对应着形参也必须有两种解决方案*与**,类分别应对溢出的位置实参与关键字实参

#1. 在形参中带*:会将调用函数时溢出位置实参保存成元组的形式,然后赋值*后的变量名

deffoo(x,y,*z):#z=(3,4,5,6)print(x,y,z)foo(1,2,3,4,5,6)

# 2. 在实参中带*: 但凡在实参中带*星的,在传值前都先将其打散成位置实参,再进行赋值

deffoo(x,y,*z):#z=(3,4,5,6)print(x,y,z)foo(1,*[2,3,4,5,6])#foo(1,2,3,4,5,6)#deffoo(x,y,z):print(x,y,z)foo(*(1,2,3))#foo(1,2,3)foo(*'abc')#foo('a','b','c')

# 3. 在形参中带**:会将调用函数时溢出关键字实参保存成字典的形式,然后赋值**后的变量名

deffoo(x,y,**z):#z={'z':3,'a':1,'b':2}print(x,y,z)foo(1,y=2,a=1,b=2,c=3)foo(1,2,**{"r":4,'g':9})#正确的------------------------------------------12{'a':1,'b':2,'c':3}12{'r':4,'g':9}# 错误方式:#foo(1,2,{"r":4,'g':9})#错误的#不能直接接收以字典的形式,可以是以关键字传入例如:(a=1,b=2),或是将字典的形式打散例如:**{"a":1,'b':2}

# 4. 在实参中带**: 但凡在实参中带**星的,在传值前都先将其打散成关键字实参,再进行赋值

deffoo(x,y,**z):#z={'a':100,'b':200}print(x,y,z)foo(1,**{'a':100,'b':200,'y':111})#foo(1,b=200,a=100,y=111)---------------------------1111{'a':100,'b':200}deffoo(x,y,z):print(x,y,z)foo(**{'y':111,'x':222,'z':333})#foo(z=333,x=222,y=111)------------------------------------222111333

#5. 规范: 在形参中带*与**的,*后的变量名应该为args,**后跟的变量名应该时kwargs

deffoo(*args,**kwargs):#args=(1,2,3,4,5)kwargs={'a':1,'b':2,'c':3}print(args)print(kwargs)foo(1,2,3,4,5,a=1,b=2,c=3)------------------------------------(1,2,3,4,5){'a':1,'b':2,'c':3}

注意:

!!!!!!!!!!!当我们想要将传给一个函数的参数格式原方不动地转嫁给其内部的一个函数,应该使用下面这种形式

defbar(x,y,z):print(x,y,z)defwrapper(*args,**kwargs):#args=(1,2)kwargs={'z':3}bar(*args,**kwargs)#bar(*(1,2),**{'z':3})#bar(1,2,z=3)wrapper(1,2,z=3)# 虽然调用的是wrapper,但是要遵循的确是bar的参数标准---------------------------------123

五 命名关键字参数: 放到*与**之间的参数称之为命名关键字参数
# 注意: 命名关键字参数必须按照key=value的形式传值

deffoo(x,y,*args,m,n,**kwargs):#args=(3,4,5,6,7,8)print(x,y)#1,2print(args)#(3,4,5,6,7,8)print(m,n)#222,333print(kwargs)foo(1,2,3,4,5,6,7,8,n=333,m=222,a=1,b=2)--------------------------------12(3,4,5,6,7,8)222333{'a':1,'b':2}deffoo(*,x=1,y):print(x)print(y)foo(y=2222,x=1111)foo(y=2222)-----------------------------------1111222212222deffoo(x,y=1,*args,m,**kwargs):print(x)print(y)print(args)print(m)print(kwargs)foo('fromx',ars='tuple',m='fromm',kw='fromkw',ar='args')-----------------------------------------fromx1()fromm{'ars':'tuple','kw':'fromkw','ar':'args'}

作业:

默写:函数中参数的分类及其作用位置关键字形参实参可变长度参数分类:形参,实参作用:位置:位置形参,位置实参关键字:关键字实参:在调用函数阶段,按照key=value的形式指名道姓地为形参传值形参:指的是在定义函数阶段括号内指定变量名,即形参本质就是"变量名"实参:指的是在调用函数阶段括号内传入的值,即实参本质就是"值"可变长度参数:站在实参的角度,参数长度可变指的是在调用函数时,传入的实参值的个数不固定,而实参的定义方式无法两种:位置实参,关键字实参,对应着形参也必须有两种解决方案*与**,类分别应对溢出的位置实参与关键字实参练习题:1.使用函数新的知识点继续完善,优化购物车2.使用函数完成以下功能,数据格式如下[{“name”:”张无忌”,”number”,”sh01”,”math”:90,”english”:87,”chinese”:56},{“name”:”武则天”,”number”,”sh02”,”math”:40,”english”:97,”chinese”:67}....]提供以下功能函数获取指定学生的成绩获取指定学号的成绩根据学生的学号修改姓名根据姓名修改指定学科的成绩删除指定学生及其成绩3.博客作业http://www.cnblogs.com/linhaifeng/articles/7531972.html#_label5拓展:ATM参考博客

六、函数对像

1、函数是第一类对象: 指的是函数名指向的值(函数)可以被当作数据去使用

deffunc():#func=函数的内地址print('fromfunc')print(func)#打印出func=函数的内地址#1.可以被引用age=10x=ageprint(x,age)f=funcprint(f)f()#2.可以当作参数传给另外一个函数defbar(x):print(x)bar(age)bar(func)#3.可以当作一个函数的返回值defbar(x):returnxres=bar(age)print(res)res=bar(func)print(res)#4.可以当作容器类型的元素l=[age,func,func()]print(l)


-------------------------------------------------------------------------------------------------------------------------------------------

deflogin():print('login')defregister():print('register')defshopping():print('shopping')defpay():print('pay')deftransfer():print('transfer')func_dic={'1':login,'2':register,'3':shopping,'4':pay,'5':transfer}print(func_dic)msg="""0退出1登陆2注册3购物4支付5转账"""whileTrue:print(msg)choice=input('请输入您的操作:').strip()ifchoice=='0':breakifchoice=='1':login()elifchoice=='2':register()elifchoice=='3':shopping()elifchoice=='4':pay()else:print('输入错误指令,请重新输入')ifchoiceinfunc_dic:func_dic[choice]()else:print('输入错误指令,请重新输入')#拆分:#print(func_dic["1"]())#或者#f=func_dic["1"]#f()

七、函数嵌套

# 函数嵌套分为两大类

# 一: 函数的嵌套调用:在调用一个函数过程中,其内部代码又调用了其他函数

defbar():print('frombar')deffoo():print('fromfoo')bar()foo()--------------------------------------------------------------------defmax2(x,y):ifx>y:returnxelifx<y:returnyelse:returny==x#print(max2(3,3))defmax4(a,b,c,d):res1=max2(a,b)res2=max2(res1,c)res3=max2(res2,d)returnres3print(max4(1,2,3,4)

#二:函数的嵌套定义:一个函数内部又定义了另外一个函数

defoutter():x=1print('fromoutter')definner():print('frominner')print(x)print(inner)inner()outter()------------------------------------------------------------fromoutter1<functionoutter.<locals>.innerat0x000001E5234C5510>frominner

deff1():print('fromf1')deff2():print('fromf2')deff3():print('fromf3')f3()f2()f1()-------------------------------------------fromf1fromf2fromf3

frommathimportpiprint(pi)defcircle(radius,action=0):"""圆形相关运算:paramradius:半径:paramaction:0代表求面积,1代表求周长:return:面积或者周长"""defarea(radius):returnpi*(radius**2)defperimiter(radius):return2*pi*radiusifaction==0:res=area(radius)elifaction==1:res=perimiter(radius)returnresprint(circle(10,0))print(circle(10,1))-----------------------------------------------3.141592653589793314.159265358979362.83185307179586


八、名称空间与作用域

1 什么是名称空间namespace
名称空间就是用来存放名字与值内存地址绑定关系的地方(内存空间)

age=18
但凡查找值一定要通过名字,访问名字必需去查找名称空间


2 名称空间分为三大类
内置名称空间:存放的是python解释器自带的名字
例如:
len
max
print

全局名称空间:存放的是文件级别的名字
例如:以下名字中x\y\z\b\foo都会存放于全局名称空间中
x=1
y=2
if x == 1:
z=3

while True:
b=4
break

def foo():
m=3

局部名称空间:在函数内定义的名字
例如:
foo内的m这个名字一定是存放于一个局部名称空间中


生命周期:
内置名称空间:在解释器启动时则生效,解释器关闭则失效
全局名称空间:在解释器解释执行python文件时则生效,文件执行完毕后则失效
局部名称空间:只在调用函数时临时产生该函数的局部名称空间,改函数调用完毕则失效

加载顺序:
内置->全局->局部
调用顺序:(从当前往外找,以下顺序不变)
局部-> 全局-> 内置
查找名字的顺序:
基于当前所在位置往上查找
假设当前站在局部,查找顺序:局部->全局->内置
假设当前站在全局,查找顺序:全局->内置

# 案列一:

len=111deffoo():len=222print(len)foo()print('站在全局找len:',len)--------------------------------------222站在全局找len:111

x=111deff1():x=222deff2():deff3():x=444print(x)x=333f3()f2()f1()--------------------------------444

# (******)名字的查找顺序,在函数定义阶段就已经固定死了(即在检测语法时就已经确定了名字的查找顺序),与函数的调用位置无关,也就是说无论在任何地方调用函数,都必须回到当初定义函数的位置去确定名字的查找关系#

# 案列二:

x=111defouter():definner():print('frominner',x)#x访问的是全局名称空间中xreturninnerf=outer()print(f)x=222#因为代码是从上往下读,而在最后面的x=222时是因为先读到x=111,后面再读到x=222所以最终取得的结果是print('frominner',x)>>>frominner222f()---------------------------------------------------<functionouter.<locals>.innerat0x00000204E1D45510>frominner222

# 案列三:

x=111defouter():definner():print('frominner',x)#x访问的时全局名称空间中xreturninnerf=outer()#x=222deffunc():x=333#与函数的调用位置无关f()x=444func()----------------------------------------------frominner444

# 案列四:

#都不对x=111defouter():definner():x=11111#先定义,后调用print('frominner',x)#x是来自于当前层的名字,能找到,但是先现定义,后调用x=2222222222#语法错误,而这里是x没有定义,后面再赋值,所以是语法错误returninnerf=outer()f()

# 作用域:
#域指的是范围,作用域指的就是作用的范围,分为两种
# 全局作用域:包含的是内置名称空间与全局名称空间中的名字
# 特点:全局有效,全局存活

# 局部作用域:包含的是局部名称空间中的名字
# 特点:局部有效,临时存活

x=1deffoo():y=2print(x)defbar():print(x)foo()bar()-----------------------------11

# global,nonlocal

x=1deffoo():x=2foo()print(x)--------------------1

l=[]deffoo():l.append(1)#l=33333foo()print(l)-------------------------------UnboundLocalError#(错误的云地方)

#global:在局部声明一个名字是来自于全局作用域的,可以用来在局部修改全局的不可变类型

x=1deffoo():globalxx=2foo()print(x)---------------------------2

#nonlocal:声明一个名字是来自于当前层外一层作用域的,可以用来在局部修改外层函数的不可变类型

x=0deff1():x=111deff2():#globalxnonlocalxx=222f2()print(x)f1()print(x)------------------------------2220

九、闭包函数

1 什么是闭包函数
闭:指的是闭包函数是定义在一个函数内部的函数
包:该内部函数包含对外层函数作用域名字的引用

需要结合函数对象的概念将闭包函数返回到全局作用域去使用,从而打破函数的层级限制
def outter():
x=111
def inner():
print(x)
return inner

f=outter() #f=outter内的inner

# x=22222
# f()
def func():
x=3333
f()

func()
2 为何要用闭包函数
闭包函数提供了一种为函数体传值的解决方案

3 如何用闭包函数

# 为函数体传值的方式一:参数

deffunc(x,y):print(x+y)func(1,2)-------------------3

# 为函数体传值的方式二:闭包

defoutter(x,y):#x=1#y=2deffunc():print(x+y)returnfuncf=outter(1,2)f()------------------3

# 解决方案一:参数

# pip3 install requests

importrequestsdefget(url):response=requests.get(url)ifresponse.status_code==200:print(len(response.text))get('https://www.baidu.com')get('https://www.baidu.com')get('https://www.baidu.com')get('https://www.tmall.com')get('https://www.tmall.com')url1='https://www.baidu.com'url2='https://www.tmall.com'get(url1)get(url1)get(url1)get(url2)get(url2)get(url2)

# 解决方案二:闭包

importrequestsdefoutter(url):#url='https://www.baidu.com'defget():response=requests.get(url)ifresponse.status_code==200:print(len(response.text))returngetbaidu=outter('https://www.baidu.com')baidu()baidu()baidu()tmall=outter('https://www.tmall.com')tmall()tmall()tmall()