python函数
本文目录
函数,即具备某一功能的工具。
函数的存在意义:程序的组织结构不清晰,可读性差;代码冗余;可扩展性差
使用函数必须遵循的规则:先定义,后调用
def 函数名(参数1,参数2,...): """ 文档注释 """ code1 code2 code3 ...... return 返回值 def:定义函数的关键字 函数名: 就相当于一个变量名,指向函数的内存地址, 注意:函数的内存地址()就可以出发函数体代码的执行 参数: 参数是函数的调用者为函数体代码传值的媒介,在python中函数的参数无需声明类型 """文档注释""" : 推荐写上 代码块:就是函数体功能的具体实现 return 返回值 :函数体代码块运行的成果
定义无参函数:当函数体代码逻辑不依赖任何传入的值就能执行,就不需要定义函数
定义有参函数:当函数体的代码逻辑依赖于外部调用者传入的值才能执行,必须定义参数用来接收外部传入的值
#1.1 无参函数 # def foo(): # print('from foo') # foo() #1.2 有参函数 # def bar(x,y): # print(x,y) # bar(1,2) #1.3 空函数 # def func(): # pass
#2.1 #语句形式 # def foo(): # print('from foo') # foo() #2.2 表达式形式 # def foo(x,y): # res = x + y # return res # # res=foo(1,2) #表达式形式 # res1=foo(1,2)*100 # print(res1) #2.3 可以当作参数传给另外一个函数 # def max2(x,y): # if x > y: # return x # else: # return y # 1 2 3 # res=max2(max2(1,2),3) # print(res)
函数的返回值需要注意:
1 返回值没有类型限制
2 返回值没有个数限制
返回1个值:调用函数拿到的结果就是一个值
返回多个值:调用函数拿到的结果就是一个元组
返回0个值,或者不写return:调用函数拿到的结果就是None
return关键字:return是函数结束的标志,函数内可以有多个return,但只要执行一次,整个函数就结束
1 形参: 指的是在定义函数阶段括号内指定变量名,即形参本质就是"变量名"
2 实参: 指的是在调用函数阶段括号内传入的值,即实参本质就是"值"
形参与实参的关系:在调用函数时,会将实参(值)赋值(绑定)给形参(变量名),
这种绑定关系在函数调用时临时生效,在调用结束后就失效了
# 形参与实参的具体分类 # 一 位置参数 # 1.1 位置形参: 在定义函数阶段按照从左到右的顺序依次定义的形参,称之为位置形参 # 注意:但凡按照位置定义的形参,必须被传值,多一个不行,少一个也不行 def foo(x,y): print(x,y) foo(1,2) # 1.2 位置实参: 在调用函数阶段按照从左到右的顺序依次传入的值,称之为位置实参 # 注意:但凡按照位置定义的实参,会与形参一一对应 def foo(x,y): print(x,y) foo(2,1) #自我总结:位置实参是传入的值,位置形参是参数变量名 # 二 关键字参数 #关键字实参: 在调用函数阶段,按照key=value的形式指名道姓地为形参传值 #注意: #1. 可以完全打乱顺序,但仍然能指名道姓为指定的形参传值 #2. 可以混合使用位置实参与关键字实参,但是必须注意: # 2.1 位置实参必须放到关键字实参前面 # 2.2 不能对一个形参重复赋值 def foo(name,age): print(name,age) foo('yb',18) foo(18,'yb') foo(age=18,name='yb') foo('yb',age=18) foo(name='yb',18)#错 foo('yb',age=18,name='zs')#错 # 三 默认参数 #默认参数:指的是在定义函数阶段,就已经为某个形参赋值了,改形参称之为有默认值的形参,简称默认形参 #注意: #1. 在定义阶段就已经被赋值,意味着在调用阶段可以不用为其赋值 #2. 位置形参应该放到默认形参前面 #3. 默认参数的值在函数定义阶段就已经固定死了 #4. 默认参数的值通常应该是不可变类型 def foo(x,y=2): print(x,y) foo(1) foo(1,3) foo(y=3,x=1) def foo(y=2,x): print(x,y) m=10 def foo(x,y=m): print(x,y) m=20 foo(1) #1 10 # 位置形参vs默认形参 # 对于大多情况下传的值都不相同的,应该定义成位置形参 # 对于大多情况下传的值都相同的,应该定义成默认形参 # 四 可变长度的参数 # 站在实参的角度,参数长度可变指的是在调用函数时,传入的实参值的个数不固定 # 而实参的定义方式无法两种:位置实参,关键字实参,对应着形参也必须有两种解决方案*与**,类分别应对溢出的位置实参与关键字实参 # 1. 在形参中带*:会将调用函数时溢出位置实参保存成元组的形式,然后赋值*后的变量名 def foo(x,y,*z): #z=(3,4,5,6) print(x,y,z) foo(1,2,3,4,5,6) # 2. 在实参中带*: 但凡在实参中带*星的,在传值前都先将其打散成位置实参,再进行赋值 def foo(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) def foo(x,y,z): print(x,y,z) foo(1,*(2,3,4,5,6)) #foo(1,2,3,4,5,6) foo(*(1,2,3)) #foo(1,2,3) foo(*'hello') #foo() foo(*'abc') #foo('a','b','c') # 3. 在形参中带**:会将调用函数时溢出关键字实参保存成字典的形式,然后赋值**后的变量名 def foo(x,y,**z): #z={'z':3,'a':1,'b':2} print(x,y,z) foo(1,y=2,a=1,b=2,c=3) # 4. 在实参中带**: 但凡在实参中带**星的,在传值前都先将其打散成关键字实参,再进行赋值 def foo(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) def foo(x,y,z): print(x,y,z) foo(**{'y':111,'x':222,'z':333}) #foo(z=333,x=222,y=111) #5. 规范: 在形参中带*与**的,*后的变量名应该为args,**后跟的变量名应该时kwargs def foo(*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) def bar(x,y,z): print(x,y,z) def wrapper(*args,**kwargs): #args=(1,2,3,4,5,6) kwargs={'a':1,'b':2,'c':3} bar(*args,**kwargs) #bar(*(1,2,3,4,5,6),**{'a':1,'b':2,'c':3}) #bar(1,2,3,4,5,6,a=1,b=2,c=3) wrapper(1,2,3,4,5,6,a=1,b=2,c=3) # !!!!!!!!!!!!!!!当我们想要将传给一个函数的参数格式原方不动地转嫁给其内部的一个函数,应该使用下面这种形式 def bar(x,y,z): print(x,y,z) def wrapper(*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的参数标准 # 五 命名关键字参数: 放到*与**之间的参数称之为命名关键字参数 # 注意: 命名关键字参数必须按照key=value的形式传值 def foo(x,y,*args,m,n,**kwargs): #args=(3,4,5,6,7,8) print(x,y) # 1,2 print(args) #(3,4,5,6,7,8) print(m,n) #222,333 print(kwargs) foo(1,2,3,4,5,6,7,8,n=333,m=222,a=1,b=2) def foo(*,x=1,y): print(x) print(y) foo(y=2222,x=1111) foo(y=2222) def foo(x,y=1,*args,m,**kwargs): print(x) print(y) print(args) print(m) print(kwargs)
函数是第一类对象: 指的是函数名指向的值(函数)可以被当作数据去使用
def func(): # func=函数的内地址 print('from func') # print(func) age=10 # 1. 可以被引用 # x=age # print(x,age) # f=func # print(f) # f() # 2. 可以当作参数传给另外一个函数 # def bar(x): # print(x) # bar(age) # bar(func) # 3. 可以当作一个函数的返回值 # def bar(x): # return x # res=bar(age) # print(res) # res=bar(func) # print(res) # 4. 可以当作容器类型的元素 # l=[age,func,func()] # print(l)
一:函数的嵌套调用:在调用一个函数过程中,其内部代码又调用了其他函数
#示例1 def bar(): print('from bar') def foo(): print('from foo') bar() foo() #示例2 def max2(x,y): if x > y: return x else: return y def max4(a,b,c,d): res1=max2(a,b) res2=max2(res1,c) res3=max2(res2,d) return res3 print(max4(1,2,3,4))
二:函数的嵌套定义:一个函数内部又定义了另外一个函数
#示例1 def outter(): x=1 print('from outter') def inner(): print('from inner') # print(x) # print(inner) inner() # outter() # inner outter() #示例2 def f1(): print('from f1') def f2(): print('from f2') def f3(): print('from f3') f3() f2() f1() from math import pi # print(pi) def circle(radius,action=0): """ 圆形相关运算 :param radius: 半径 :param action: 0代表求面积,1代表求周长 :return: 面积或者周长 """ def area(radius): return pi * (radius ** 2) def perimiter(radius): return 2 * pi * radius if action == 0: res=area(radius) elif action == 1: res=perimiter(radius) return res print(circle(10,0)) print(circle(10,1))
名称空间就是用来存放名字与值内存地址绑定关系的地方(内存空间)
名称空间的分类
内置名称空间:存放的是python解释器自带的名字
例如:len
max
全局名称空间:存放的是文件级别的名字
例如:以下名字中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=111 # # def foo(): # len=222 # print(len) # # foo() # print('站在全局找len: ',len) # x=111 # # def f1(): # x=222 # def f2(): # def f3(): # # x=444 # print(x) # x=333 # f3() # f2() # f1() # (******)名字的查找顺序,在函数定义阶段就已经固定死了(即在检测语法时就已经确定了名字的查找顺序),与函数的调用位置无关,也就是说无论在任何地方调用函数,都必须回到 # 当初定义函数的位置去确定名字的查找关系 # 案列二: # x=111 # def outer(): # def inner(): # print('from inner',x) # x访问的时全局名称空间中x # return inner # # f=outer() # # print(f) # # x=222 # f() # 案列三: # x=111 # def outer(): # def inner(): # print('from inner',x) # x访问的时全局名称空间中x # return inner # # f=outer() # # # x=222 # def func(): # x=333 # f() # # x=444 # # func() # 案列四: # x=111 # def outer(): # def inner(): # print('from inner',x) # x是来自于当前层的名字 # x=2222222222 # return inner # # f=outer() # # f()
(******)名字的查找顺序,在函数定义阶段就已经固定死了(即在检测语法时就已经确定了名字的查找顺序),与函数的调用位置无关,也就是说无论在任何地方调用函数,都必须回到当初定义函数的位置去确定名字的查找关系
域指的是范围,作用域指的就是作用的范围,分为两种
全局作用域:包含的是内置名称空间与全局名称空间中的名字
特点:全局有效,全局存活
局部作用域:包含的是局部名称空间中的名字
特点:局部有效,临时存活
x=1 def foo(): y=2 print(x) def bar(): print(x) foo() bar() """ 1 1 """ x=1 def foo(): x=2 foo() print(x) """ 1 """ l=[] def foo(): l.append(1) # l=33333 foo() print(l) #[1]
global:在局部声明一个名字是来自于全局作用域的,可以用来在局部修改全局的不可变类型
x=1 def foo(): global x x=2 foo() print(x) #2
nonlocal:声明一个名字是来自于当前层外一层作用域的,可以用来在局部修改外层函数的不可变类型
x=0 def f1(): x=111 def f2(): # global x nonlocal x x=222 f2() print(x) f1() # print(x) """ 222 """
闭:指的是闭包函数是定义在一个函数内部的函数
包:该内部函数包含对外层函数作用域名字的引用
需要结合函数对象的概念将闭包函数返回到全局作用域去使用,从而打破函数的层级限制
def outter(): x = 111 def inner(): print(x) return inner f = outter() # f=outter内的inner x=22222 f() def func(): x = 3333 f() func()
"""
111
111
"""
闭包的意义:闭包函数提供了一种为函数体传值的解决方案
# 为函数体传值的方式一:参数 def func(x,y): print(x+y) func(1,2) # 为函数体传值的方式二:闭包 def outter(x,y): # x=1 # y=2 def func(): print(x+y) return func f=outter(1,2) f()
器:指的是具备某一功能的工具
装饰:指的是为被装饰器对象添加新功能
装饰器就是用来为被装饰器对象添加新功能的工具
注意:装饰器本身可以是任意可调用对象,被装饰器的对象也可以是任意可调用对象
开放封闭原则:封闭指的是对修改封闭,对扩展开放
装饰器的实现必须遵循两大原则:
1. 不修改被装饰对象的源代码
2. 不修改被装饰器对象的调用方式
装饰器的目标:就是在遵循1和2原则的前提下为被装饰对象添加上新功能
import time def index(): print('welcome to index page') time.sleep(3) def outter(func): # func=最原始那个index的内存地址 def wrapper(): start=time.time() func() #最原始的那个index的内存地址() stop=time.time() print('run time is %s' %(stop - start)) return wrapper index=outter(index) #index=outter(最原始那个index的内存地址) #index=wrapper的内存地址 index() #wrapper的内存地址()
import time def index(): print('welcome to index page') time.sleep(3) def home(name): print('welcome %s to home page' %name) time.sleep(2) return 123 def timmer(func): #func=最原始那个home函数的内地址 def wrapper(*args,**kwargs): #args=('egon',) kwargs={} start=time.time() res=func(*args,**kwargs) #最原始那个home函数的内地址('yb') stop=time.time() print('run time is %s' %(stop - start)) return res return wrapper # index=timmer(index) index() home=timmer(home) #home=outter(最原始那个home函数的内地址) #home=wrapper函数的内地址 res=home('yb') # res=wrapper函数的内地址('yb') print(res)
装饰器的语法糖:在被装饰对象正上方单独一行写@装饰器的名字
运行原理:
python解释器一旦运行到@装饰器的名字,就会调用装饰器,然后将被装饰函数的内存地址当作参数传给装饰器,最后将装饰器调用的结果赋值给原函数名
import time def timmer(func): #func=最原始那个home函数的内地址 def wrapper(*args,**kwargs): #args=('yb',) kwargs={} start=time.time() res=func(*args,**kwargs) #最原始那个home函数的内地址('egon') stop=time.time() print('run time is %s' %(stop - start)) return res return wrapper @timmer #index=timmer(index) def index(): print('welcome to index page') time.sleep(3) @timmer #home=timmer(home) def home(name): print('welcome %s to home page' %name) time.sleep(2) return 123 index() res=home('yb') # res=wrapper函数的内地址('yb') print(res) """ welcome to index page run time is 3.0062618255615234 welcome egon to home page run time is 2.0007591247558594 123 """
import time # 装饰器模板 def outter(func): def wrapper(*args,**kwargs): #在调用函数前加功能 res=func(*args,**kwargs) #调用被装饰的\也就是最原始的那个函数 #在调用函数后加功能 return res return wrapper @outter #index=outter(index) #index=wrapper def index(): print('welcome to index page') time.sleep(3) index()
import time def auth(func): def wrapper(*args,**kwargs): inp_user = input('please input your username: ').strip() inp_pwd = input('please input your password: ').strip() if inp_user == 'egon' and inp_pwd == '123': print('login successfull') res=func(*args,**kwargs) # 调用最原始的那个/也就是被装饰的那个函数 return res else: print('username or password error') return wrapper @auth # index=auth(index) #index=wrapper def index(): print('welcome to index page') time.sleep(3) index() #wrapper()
import time def timmer(func): #func=wrapper2 def wrapper1(*args,**kwargs): start=time.time() res=func(*args,**kwargs) #res=wrapper2(*args,**kwargs) stop=time.time() print('run time is %s' %(stop - start)) return res return wrapper1 def auth(func): #func=最原始的那个index的内存地址 def wrapper2(*args,**kwargs): inp_user = input('please input your username: ').strip() inp_pwd = input('please input your password: ').strip() if inp_user == 'yb' and inp_pwd == '123': print('login successfull') res=func(*args,**kwargs) # 调用最原始的那个/也就是被装饰的那个函数 return res else: print('username or password error') return wrapper2 # 解释@语法的时候是自下而上运行 # 而执行装饰器内的那个wrapper函数时的是自上而下 @timmer # index=timmer(wrapper2) #index=wrapper1 @auth # index=auth(最原始的那个index的内存地址) #index=wrapper2 def index(): print('welcome to index page') time.sleep(3) index() #wrapper1()
import time def timmer(func): print('timmer') def wrapper1(*args,**kwargs): start=time.time() res=func(*args,**kwargs) #res=wrapper2(*args,**kwargs) stop=time.time() print('run time is %s' %(stop - start)) return res return wrapper1 def auth(func): print('auth') def wrapper2(*args,**kwargs): inp_user = input('please input your username: ').strip() inp_pwd = input('please input your password: ').strip() if inp_user == 'yb' and inp_pwd == '123': print('login successfull') res=func(*args,**kwargs) # 调用最原始的那个/也就是被装饰的那个函数 return res else: print('username or password error') return wrapper2 @auth # index=auth(wrapper1) #index=wrapper2 @timmer #timmer(最原始的index)返回wrapper1 def index(): print('welcome to index page') time.sleep(3) index() #wrapper2() """ timmer auth please input your username: yb please input your password: 123 login successfull welcome to index page run time is 3.0002686977386475 """
import time def outter1(func1): #func1=wrapper2 print('outter1') def wrapper1(*args,**kwargs): print('wrapper1') res1=func1(*args,**kwargs) #res1=wrapper2(*args,**kwargs) return res1 return wrapper1 def outter2(func2): #func2=最原始的那个index的内存地址 print('outter2') def wrapper2(*args,**kwargs): print('wrapper2') res2=func2(*args,**kwargs) return res2 return wrapper2 @outter1 # index=outter1(wrapper2) #index=wrapper1 @outter2 #outter2(最原始的那个index的内存地址) ===> wrapper2 def index(): print('welcome to index page') time.sleep(3) index() #wrapper1() ''' outter2 outter1 wrapper1 wrapper2 '''
import time current_user={'username':None} # 补充:所有的数据类型的值自带布尔值,可以直接当作条件去用,只需要记住布尔值为假的那一些值即可(0,空,None) def login(engine='file'): #engine='mysql' def auth(func): #func=最原始那个index的内存地址 def wrapper(*args,**kwargs): if current_user['username']: print('已经登录过了,无需再次登陆') res=func(*args,**kwargs) return res if engine == 'file': inp_user = input('please input your username: ').strip() inp_pwd = input('please input your password: ').strip() if inp_user == 'egon' and inp_pwd == '123': print('login successfull') current_user['username']=inp_user # 在登陆成功之后立刻记录登录状态 res=func(*args,**kwargs) # res=最原始那个index的内存地址(*args,**kwargs) return res else: print('username or password error') elif engine == 'mysql': print('基于mysql的认证机制') elif engine == 'ldap': print('基于ldap的认证机制') else: print('无法识别的认证源') return wrapper return auth @login('file') #@auth # index=auth(最原始那个index的内存地址) #index=wrapper def index(): print('welcome to index page') time.sleep(3) @login('file') def home(name): print('welcome %s to home page' %name) time.sleep(2) return 123 index() #wrapper() res=home('egon') print(res) # 有参装饰器的模板 def outter1(x,y,z): def outter2(func): def wrapper(*args,**kwargs): res=func(*args,**kwargs) return res return wrapper return outter2 # 无参装饰器的模板 def outter(func): def wrapper(*args,**kwargs): res=func(*args,**kwargs) return res return wrapper
# 一:编写函数,(函数执行的时间是随机的) # # import time # import random # def random_time(): # # start_time=time.time() # a=random.randint(0,10) # time.sleep(a) # # end_time=time.time() # # print('time is %s'%(end_time-start_time)) # print(a) # random_time() # 二:编写装饰器,为函数加上统计时间的功能 import time # 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 foo(): # time.sleep(4) # print('yb is very cool') # foo() # 三:编写装饰器,为函数加上认证的功能 # # 四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码 # 注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式 # # 五:编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录 # # 六:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果 # # 七:为题目五编写装饰器,实现缓存网页内容的功能: # 具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中 # # 扩展功能:用户可以选择缓存介质/缓存引擎,针对不同的url,缓存到不同的文件中 # # 八:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作 # # 九 编写日志装饰器,实现功能如:一旦函数f1执行,则将消息2017-07-21 11:12:11 f1 run写入到日志文件中,日志文件路径可以指定 # 注意:时间格式的获取 # import time # time.strftime('%Y-%m-%d %X') import time current_user={'username':None} def login(engine='file'): def auth(func): def wrapper(*args,**kwargs): if current_user['username']: print('已经登录成功,无需再次登录') res=func(*args,**kwargs) return res if engine=='file': inp_username=input('请输入用户名:').strip() inp_pwd=input('请输入密码:').strip() if inp_username=='yb' and inp_pwd=='123': print('登录成功') current_user['username']=inp_username res=func(*args,**kwargs) return res else: print('用户名或密码不正确') elif engine=='mysql': print('基于mysql的认证机制') elif engine=='ldap': print('基于ldap的认证机制') else: print('无法识别的认证源') return wrapper return auth @login('file') def index(): print('welcome to index page') time.sleep(3) index() @login('file') def home(name): print('welcome %s to home page'%name) time.sleep(3) return 123 home('yb') import time current_user={'username':None} def login(engine='file'): def auth(func): def wrapper(*args,**kwargs): if current_user['username']: print('sdahsda') res=func()
总结:
1. 无参装饰器
def 装饰器名字(func): def wrapper(*args,**kwargs): res=func(*args,**kwargs) return res return wrapper
@装饰器名字 # 被装饰的函数名字=装饰器名字(被装饰的函数的内存地址)
def 被装饰的函数名字():
pass
2. 有参装饰器
def 装饰器名字(参数1,参数2,...): def outter(func): def wrapper(*args,**kwargs): res=func(*args,**kwargs) return res return wrapper return outter
@装饰器名字(值1,值2,...) # @outter
def 被装饰的函数名字():
pass
3. 叠加多个装饰器
@deco1
@deco2
@deco3
def index():
pass
解释语法的顺序:自下而上,deco3->deco2->deco1
调用index()时才开始触发装饰器内部闭包函数的执行,闭包函数执行顺序是:自上而下, deco1.wrapper1->deco2.wrapper2->deco3.wrapper3
#wraps装饰器应该加到装饰器最内层的函数上 from functools import wraps import time def deco(func): @wraps(func) def wrapper(*args, **kwargs): res = func(*args, **kwargs) return res # wrapper.__name__=func.__name__ # wrapper.__doc__=func.__doc__ return wrapper @deco #index=deco(index) #index=wrapper函数的内存地址 def index(): """ index 功能 """ print('welcome to index page') time.sleep(3) # print(index.__name__) # print(help(index)) #index.__doc__ index() print(index.__name__) print(index.__doc__)
函数的递归调用是函数嵌套调用的一种特殊形式,在调用一个函数的过程中又直接或者间接地调用该函数本身,称之为函数的递归调用
递归调用必须有两个明确的阶段:
1. 回溯: 一次次递归调用下去,说白了就一个重复的过程,但需要注意的是每一次重复问题的规模都应该有所减少,直到逼近一个最终的结果,即回溯阶段一定要有一个明确的结束条件
2. 递推: 往回一层一层推算出结果
import sys print(sys.getrecursionlimit()) sys.setrecursionlimit(2000) def foo(n): print('from foo',n) foo(n+1) foo(0) # 递归到1995报错 """ sys.getrecursionlimit() 返回递归限制的当前值,即Python解释器堆栈的最大深度。此限制可防止无限递归导致C堆栈溢出并导致Python崩溃。它可以设置 setrecursionlimit()。 sys.setrecursionlimit(限制) 设置Python解释器堆栈的最大深度以限制。此限制可防止无限递归导致C堆栈溢出并导致Python崩溃。 最高可能的限制取决于平台。当用户需要深度递归的程序和支持更高限制的平台时,用户可能需要设置更高的限制。这应该小心,因为太高的限制可能导致崩溃。 """
递归调用就是一个重复的过程,但是每一次重复问题的规模都应该有所减少,并且应该在满足某种条件的情况下结束重复,开始进入递推阶段
def age(n): if n == 1: return 18 return age(n-1) + 2 print(age(5)) #26 l=[1,[2,[3,[4,[5,[6,[7,[8,[9,[10,[11,]]]]]]]]]]] def search(l): for item in l: if type(item) is not list: # 不是列表直接打印 print(item) else: # 判断是列表则继续循环,判断... search(item) search(l) """ 1 2 3 4 5 6 7 8 9 10 11 """
# 算法:是如何高效率地解决某一个问题的方法/套路 # 二分法 nums=[13,15,17,23,31,53,74,81,93,102,103,201,303,403,503,777] find_num=503 def binary_search(nums,find_num): print(nums) if len(nums) == 0: print('not exists') return mid_index=len(nums) // 2 if find_num > nums[mid_index]: # in the right nums=nums[mid_index+1:] # 重新执行二分的逻辑 binary_search(nums,find_num) elif find_num < nums[mid_index]: #in the left nums=nums[0:mid_index] # 重新执行二分的逻辑 binary_search(nums,find_num) else: print('find it') binary_search(nums,find_num) binary_search(nums,94) """ [13, 15, 17, 23, 31, 53, 74, 81, 93, 102, 103, 201, 303, 403, 503, 777] [102, 103, 201, 303, 403, 503, 777] [403, 503, 777] find it [13, 15, 17, 23, 31, 53, 74, 81, 93, 102, 103, 201, 303, 403, 503, 777] [102, 103, 201, 303, 403, 503, 777] [102, 103, 201] [102] [] not exists """
nums=[13,15,28,36,41,54,56,77,85] find_num=36 def half_search(nums,find_num): print(nums) if len(nums)==0: print('no exists') return mid_index=len(nums)//2 if find_num>nums[mid_index]: nums=nums[mid_index+1:] half_search(nums,find_num) elif find_num<nums[mid_index]: nums=nums[0:mid_index] half_search(nums,find_num) else: print('find it') half_search(nums,find_num)
三元表达式
res=条件成立情况下的返回值 if 条件 else 条件不成立情况下的返回值
def max2(x, y): # if x > y: # return x # else: # return y return x if x > y else y print(max2(2,5)) # 三元表达式实现的效果就是:条件成立的情况下返回一个值,不成立的情况下返回另外一种值 # res=条件成立情况下返回的值 if 条件 else 条件不成立情况下返回的值
列表生成式
[i for i in range(10)]
[i for i in range(10) if i > 3]
字典生成式
生成字典
{i:i for i in range(10) if i>3}
生成集合
{i for i in range(10) if i>3}
names=['yb','zs','yxd','yl'] l=[] for name in names: res=name + '_sg' l.append(res) print(l) l=[name + '_sg' for name in names] print(l) """ ['yb_sg', 'zs_sg', 'yxd_sg', 'yl_sg'] ['yb_sg', 'zs_sg', 'yxd_sg', 'yl_sg'] """ names=['aa_sb','bb_sb','cc_sb','dd_sb','yb'] l=[] for name in names: if name.endswith('sb'): l.append(name) print(l) l=[name for name in names if name.endswith('sb')] print(l) """ ['aa_sb', 'bb_sb', 'cc_sb', 'dd_sb'] ['aa_sb', 'bb_sb', 'cc_sb', 'dd_sb'] """
items=[ ('name','yb'), ('age',18), ('sex','male'), ] dic=dict(items) print(dic) """ {'name': 'yb', 'age': 18, 'sex': 'male'} """ l=['a','b','c','d'] for i,v in enumerate(l): print(i,v) """ 0 a 1 b 2 c 3 d """ keys = ['name', 'age', 'sex'] vals = ['yb', 18, 'male'] dic = {} for i, k in enumerate(keys): # print(i,k) dic[k] = vals[i] print(dic) dic={k:vals[i] for i,k in enumerate(keys)} print(dic) dic={k:vals[i] for i,k in enumerate(keys) if i > 0} print(dic) """ {'name': 'yb', 'age': 18, 'sex': 'male'} {'name': 'yb', 'age': 18, 'sex': 'male'} {'age': 18, 'sex': 'male'} """ print({i for i in 'hello'}) #{'l', 'o', 'e', 'h'}
匿名函数
匿名函数:就是没有名字的函数
用于仅仅临时使用一次的场景,没有重复使用的需求
匿名函数的精髓在于没有名字,没有名字意味着只能调用一次就会被当做垃圾回收
lambda 参数1,参数2,...:函数体代码 max([],key=lambda 参数1:函数体代码)
print(lambda x,y:x+y) #<function <lambda> at 0x0000024D87AFC1E0> print((lambda x,y:x+y)(1,2)) #3 # 匿名函数的精髓就是没有名字,为其绑定名字是没有意义的 f=lambda x,y:x+y print(f) #<function <lambda> at 0x0000024D87AFC1E0> print(f(1,2)) #3 # 匿名函数与内置函数结合使用 # max,min,sorted,map,filter,reduce
salaries={ 'yb':300000, 'sb':100000000, 'yxd':10000, 'zs':2000 } # 求薪资最高的那个人名:即比较的是value,但取结果是key res=max(salaries) print(res) #zs # 可以通过max函数的key参数来改变max函数的比较依据,运行原理: # max函数会“for循环”出一个值,然后将该值传给key指定的函数 # 调用key指定的函数,将拿到的返回值当作比较依据 def func(name): # 返回一个人的薪资 return salaries[name] res=max(salaries,key=func) #sb print(res) # 求最大值 res=max(salaries,key=lambda name:salaries[name]) #'sb' print(res) # 求最小值 res=min(salaries,key=lambda name:salaries[name]) #'zs' print(res)
1.sorted
# sorted排序 nums=[11,33,22,9,31] res=sorted(nums,reverse=True) print(nums) print(res) """ [11, 33, 22, 9, 31] [33, 31, 22, 11, 9] """ salaries={ 'yb':300000, 'sb':100000000, 'yxd':10000, 'zs':2000 } for v in salaries.values(): print(v) res=sorted(salaries.values()) print(res) res=sorted(salaries,key=lambda name:salaries[name],reverse=True) print(res) """ 300000 100000000 10000 2000 [2000, 10000, 300000, 100000000] ['sb', 'yb', 'yxd', 'zs'] """
2.map
# map:把一个列表按照我们自定义的映射规则映射成一个新的列表 names=['yb','zs','yxd','yjw'] res=map(lambda name: name + "_shuai", names) print(list(res)) #['yb_shuai', 'zs_shuai', 'yxd_shuai', 'yjw_shuai']
3.filter
# filter: 从一个列表中过滤出符合我们过滤规则的值 # 运行原理:相当于for循环取出每一个人名,然后传给匿名函数,将调用匿名函数返回值为True的那个人名给留下来 names=['yb_shuai', 'zs_shuai', 'yxd_shuai', 'yjw_shuai'] res=filter(lambda name:name.endswith('shuai'),names) print(list(res)) print([name for name in names if name.endswith('shuai')]) """ ['yb_shuai', 'zs_shuai', 'yxd_shuai', 'yjw_shuai'] ['yb_shuai', 'zs_shuai', 'yxd_shuai', 'yjw_shuai'] """
4.reduce
# reduce: 把多个值合并成一个结果 from functools import reduce l=['a','b','c','d'] res=reduce(lambda x,y:x+y,l,'A') #'A','a' => 'Aa' #'Aa','b'=>'Aab' #'Aab','c'=>'Aabc' #'Aabc','d'=>'Aabcd' print(res) #Aabcd
res=reduce(lambda x,y:x+y,l) # 'a','b'=>'ab' print(res) res=reduce(lambda x,y:x+y,range(1,101)) # 1,2=>3 # 3,3=>6 print(res) """ abcd 5050 """
# 1使用递归打印斐波那契数列(前两个数的和得到第三个数,如:0 1 1 2 3 5 8...) # # def nabo(n1,n2): # if n2<100: # n3=n1+n2 # n4=n2+n3 # print('%s\n%s'%(n3,n4)) # nabo(n3,n4) # else: # return 1 # # nabo(0,1) # 2 一个嵌套很多层的列表,如l =[1, 2, [3, [4, 5, 6, [7, 8, [9, 10, [11, 12, 13, [14, 15]]]]]]],用递归取出所有的值 # l=[1, 2, [3, [4, 5, 6, [7, 8, [9, 10, [11, 12, 13, [14, 15]]]]]]] def search(l): for item in l: if item is not list: print(item) else: search(l-item) search(l) # 3 编写用户登录装饰器,在登录成功后无需重新登录,同一账号重复输错三次密码则锁定5分钟 # 4、求文件a.txt中总共包含的字符个数?思考为何在第一次之后的n次sum求和得到的结果为0?(需要使用sum函数) # 5、文件shopping.txt内容如下 # mac, 20000, 3 # lenovo, 3000, 10 # tesla, 1000000, 10 # chicken, 200, 1 # 求总共花了多少钱? # # 打印出所有商品的信息,格式为[{'name': 'xxx', 'price': 333, 'count': 3}, ...] # # 求单价大于10000的商品信息, 格式同上
5.abs()
#abs() 函数返回数字的绝对值。 print(abs(-1)) print(abs(0))
6.all()
#all() 函数用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False。 #元素除了是 0、空、FALSE 外都算 TRUE。 print(all([1,'',None])) print(all([1,2,'aaa'])) print(all([])) print(all('')) """ False True True True """
7.any()
#any() 函数用于判断给定的可迭代参数 iterable 是否全部为 False,则返回 False,如果有一个为 True,则返回 True。 #元素除了是 0、空、FALSE 外都算 TRUE。 print(any([0,'',None,False,1])) print(any([0,'',None,False])) print(any([])) """ True False False """
8.进制
bin() 返回一个整数 int 或者长整数 long int 的二进制表示。
oct() 函数将一个整数转换成8进制字符串。
hex() 函数用于将10进制整数转换成16进制,以字符串形式表示。
9.bool()
#bool() 函数用于将给定参数转换为布尔类型,如果没有参数,返回 False。 #bool 是 int 的子类。 print(bool(0)) print(bool('')) print(bool([])) print(bool(None)) """ False False False False """
10.callable()
#callable() 函数用于检查一个对象是否是可调用的。如果返回True,object仍然可能调用失败;但如果返回False,调用对象ojbect绝对不会成功。 #对于函数, 方法, lambda 函式, 类, 以及实现了 __call__ 方法的类实例, 它都返回 True。 print(callable(len)) #True
11.chr()
#chr() 用一个范围在 range(256)内的(就是0~255)整数作参数,返回一个对应的字符。 print(chr(65)) print(chr(90))
12.ord()
#ord() 函数是 chr() 函数(对于8位的ASCII字符串)或 unichr() 函数(对于Unicode对象)的配对函数,它以一个字符(长度为1的字符串)作为参数,返回对应的 ASCII 数值,或者 Unicode 数值,如果所给的 Unicode 字符超出了你的 Python 定义范围,则会引发一个 TypeError 的异常。 print(ord('a')) print(ord('z')) print(ord('@')) """ 97 122 64 """
13.dir()
#dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息。 import time print(dir(time)) """ ['_STRUCT_TM_ITEMS', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'altzone', 'asctime', 'clock', 'ctime', 'daylight', 'get_clock_info', 'gmtime', 'localtime', 'mktime', 'monotonic', 'monotonic_ns', 'perf_counter', 'perf_counter_ns', 'process_time', 'process_time_ns', 'sleep', 'strftime', 'strptime', 'struct_time', 'thread_time', 'thread_time_ns', 'time', 'time_ns', 'timezone', 'tzname'] """
14.divmod()
#python divmod() 函数把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b, a % b)。 #在 python 2.3 版本之前不允许处理复数。 print(divmod(3003,20)) """ (150, 3) """
15.enumerate()
#enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。 #Python 2.3. 以上版本可用,2.6 添加 start 参数。 for i in enumerate(['a','b','c']): print(i) """ (0, 'a') (1, 'b') (2, 'c') """
16.eval()
#eval() 函数用来执行一个字符串表达式,并返回表达式的值。 res=eval('[1,2,3]') print(res,type(res)) """ [1, 2, 3] <class 'list'> """
17.frozenset()
#frozenset() 返回一个冻结的集合,冻结后集合不能再添加或删除任何元素。 fs=frozenset({1,2,3})
18.pow()
#pow() 方法返回 xy(x的y次方) 的值。 print(pow(2,3,3)) #(2**3)%3
"""
2
"""
19.reversed
#reversed 函数返回一个反转的迭代器。 l=[1,'a','3',3] l1=reversed(l) print(list(l1)) """ [3, '3', 'a', 1] """
20.round()
#round() 方法返回浮点数x的四舍五入值。 print(round(3.5)) print(round(3.3)) """ 4 3 """
21.slice()
#slice() 函数实现切片对象,主要用在切片操作函数里的参数传递。 l=['a','b','c','d','e'] s='helloworld' obj=slice(1,5,2) print(l[1:5:2]) print(l[obj]) """ ['b', 'd'] ['b', 'd'] """
print(s[1:5:2])
print(s[obj])
"""
el
el
"""
22.vars() & locals()
#vars() 函数返回对象object的属性和属性值的字典对象 #locals() 函数会以字典类型返回当前位置的全部局部变量。 #对于函数, 方法, lambda 函式, 类, 以及实现了 __call__ 方法的类实例, 它都返回 True。 print(vars() is locals()) """ True """
23.zip()
""" zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。 如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。 zip 方法在 Python 2 和 Python 3 中的不同:在 Python 3.x 中为了减少内存,zip() 返回的是一个对象。如需展示列表,需手动 list() 转换。 如果需要了解 Pyhton3 的应用,可以参考 Python3 zip()。 """ l=[1,2,3,4,5,6,7] s='hello' res=zip(l,s) print(list(res)) """ [(1, 'h'), (2, 'e'), (3, 'l'), (4, 'l'), (5, 'o')] """
#1.1. 迭代器指的是迭代取值的工具 #1.2. 迭代是一重复的过程,每一次重复都是基于上一次的结果而来 #单纯的重复不是迭代 i=0 while True: print(i) # 迭代:重复+每次重复都是基于上一次的结果而进行 l=['a','b','c'] i=0 while i < len(l): print(l[i]) i+=1 #迭代器的意义:迭代器提供了一种通用的且不依赖于索引的迭代取值方式
一 :可迭代的对象iterable:但凡内置有__iter__方法的对象都称之为可迭代的对象
可迭代的对象:str,list,tuple,dict,set,文件对象
执行可迭代对象下的__iter__方法,返回的值就是一个迭代器对象iterator
dic={'x':1,'y':2,'z':3} iter_dic=dic.__iter__() print(iter_dic) print(iter_dic.__next__()) print(iter_dic.__next__()) print(iter_dic.__next__()) print(iter_dic.__next__()) #StopIteration应该被当成一种结束信号 """ <dict_keyiterator object at 0x0000014D2839A4A8> x y z """ l=['a','b','c'] iter_l=l.__iter__() print(iter_l.__next__()) print(iter_l.__next__()) print(iter_l.__next__()) # print(iter_l.__next__()) """ a b c """ l=['a','b','c'] print(l.__iter__().__next__()) print(l.__iter__().__next__()) print(l.__iter__().__next__()) iter_l=l.__iter__() print(iter_l.__next__()) print(iter_l.__next__()) print(iter_l.__next__()) """ a a a a b c """
二: 迭代器对象
1. 既内置有__next__方法的对象,执行迭代器__next__方法可以不依赖索引取值
2. 又内置有__iter__方法的对象,执行迭代器__iter__方法得到的仍然是迭代器本身
ps:
1.迭代器对象一定是可迭代的对象,而可迭代的对象却不一定是迭代器对象
2.文件对象本身就是一个迭代器对象
# dic={1,2,3,4} dic={'x':1,'y':2,'z':3} # print(len(dic)) #dic.__len__() iter_dic=iter(dic) # dic.__iter__() # while True: try: print(next(iter_dic)) #iter_dic.__next__() except StopIteration: break print('='*100) # # 同一个迭代器只能完整地取完一次值 iter_dic=iter(dic) #dic.__iter__() while True: try: print(next(iter_dic)) #iter_dic.__next__() except StopIteration: break """ x y z ==================================================================================================== x y z """
for本质应该称之为迭代器循环
工作原理
1. 先调用in后面那个对象的__iter__方法,将其变成一个迭代器对象
2. 调用next(迭代器),将得到的返回值赋值给变量名k
3. 循环往复直到next(迭代器)抛出异常,for会自动捕捉异常然后结束循环
ps:从for角度,可以分辨出但凡可以被for循环循环取值的对象都是可迭代的对象
dic={'x':1,'y':2,'z':3} for k in dic: print(k) """ x y z """
三:迭代器总结
优点:
1. 提供一种通用的且不依赖于索引的迭代取值方式
2. 同一时刻在内存中只存在一个值,更节省内存
缺点:
1. 取值不如按照索引的方式灵活,(不能取指定的某一个值,而且只能往后取)
2. 无法预测迭代器的长度
l=[1,2,2,3,3,3,3,3,3,3,3,3,3,3] iter_l=iter(l) print(iter_l) names = ['yb', 'zs', 'yxd'] res=map(lambda x:x+"_NB",names) print(res) obj=range(1,1000000000000000000) print(obj) """ <list_iterator object at 0x0000026706BF9278> <map object at 0x0000026706BF9208> range(1, 1000000000000000000) """
1. 迭代器 什么是迭代器? 迭代器是迭代取值的工具,迭代是一个重复的过程,每一次重复都是基于上一次的结果而来 为何要用迭代器? 1. 可以不依赖索引取值 2. 同一时刻在内存中只有一个值,不会过多地占用内存 如何用迭代器? 可迭代的对象:但凡内置有__iter__方法的对象都是可迭代的对象,例如str,list,dict,tuple,set,f 迭代器的对象:既内置有__iter__方法又内置有__next__方法的对象称之为迭代器对象,例如f 可迭代对象.__iter__()-------》迭代器对象 迭代器对象.__iter__()-------》迭代器本身 迭代器对象.__next__()-------》迭代器的下一个值 for i in obj: pass 总结迭代器: 优点: 1. 可以不依赖索引取值 2. 同一时刻在内存中只有一个值,不会过多地占用内存 缺点: 1. 不如按照索引或者key取值的方式灵活 2. 无法预测迭代器所包含值的长度
大前提:生成器就是一种自定义的迭代器,本质就是迭代器
但凡函数内包含yield关键字,调用函数不会执行函数体代码,会得到一个返回值,该返回值就是生成器对象
def func(): print('first') yield 1 print('second') yield 2 print('third') yield 3 print('fourth') g=func() print(g) print(g.__iter__().__iter__() is g) """ <generator object func at 0x0000026AD8673F48> True """ res1=next(g) #会触发函数的执行,直到碰到一个yield停下来,并且将yield后的值当作本次next的结果返回 print(res1) res2=next(g) print(res2) res3=next(g) print(res3) # res4=next(g) """ first 1 second 2 third 3 """
def my_range(start,stop,step=1): while start < stop: # 3 < 3 yield start start+=step #start=3 obj=my_range(1,5,2) # 1 3 print(next(obj)) print(next(obj)) # print(next(obj)) # 了解(*):yield的表达式形式的应用: x=yield def dog(name): print('狗哥 %s 准备开吃' %name) food_list=[] while True: food=yield food_list # food=yield='肉包子' print('%s 吃了 %s' %(name,food)) food_list.append(food) g=dog('aaa') # 强调:针对表达式形式的yield的使用,第一步必须让函数先暂停到一个yield的位置,才能进行传值操作 next(g) # 张开狗嘴,让生成器先暂停到yield的位置,准备接收外部传进来的值 res1=next(g) #g.send(None) # print(res1) res2=g.send('屎包子') # 1. 先为当前暂停位置的yield赋值 2. next(生成器)直到再次碰到一个yield停下来,然后其的值当做本次next的结果 # print(res2) res3=g.send('肉包子') # print(res3) res4=g.send('泔水') print(res4) """ 狗哥 aaa 准备开吃 aaa 吃了 None aaa 吃了 屎包子 aaa 吃了 肉包子 aaa 吃了 泔水 [None, '屎包子', '肉包子', '泔水'] """
总结yield:只能在函数内使用
1. yield提供了一种自定义迭代器的解决方案
2. yield可以保存函数的暂停的状态
3. yield对比return
1. 相同点:都可以返回值,值的类型与个数都没有限制
2. 不同点:yield可以返回多次值,而return只能返回一次值函数就结束了
l=[i**2 for i in range(1,6) if i > 3] print(l) #[16, 25] # 生成器表达式 g=(i**2 for i in range(1,6) if i > 3) # print(g) print(next(g)) print(next(g)) # print(next(g))