三、函数
目录
一、理解函数
二、函数的定义
三、函数的调用
四、函数的返回值return
五、函数的参数
六、函数嵌套
七、名称空间与作用域
八、函数对象
九、闭包函数
十、装饰器
十一、匿名函数
十二、内置函数
一、理解函数
1.为什么要用函数
#1.程序的组织结构不清晰,可读性差 #2.代码冗余 #3.程序的可扩展性极差
2.什么是函数
#1.具备某一功能的工具就是程序中的函数 #2.事先准备工具的过程称之为函数的定义 #3.遇到应用场景“拿来就用”就是函数的调用 ##综上所述,在程序中,函数的使用必须遵循:先定义,在调用
3.怎么用函数
name_from_db='egon' pwd_from_db='123' def auth(): name=input('用户名>>: ').strip() pwd=input('密码>>: ').strip() if name == name_from_db and pwd == pwd_from_db: print('login successfull') else: print('user or pwd error') auth()
二、函数的定义
# def 函数名(参数1,参数2,...): # """ # 函数的文档注释 # :param 参数1: 参数1的作用 # :param 参数2: 参数1的作用 # :return: 返回值的描述 # """ # 代码1 # 代码2 # 代码3 # .... # return 返回值
2.1 语法:在函数定义阶段只检测语法,不执行代码
# def foo(): # if #错误语法
2.2 先定义后调用
#定义阶段 def bar(): print('from bar') def foo(): print('from foo') bar() #调用阶段 foo()
2.3 定义函数的三种形式
#1.无参函数 def foo(): print ('from foo') foo() #使用场景: #需求:用户在选择界面选择不同ATM的功能,进入对应的函数(简单实现) def repay(): print ('我是支付功能') def check(): print ('我是查询功能') def transfer(): print ('我是转账功能') def run(): for i in func_dic: print (i ,func_dic[i][0]) choice=input('>>>:').strip() func_dic[int(choice)][1]() #执行对应的功能函数 func_dic={ 1:['支付',repay], 2:['查询',check], 3:['转账',transfer], } if __name__ =="__main__": run()
#2.有参函数 def bar(x,y): print (x,y) bar(1,2) #举例 #需求:比较俩个数大小 def max2(x,y): if x >y: print (x) else: print (y) max2(20,30)
#3.空函数 def foo(x,y): pass #使用场景:当写项目时,有了整体框架,但是还没细写功能,可先定义空函数
三、函数的调用
三种调用方式:
3.1语句形式
def foo(): print ('from foo') foo() #语句形式
3.2表达式形式
def max2(x,y): if x>y: return x else: return y salary= max2(3000,2000) #表达式形式 annual_salary = salary * 12 print (annual_salary)
3.3返回值当做参数传给另一个函数
#找出一个1,2,3最大的值 def max2(x,y): if x>y: return x else: return y res= max2(max2(3,2),1) #函数返回值当参数传递 print (res)
四、函数的返回值return
4.1特点:
return 是函数结束的标志,函数内可以有多个return,但是只要执行一个,函数就立即结束,并且把return后的值当做本次调用的结果
def foo(): print ('first') return 1 print ('second') #以下语句都不会被执行 return 2 print ('third') return 3 res = foo() print (res)
4.2 返回值注意事项:
#1.返回值没有类型限制 def bar(): return {'x',1} #可以为任意类型,如字符串,字典,函数,列表等等 print (bar()) #2. 返回值没有个数限制,可以用逗号分开多个值,一次返回 def bar(): return (1,'hello',{'x':1}) #以元组tuple形式返回 x,y,z = bar() print (x,y,z)
#3.可以没有return,默认返回None def bar(): pass res = bar() #无返回值,返回None print (res)
五、函数的参数
5.1 函数的参数分为两大类:形参和实参
#形参:指的是在定义函数时括号定义的参数,形参即变量名 def foo(x,y): #x=1,y=2 print (x,y) #实参:指的是在调用函数时括号内传入的值,实参即变量值 foo(1,2) #在调用时,实参的值会传给形参,可以理解为这是一个赋值操作
5.2 函数参数
5.2.1位置参数
#1.在定义函数时,按照从左到右的顺序依次定义的参数,称为位置形参 #特性:位置形参必须被传值,多一个不行少一个也不行 def foo(x,y,z): print (x,y,z) #2.在调用函数时,按照从左到右的顺序依次传入的值,称为位置实参 #特点:与形参一一对应 foo(1,2,3) foo(1,2) #报错 foo(1,2,3,4) #报错
5.2.2关键字实参
#1.在调用函数时,按照key=value的形式定义的实参,称为关键字参数 #特点:可以完全打乱顺序,但仍然能指名道姓为指定的参数传值 def foo(x,y,z): print (x,y,z) foo(z=3,x=1,y=2,) #2.位置实参与关键字实参可以混合使用,规则 ##2.1位置参数一定放在关键字参数的前面 ##2.2同一个形参只能被赋值一次 foo(1,2,z=3) #正常 foo(1,y=2,3) #报错,不符合2.1 foo(1,y=2,x=4) #报错,不符合2.2
5.2.3默认参数
#默认参数:在定义函数时,就已经被赋值的参数,称为默认形参 #有以下三个特征: #1.在定义阶段就已经被赋值,意味着在调用阶段就可以不用传值 def foo(x,y=10): print (x,y) foo(1) foo(1,3) foo(y=4,x=1) #2.默认参数必须跟在位置形参的后面 def foo(y=1,x): #定义函数阶段就会报错 pass #3*.默认参数的值只在定义阶段被赋值一次就固定死了,定义之后改变没有影响 m=10 def func(x,y=m): #定义阶段10赋值为y print (y) m=111 func(1) #4*.默认参数的值应该设置成不可变类型 #举例:如果想要将每个人的爱好存到各自的列表中,就不能将默认参数l设置成列表 #错误做法:不会报错,需求达不到预期 def func(name,hobby,l=[]): #['read','play'] l.append(hobby) print (name.l) func('lisl','read') func('zhangsan','play') func('lixi','eat') #正确做法 def func(name,hobby,l=None): if l is None: l=[] l.append(hobby) print(name,l) func('lisl','read') func('zhangsan','play') func('lixi','eat') #位置形参与默认参数的应用: ##1 大多数场景值都固定不变则需要定义成默认参数 ##2 大多数场景值需要改变则需要定义成位置形参 #eg1: def register(name,password,gender='male'): print(name) print(password) print(gender) register('lisl','123',) register('xiaohong','123','female')
5.2.4 可变长参数
#指的是在调用函数时,传入的实参个数可以不固定 #而实参无非两种形式:1 位置实参 2.关键字实参 #所以对应的形参也必须对应俩种解决方案,专门用于接收溢出位置实参和溢出的关键字实参 #*:接收溢出的位置实参,存在元组形式,然后赋值给*后面跟的那个变量名 ##用法1:在形参中用* def foo(x,y,*z): #z=(3,4,5) print (x,y) print (z) foo(1,2,3,4,5) foo(1,2,3,4,5,y=20) #报错,2已经传给y,y=20重复赋值报错 ##用法2:在实参中用* def foo(x,y,*z): print (x,y) print (z) foo (1,2,*(3,4,5)) #foo(1,2,3,4,5) 实参*是拆分元组元素 foo(1,2,*'abc') #foo(1,2,'a','b','c') #**:接收溢出关键字实参,存在字典形式,然后赋值给**后面跟的那个变量名 ##用法1:在形参中用** def foo(x,y,**z): #z={'b': 3, 'c': 4, 'a': 2} print (x,y) print (z) foo(1,a=2,b=3,c=4,y=5) ##用法2:在实参中用** def foo(x,y,**z): print (x,y) print (z) foo(1,**{'a':2,'c':3}) #foo(1,a=2,c=3) 实参**是拆分字典的key=value为关键字参数
六、函数嵌套
#1.函数的嵌套调用 def max2(x,y): if x >y: return x else: return y def max4(x,y,m,n): res1=max2(x,y) res2=max2(res1,m) res3 =max2(res2,n) return res3 print (max4(1,2,3,4)) #2.函数的嵌套定义 def f1(): print ('from f1') def f2(): print ('from f2') def f3(): print ('from f3') f3() f2() f1()
七、名称空间与作用域
7.1什么是名称空间
#存放名字与值绑定关系(内存地址)的地方 问:如何查看名称空间的值 答:dir(),将dir()放置在全局可查看全局名称空间的值,放在局部可查看局部名称空间的值,要放在最后一行。dir()不加参数,加了参数则查的是对应对象的所有属性 __dict__与dir()的区别: #1.dir()是一个函数,返回的是list #2.__dict__是一个字典,键为属性名,值为属性值 #3.dir()用来寻找一个对象的所有属性,不加参数代表是所在空间的名称,包括__dict__中的属性,__dict__是dir()的子集
7.2名称空间的分类
#1.内置名称空间 存放python解释器自带名字,比如内置的函数名:len,max,sum 创建:随着python解释器启动而创建 销毁:随着python解释器关闭而销毁 #2.全局名称空间 存放文件级别的名字,比如x,f1,z x=1 def f1(): #定义阶段f1也属于全局名称空间 y=w 创建:文件开始执行时则立即创建 销毁:文件开始执行完毕时销毁 #3.局部名称空间 存放函数内的名字,强调函数的参数也属于局部的 创建:函数执行时才临时创建 销毁:函数执行完毕则立即销毁 def f1(): x=1 #局部名称空间 y=2 f1()
7.3名称空间的加载顺序
内置名称空间---》全局名称空间---》局部名称空间
注意:加载的目的是为了把名字存起来,然而存起来的目的就是为了取,
那么但凡查找一个名字一定会从三种名称空间之一找到
7.4名称空间的查找名字顺序
局部名称空间---》全局名称空间---》内置名称空间 len =10 def f1(): len =100 def f2(): len =100 def f3(): len=10000 print (len) f3() f2() f1() #结果10000
7.5 特性:名字的查找关系是在函数定义阶段就已经固定死的,与调用位置无关
x =100 def f1(): print (x) def f2(): x=1111 f1() f2() #结果为100
7.6 作用域:域=范围
#1.全局范围:内置名称空间中的名字,全局名称空间的名字 #特点:全局有效,全局存活 #2.局部范围:局部名称空间中的名字 #特点:局部有效,临时存活 #定义在全局作用域的名字称为全局变量 #定义在局部作用域的名字称为局部变量 len=10 #全局变量 def f2(): print (len) #局部变量 def f3(): print (len) #局部变量 f3() ############################## x=10 print (globals()) #查看全局作用域中的名字,结果包含'x':10 print (locals() is globals()) #结果:True,在全局作用域的局部变量就是全局变量 def f1(): y = 2 z = 3 print (locals()) #结果{'z': 3, 'y': 2},为函数内的局部变量 f1()
7.7 global与nonlocal 关键字
#一般情况下函数里的局部变量是无法影响到全局变量的,除非全局变量是可变类型,如:列表,字典,集合 #例1: s='hello' #不可变类型有数字,字符串,元组 def foo(): s='hello world' foo() print (s) #结果‘hello’ #例2: l=[] #空列表 def foo(): l.append('aa') foo() print (l) #结果['aa'] #global #特殊情况下,需要将局部变量定义成全局变量(不建议) x=10 def foo(): global x x=100 foo() print (x) #结果100 #nonlocal #定义:会从当前外一层开始查找一直找到最外层的函数,如果没有就报错 def f1(): x=10 def f2(): def f3(): nonlocal x x=11 f3() f2() print (x) #结果11 f1() #print (x) #报错,因为x变量全局没有定义
八、函数对象
8.1 函数对象定义:
#函数可以当做变量去处理
8.2函数对象的用法:
#1.可以被赋值 def foo() print ('from foo') f=foo print (f) #打印的是函数的内存地址 #2.可以当做参数传给一个函数 def foo(): print ('from foo') def bar(func): print (func) #打印函数地址 func() #执行函数 bar(foo) #3.可以当做函数的返回值 def foo(): print ('from foo') def bar(func): return func f=bar(foo) #返回函数foo的内存地址赋值给f print (f) #4.可以当做容器类型的元素 def foo(): print ('from foo') l=[foo,] print (l) l[0]() #执行foo函数 #需求1:打造用户选择界面 def get(): print ('使用下载功能') def put(): print ('使用上传功能') def ls(): print ('显示当前列表') def login(): pirnt ('用户登录') func_dic={ "1":[get,'下载'], "2":[put,'上传'], "3":[ls,'浏览'], "4":[login,'登录'], } for i,k in func_dic.items(): print (int(i),' ',k[1]) choice=input('>>:') if choice =='q':break if choice in func_dic: func_dic[choice][0]()
九、闭包函数
闭包函数就是:函数嵌套+名称空间与作用域+函数对象
9.1 什么是闭包函数
#1.定义在函数的内函数 #2.该函数体代码包含对该函数外层作用域中名字的引用 强调:函数外层指的不是全局作用域 #满足上述两个条件,那么该内部函数就称之为闭包函数 #例子: def outter(): x=1 def inner(): print (x) return inner #利用函数对象的概念,将一个内部函数返回并在全局中拿到使用,从而打破函数的层级限制 f=outter() #f=inner print (f) f()
9.2 为函数体传值的方式
#方式一:以参数的形式为函数体传值 import requests #pip3 install requests def get(url): response=requests.get(url) if response.status_code ==200: print (response.text) get('https://www.python.org') #每次都需要将url传进函数 get('https://www.python.org') #方式二:包给函数,(方便,推荐) def outter(url): def get(): response=requests.get(url) if response.status_code ==200: print (response.text) return get python = outter('https://www.python.org') #相同网站只需一次传值 python() python()
十、装饰器
10.1 开放封闭原则
#软件一旦上之后就应该开放封闭原则 #具体是指修改是封闭的,但对扩展是开放的
10.2 什么是装饰器
#装饰就是修饰,器指的就是工具 #装饰器本身可以是任意可调用的对象 #被装饰的对象也可以是任意可调用的对象 装饰器----》函数 被装饰的对象----》函数 #装饰器是用来为被装饰对象添加新功能的一种工具 #必须遵循: 1.不能修改被装饰对象的源代码 2.不能修改被装饰对象的调用方式
10.3 举例
import time def index(): time.sleep(1) print ('welcome to index') def outter(func): def wrapper(): start_time = time.time() func() stop_time = time.time() print ('run time is %s'%(stop_time-start_time)) return wrapper index = outter(index) #index=wrapper index()
import time def index(): time.sleep(1) print ('welcome to index') return '我是返回值' #定义有返回值 def outter(func): def wrapper(): start_time=time.time() res = func() stop_time = time.time() print ('run time is %s'%(stop_time-start_time)) return res #定义有返回值 return wrapper index=outter(index) #index=wrapper res=index() #res=wrapper() print ('返回值:',res)
def home(name): time.sleep(1) print ('welcome %s to home page'%name) def outter(func): def wrapper(*args,**kwargs): start_time = time.time() res = func(*args,**kwargs) stop_time=time.time() print ('run time is %s'%(stop_time-start_time)) return res return wrapper home=outter(home) #home=wrapper home('lisl') #wrapper('lisl')
10.4装饰器语法糖
#在被装饰器对象正上方单独一行写@装饰器的名字 def timmer(func): def wrapper(*args,**kwargs): start_time = time.time() res=func(*args,**kwargs) stop_time = time.time() print ('run time is %s'%(stop_time-start_time)) return res return wrapper @timmer def index(): time.sleep(1) print ('welcome to index') return 1234 @timmer def home(name): time.sleep(2) print ('welcome %s to home page'%name) index() home('lisl')
#例子:检测用户是否登录,没有要求登录在使用功能 import time current_user = {'login':False} def outter(func): def wrapper(*args,**kwargs): if current_user['login']: return func(*args,**kwargs) user=input('username>>>:').strip() pwd = input('password>>>:').strip() if user =='lisl' and pwd=='123': current_user['login']=True return func(*args,**kwargs) return wrapper @outter def index(): time.sleep(1) print ('welcome to index') return 1234 @outter def home(name): time.sleep(2) print ('welcome %s to home page'%name) index() home('lisl')
10.5 有参装饰器
import time current_user={'login':False} def auth(engine): def outter(func): def wrapper(*args,**kwargs): if current_user['login']: return func(*args,**kwargs) user=input('username>>>:').strip() pwd =input('password>>>:').strip() if engine =='file': if user =='lisl' and pwd=='123': current_user['login'] =True return func(*args,**kwargs) elif engine =='mysql': print ('基于mysql认证') elif engine =='ldap': print ('基于ldap的认证方式') return wrapper return outter @auth(engine='mysql') #@outter #index=outter(index) def index(): time.sleep(1) print ('welcome to index') return 1234 @auth(engine=='ldap') def home(name): time.sleep(2) print ('welcome %s to home page'%name) index() home('lisl')
十一、匿名函数
11.1 匿名函数格式
#匿名函数 lambda x,y:x+y #普通函数 def func(x,y): return x+y
11.2 使用匿名函数注意事项
#1.不会单独使用,会与其他函数配合使用 #2.匿名函数的精髓在于没有名字,如果没有名字意味用一次就立即回收,所以,匿名函数的应用仅应用于值使用一次的场景
11.3 与匿名函数配合较多的内置函数:max,min,sorted,filter,map
salaries={ '唐三':2000, '王东':40000, '张三':3000, '戴沐白':2500, } #求出最大工资的那个人名 print('工资最多的人:',max(salaries,key=lambda x:salaries[x])) #求出工资最低的人名 print ('工资最少的人:',min(salaries,key=lambda x:salaries[x])) #按照薪资排序,从小到大排 print(' < '.join(sorted(salaries,key=lambda x:salaries[x]))) #需求:将每个元素都加上"_唐门" #方法一:map与lambda配合使用: names=['唐三','小舞','唐昊','胖子','戴沐白'] g=list(map(lambda x:x+'_唐门',names)) #map()返回的结果是一个迭代器,(第四章会学) print (g) #方法二: names=['唐三','小舞','唐昊','胖子','戴沐白'] names=[x+'_唐门' for x in names] print (names) #需求:将列表里的_唐门去除掉 #方法一:filter与lambda配合使用 names=['唐三_唐门', '小舞_唐门', '唐昊_唐门', '胖子_唐门', '戴沐白'] # filter会得到names的迭代器对象obj,然后next(obj)将得到的值传给filter第一个参数指定的函数,将函数返回值为True的那个值留下 g=filter(lambda x:x.endswith('_唐门'),names) print (list(g)) #方法二:列表生成式(第四章会学) names=['唐三_唐门', '小舞_唐门', '唐昊_唐门', '胖子_唐门', '戴沐白'] names=[x for x in names if x.endswith('_唐门')] print (names)
十二、内置函数
12.1 isinstance(obj,cls) 判断类型
l=list([]) print (isinstance(l,list)) #判断l是否是列表
12.2 issubclass(sub,super) #判断子类
class Father: #父类 pass class Children(Father): #子类 pass class Grandson(Children): #孙类 pass print (issubclass(Grandson,Father)) #孙类是父类的子类,返回True