python基础之函数对象、嵌套、闭包、装饰器、迭代器、生成器、递归、二分法、生成式
一、函数对象
可以把函数当成变量去用
# 1、可以赋值 # f=func # print(f,func) # f() # 2、可以当做参数传给另外一个函数 # def foo(x): # x = func的内存地址 # # print(x) # x() # # foo(func) # foo(func的内存地址) # 3、可以当做另外一个函数的返回值 # def foo(x): # x=func的内存地址 # return x # return func的内存地址 # # res=foo(func) # foo(func的内存地址) # print(res) # res=func的内存地址 # # res() # 4、可以当做容器类型的一个元素 # l=[func,] # # print(l) # l[0]() # dic={'k1':func} # print(dic) # dic['k1']()
二、函数的嵌套
函数的嵌套:在调用一个函数的过程中又调用其他函数
语法:
def f1():
def f2():
pass
应用:
# 求圆形的求周长:2*pi*radius def circle(radius,action=0): from math import pi def perimiter(radius): return 2*pi*radius # 求圆形的求面积:pi*(radius**2) def area(radius): return pi*(radius**2) if action == 0: return 2*pi*radius elif action == 1: return area(radius) circle(33,action=0)
三、闭包函数
闭包函数=名称空间与作用域+函数嵌套+函数对象。"闭"函数指的该函数是内嵌函数。"包"函数指的该函数包含对外层函数作用域名字的引用(不是对全局作用域)
1.定义:内部函数包含对外部作用域而非全局作用域名字的引用,该内部函数称为闭包函数。函数里套有其他函数的形式,两层以上的
#完整的闭包函数结构,嵌套调用 def func(): name = 'aaa' def inner(): print(name) return inner f = func() f()
2.应用:两种为函数体传参的方式
#方式一:直接传值
def index(x,y): print(x,y)
#方式二:用闭包函数
def f1(x): # x=3 x=3 def f2(): print(x) #结果为3 return f2 x=f1(3) print(x) #此时x为函数f2的内存地址 x() #调用x,
3、用于爬虫
import requests # 传参的方案一: # def get(url): # response=requests.get(url) # print(len(response.text)) # # get('https://www.baidu.com') # get('https://www.cnblogs.com/linhaifeng') # get('https://zhuanlan.zhihu.com/p/109056932') # 传参的方案二: def outter(url): # url='https://www.baidu.com' def get(): response=requests.get(url) print(len(response.text)) return get baidu=outter('https://www.baidu.com') baidu() cnblogs=outter('https://www.cnblogs.com/linhaifeng') cnblogs() zhihu=outter('https://zhuanlan.zhihu.com/p/109056932') zhihu()
四、装饰器
本质:装饰器的本质:一个闭包函数(可调用的对象)
功能:装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展(不修改被装饰对象的源代码和调用方式)
无参装饰器:
1、装饰器固定格式
#无参装饰器固定模板
def timer(func): def inner(*args,**kwargs): '''执行函数之前要做的''' re = func(*args,**kwargs) '''执行函数之后要做的''' return re return inner
2、简单的装饰器
""" 1.统计index函数执行的时间 """ import time def index(): time.sleep(3) print('吃起吃起!') def outter(func): # func = 最原始的index函数的内存地址 def get_time(): start = time.time() func() # func = index函数的内存地址() 直接调用,此时直接调用index函数 end = time.time() print('index run time:%s'%(end-start)) return get_time index = outter(index) #outter(最原始的index函数内存地址) ,相当于给outter传入index函数 # index指向get_time函数的内存地址,调用index就是执行get_time函数 index()
3、装饰器语法糖
@ 符号就是装饰器的语法糖。语法糖在书写的时候应该与被装饰对象紧紧挨着 两者之间不要有空格
""" 1.统计index函数执行的时间 """ import time def outter(func): # func = 最原始的index函数的内存地址 def get_time(): start = time.time() func() # func = index函数的内存地址() 直接调用,此时直接调用index函数 end = time.time() print('index run time:%s'%(end-start)) return get_time @outter #相当于index = outter(index) def index(): time.sleep(3) print('吃起吃起') index() #此时调用被装饰器装饰的函数。结果输出就会给函数加上装饰器功能。
4、叠加多个装饰器
装饰器在加载的时候 :自下而上
装饰器在执行的时候 : 函数调用前的,自上而下,函数调用后的,自下而上
def deco1(func1): # func1 = wrapper2的内存地址 def wrapper1(*args,**kwargs): print('正在运行1') res1=func1(*args,**kwargs) print('正在运行7') return res1 return wrapper1 def deco2(func2): # func2 = wrapper3的内存地址 def wrapper2(*args,**kwargs): print('正在运行2') res2=func2(*args,**kwargs) print('正在运行6') return res2 return wrapper2 def deco3(func3): def wrapper3(*args,**kwargs): print('正在运行3') res3=func3(*args,**kwargs) print('正在运行5') return res3 return wrapper3 # 加载顺序自下而上(了解) @deco1 @deco2 @deco3 def index(x,y): print('正在运行4') print('from index %s:%s' %(x,y)) index(1,2) # wrapper1(1,2) '''执行结果 正在运行1 正在运行2 正在运行3 正在运行4 from index 1:2 正在运行5 正在运行6 正在运行7 '''
有参装饰器
1、固定格式
def 有参装饰器名字(x,y,z): (括号里面为要传的参数) def outter(func): def wrapper(*args, **kwargs): #括号接收参数 res = func(*args, **kwargs) return res return wrapper return outter @有参装饰器(1,y=2,z=3) def 被装饰对象(): pass
2、语法糖
def auth(db_type): def deco(func): def wrapper(*args, **kwargs): #因为下面func需要使用参数,所以这里需要有 name = input('your name>>>: ').strip() pwd = input('your password>>>: ').strip() if db_type == 'file': print('基于文件的验证') if name == 'aaa' and pwd == '123': res = func(*args, **kwargs) # index(1,2) #与*args, **kwargs接收参数一样,但实质来自index的参数 return res else: print('user or password error') elif db_type == 'mysql': print('基于mysql的验证') elif db_type == 'ldap': print('基于ldap的验证') else: print('不支持该db_type') return wrapper return deco @auth(db_type='file') # @deco # index=deco(index) # index=wrapper def index(x, y): print('index->>%s:%s' % (x, y)) @auth(db_type='mysql') # @deco # home=deco(home) # home=wrapper def home(name): print('home->>%s' % name) @auth(db_type='ldap') # 账号密码的来源是ldap def transfer(): print('transfer') index(1, 2) ''' 执行结果 your name>>>: aaa your password>>>: 123 基于文件的验证 index->>1:2 ''' #home('aaa') #transfer()
装饰器之偷梁换柱
#偷梁换柱,即将原函数名指向的内存地址偷梁换柱成wrapper函数 # 所以应该将wrapper做的跟原函数一样才行 from functools import wraps def outter(func): @wraps(func) def wrapper(*args, **kwargs): """这个是主页功能""" res = func(*args, **kwargs) # res=index(1,2) return res # 手动将原函数的属性赋值给wrapper函数 # 1、函数wrapper.__name__ = 原函数.__name__ # 2、函数wrapper.__doc__ = 原函数.__doc__ # wrapper.__name__ = func.__name__ # wrapper.__doc__ = func.__doc__ return wrapper @outter # index=outter(index) def index(x,y): """这个是主页功能""" print(x,y) print(index.__name__) print(index.__doc__) #help(index)实际实行wrapper函数
五、迭代器
定义:迭代器指的是迭代取值的工具,迭代是一个重复的过程,每次重复
都是基于上一次的结果而继续的,单纯的重复并不是迭代。迭代器是用来迭代取值的工具,而涉及到把多个值循环取出来的类型。
有:列表、字符串、元组、字典、集合、打开文件。
1、可迭代的对象:但凡内置有__iter__方法的都称之为可迭代的对象。列表、字符串、元组、字典、集合、打开文件。
# s1='' # # s1.__iter__() # # l=[] # # l.__iter__() # # t=(1,) # # t.__iter__() # # d={'a':1} # # d.__iter__() # # set1={1,2,3} # # set1.__iter__() # # with open('a.txt',mode='w') as f: # # f.__iter__() # pass
2、调用可迭代对象下的__iter__方法会将其转换成迭代器对象
d = {'a': 1, 'b': 2, 'c': 3} d_iterator = d.__iter__() print(d_iterator) #迭代器对象print(d_iterator.__next__()) #a,.__next__()是对迭代器对象取值 print(d_iterator.__next__()) #b print(d_iterator.__next__()) #c print(d_iterator.__next__()) #没值了,抛出异常StopIteration
循环取值,自动退出
# l=[1,2,3,4,5] # l_iterator=l.__iter__() # # while True: # try: # print(l_iterator.__next__()) # except StopIteration: # break
3、可迭代对象与迭代器区别
3.1 可迭代对象("可以转换成迭代器的对象"):内置有__iter__方法对象,如列表、字符串、元组、字典、集合、打开文件。可称为可迭代对象
可迭代对象.__iter__(): 得到迭代器对象
3.2 迭代器对象:内置有__next__方法并且内置有__iter__方法的对象,文件对象可为迭代器对象
迭代器对象.__next__():得到迭代器的下一个值
迭代器对象.__iter__():得到迭代器的本身。与没调用一样
dic={'a':1,'b':2,'c':3} #可迭代对象 dic_iterator=dic.__iter__() #迭代器对象 print(dic_iterator is dic_iterator.__iter__().__iter__().__iter__())
4、for循环的工作原理
for循环可以称之为叫迭代器循环
# 1、d.__iter__()得到一个迭代器对象 # 2、迭代器对象.__next__()拿到一个返回值,然后将该返回值赋值给k # 3、循环往复步骤2,直到抛出StopIteration异常for循环会捕捉异常然后结束循环 # for k in d: # print(k) # with open('a.txt',mode='rt',encoding='utf-8') as f: # for line in f: # f.__iter__() # print(line)
迭代器总结:
#迭代器优缺点总结 # 优点: # I、为序列和非序列类型提供了一种统一的迭代取值方式。 # II、惰性计算:迭代器对象表示的是一个数据流,可以只在需要时才去调用next来计算出一个值,就迭代器本身来说,同一时刻在内存中只有一个值,因而可以存放无限大的数据流,而对于其他容器类型,如列表,需要把所有的元素都存放于内存中,受内存大小的限制,可以存放的值的个数是有限的。 # 6.2 缺点: # I、除非取尽,否则无法获取迭代器的长度 # II、只能取下一个值,不能回到开始,更像是‘一次性的’,迭代器产生后的唯一目标就是重复执行next方法直到值取尽,否则就会停留在某个位置,等待下一次调用next;若是要再次迭代同个对象,你只能重新调用iter方法去创建一个新的迭代器对象,如果有两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值。
六、生成器
人为自定义实现迭代的功能就叫生成器 ,即自定义的迭代器。
1、关键字:yield
函数内如果有yield关键字,那么加括号执行函数的时候不会触发函数体代码的运行 ,但在程序中遇到yield会暂停运行,等待调入参数 。将yield后的值
当做本次调用的结果返回
def func(): print('one') yield 12 #程序调用函数执行到 yeild 后就会终止,将值12作为结果返回。后面代码不会执行 print('two') yield print('three') res=func() print(res.__next__()) #此时取到yeild返回的值,如果无值,返回None ''' 结果 one 12 '''
例子:
#实现自定义range原理 def my_range(start, stop, step): # print('start...') while start < stop: yield start start += step # print('end....') # g=my_range(1,5,2) # 1 3代用my_range相当于循环执行next方法取值, # print(next(g)) # print(next(g)) # print(next(g)) for n in my_range(1, 7, 2): print(n) ''' 结果为:1 3 5 '''
总结:有了yield关键字,我们就有了一种自定义迭代器的实现方式。yield可以用于返回值,但不同于return,函数一旦遇到return就结束了,而yield可以保存函数的运行状态挂起函数,用来返回多次值
2、生成器表达式
把列表解析的 [] 换成 () 得到的就是生成器表达式
res=[i for i in range(1,10) if i!=4] print(res) #[1, 2, 3, 5, 6, 7, 8, 9] res1=(i for i in range(1,10000) if i!=4) #生成器表达式 print(res1) # next=res.__iter__() print(res1.__next__()) print(res1.__next__()) print(res1.__next__()) print(res1.__next__())# 必须通过触发—next—才会触发,有几个就取几个值
生成器例子:
def demo(): for i in range(4): yield i # i=[0,1,2,3] # 遇到—next--才会触发执行 g=demo() g1=(i for i in g) # for 循环取值赋值给g1,已经取完i中的内存地址 g2=(i for i in g1) print(list(g1)) print(list(g2)) 结果: >>>[0,1,2,3] >>>[]
def add(n,i): return n+i def test(): for i in range(4): yield i g=test() for n in [1,10]: g=(add(n,i) for i in g) # 第一次for循环g=(add(n,i) for i in test()) # 第二次for循环g=(add(n,i) for i in (add(n,i) for i in test())) print(n) #10 print(g.__next__()) #此时g对应值第一个值为20 res=list(g) #再次循环 print(res) #[20, 21, 22, 23] """ for i in (add(10,i) for i in test()): 会执行所有的生成器内部的代码 add(n,i) """
七、递归
如果函数中包含了对其自身的调用,该函数就是递归的;递归(Recursion),在数学与计算机科学中,是指在函数的定义中使用函数自身的方法;
1、递归的实现
# 非递归实现阶乘 def factorial(n): res = 1 for i in range(2, n+1): res *= i return res print(factorial(5)) # 120 # 递归实现阶乘 def factorial(n): if n == 0 or n == 1: return 1 else: return (n * factorial(n - 1)) #此时调用了factorial函数 print(factorial(5)) # 120
2、递归函数:在函数体内再调用函数本身(简单的说就是多次甚至无限的循环)但不能超过递归最大深度,递归的最大深度——997
实现过程:1:回溯:就是一次次重复的过程,重复的过程必须在逐渐的降低问题的复查程度,直到找到一个最终的结束条件。2:递归:就是拿到条件后 ,一次次往回推导的过程
def age(n): if n==1: # 第一个开始回溯的人 return 18 # 结束的条件 else: return age(n-1)+2 #递归的规律 res=age(10) print(res)
七.二、算法之二分法:所谓的算法就是提高效率的方法, 二分法:就是容器内必须有一定的大小顺序,以中间数为基准寻找规律,分段比较左右对比
l=[1,4,8,9,14,25,48,58,124,236,485,489,520,1234,4526,7852] def func(l,aim): mid=(len(l)-1)//2 # 先取列表的中间值 if l: if aim>l[mid]: func(l[mid+1:],aim) # 按索引的方式取值 elif aim<l[mid]: func(l[:mid],aim) elif aim==l[mid]: print(aim) else: print('找不到') func(l,124) # 124是要找的数 func(l,8) # 8是分段寻找 前后取出在对比
七.三、三元表达式
固定格式
值1 if 条件 else 值2
条件成立 值1
条件不成立 值2
#平常想法 def my_max(x,y): if x>y: return x else: return y res=my_max(1,9) print(res) #用三元表达式 x=1 y=9 res=x if x>y else y print(res) >>9
七.四 列表表达式
l=['tank','nick','oscar','topa','kevin','jamse'] k=[] for name in l: k .append('%s_boy'%name) # k.append(name+'boy')但不推荐使用,pycharm里效率极低 print(k) >>['tank_boy', 'nick_boy', 'oscar_boy', 'topa_boy', 'kevin_boy', 'jamse_boy'] #用列表生成式 l=['tank','nick','oscar','topa','kevin','jamse'] res=['%s_boy'%name for name in l] print(res) #结果同上
七.五字典生成式
内部都是基于 for 循环 以 key:vlaue 键值对的形式生成列表 list,关键字 enumerate
#d = {key: value for (key, value) in iterable} d1 = {'x': 1, 'y': 2, 'z': 3} d2 = {k: v for (k, v) in d1.items ()} print (d2)