第四篇: python函数续
1、函数的嵌套
函数的嵌套调用:在调用一个函数的过程中,又调用了其它函数
示例1:
def bar(): print('from nbar') def foo(): print('from foo') bar() foo() #执行结果为 C:\Users\Administrator\AppData\Local\Programs\Python\Python36\python.exe C:/Users/Administrator/PycharmProjects/python18/day4/函数嵌套.py from foo from nbar
示例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(max(1,2,3,-1)) #执行结果如下 C:\Users\Administrator\AppData\Local\Programs\Python\Python36\python.exe C:/Users/Administrator/PycharmProjects/python18/day4/函数嵌套.py 3
函数的嵌套定义:在一个函数的内部,又定义另外一个函数
2、名称空间和作用域
名称空间:存放名字的地方,准确的说名称空间是存放名字与变量值绑定关系的地方
名称空间共有三种名称空间既:
1、内置名称空间:在python解释器启动时产生,存放一些python内置的名字
2、全局名称空间:在执行文件时产生,存放文件级别定义的名字
3、局部名称空间:在执行文件的过程中,如果调用了函数,则会产生该函数的局部名称空间,用来存放该函数内定义的名字,该名字在函数调用时生效,在函数调用结束后失效
加载顺序:内置===》全局===》局部
优先掌握一:名字的查找顺序是:局部==》全局===》内置
作用域:作用的范围
全局作用域:全局存货,全局有效:globals()
局部作用域:临时存活,局部生效:locals()
x=11111111111111111111111111111111111111111111 # def f1(): # x=1 # y=2 # def f2():pass # # print(locals()) # print(globals()) # # f1() # print(locals() is globals()) # print(locals()) # # print(dir(globals()['__builtins__'])) #global nonlocal掌握 # x=1 # def f1(): # global x # x=2 # # f1() # print(x) # l=[] # def f2(): # l.append('f2') # # f2() # print(l) # x=0 # def f1(): # # x=1 # def f2(): # # x=2 # def f3(): # # global x # nonlocal x # x=3 # f3() # # print(x) # f2() # print(x) # f1() # print(x)
优先掌握二:作用域关系,在函数定义时就已经固定,于调用位置无关,在调用函数时,必须必须必须回到函数原来定义的位置去找作用域关系
x=1 def f1(): def f2(): print(x) return f2 # func=f1() # print(func) # x=10000000 # func() # x=10000000 def foo(func): x=300000000 func() #f2() x=10000000000000000000000 foo(f1()) # x=10000000000000000000000 # foo(f1())
3、闭包函数
1. 定义在函数内部的函数
2. 包含对外部作用域名字的引用,而不是对全局作用域名字的引用,那么该内部函数就称为闭包函数
# x=1 # def f1(): # x=11111111111 # def f2(): # print(x) # return f2 # # func=f1() # x=1000 # func() # def foo(): # x=12312312312312312312312312312312312313123 # func() # # # foo() # def deco(): # x=123123123123 # def wrapper(): # print(x) # return wrapper # # func=deco() # func()
闭包函数的应用:惰性计算
import requests #pip3 install requests # def get(url): # return requests.get(url).text # # print(get('https://www.python.org')) # print(get('https://www.python.org')) # print(get('https://www.python.org')) # print(get('https://www.python.org')) # def index(url): # # url='https://www.python.org' # def get(): # # return requests.get(url).text # print(requests.get(url).text) # # return get # # python_web=index('https://www.python.org') # baidu_web=index('https://www.baidu.com') # python_web() # baidu_web() name='egon' def index(url): x=1 y=2 def wrapper(): # x # y # return requests.get(url).text print(name) return wrapper python_web=index('https://www.python.org') # print(python_web.__closure__[0].cell_contents) print(python_web.__closure__) # print(python_web.__closure__[0].cell_contents) # print(python_web.__closure__[1].cell_contents) # print(python_web.__closure__[2].cell_contents)
4、装饰器
1、 开放封闭原则:对扩展是开放的,对修改是封闭
2、 装饰器:装饰它人的工具,装饰器本身可以是任意可调用对象,被装饰的对象本身也可以是任意可调用对象
2.1 装饰器的遵循的原则:
2.1.1、 不修改被装饰对象的源代码
2.1.2、 不修改被调用对象的调用方式
装饰器名,必须写在被装饰对象的正上方,并且是单独一行
import time def timmer(func): # func=index def wrapper(): start=time.time() func() stop=time.time() print('run time is %s' %(stop-start)) return wrapper @timmer # index=timmer(index) def index(): time.sleep(3) print('welcome to index') @timmer # home=timmer(home) def home(): time.sleep(2) print('welcome to home page') index() home()
装饰器补充
#补充一:wraps # import time # from functools import wraps # # def timmer(func): # @wraps(func) # def wrapper(*args,**kwargs): # start=time.time() # res=func(*args,**kwargs) # stop=time.time() # print('run time is %s' %(stop-start)) # return res # return wrapper # # # @timmer # index=timmer(index) # def index(): # '''这是index函数''' # time.sleep(3) # print('welcome to index') # return 123 # # print(index.__doc__) # # print(help(index))
#补充二:一个函数头顶上可以多个装饰器 import time from functools import wraps current_user={'user':None} def timmer(func): @wraps(func) def wrapper(*args,**kwargs): start=time.time() res=func(*args,**kwargs) stop=time.time() print('run time is %s' %(stop-start)) return res return wrapper def auth(auth_type='file'): def deco(func): def wrapper(*args, **kwargs): if auth_type == 'file': if current_user['user']: return func(*args, **kwargs) name = input('name: ').strip() password = input('password: ').strip() with open('db.txt', encoding='utf-8') as f: user_dic = eval(f.read()) if name in user_dic and password == user_dic[name]: res = func(*args, **kwargs) current_user['user'] = name return res else: print('user or password error') elif auth_type == 'mysql': print('mysql') elif auth_type == 'ldap': print('ldap') else: print('not valid auth_type') return wrapper return deco @timmer #index=timmer(wrapper) @auth() # @deco #index=deco(index) #wrapper def index(): '''这是index函数''' time.sleep(3) print('welcome to index') return 123 # print(index.__doc__) # print(help(index)) index()
5、生成器
学习生成器之前,我们先来看看什么是列表生成式
#列表生成式 b = [ i*2 for i in range(10)] print(b) ###########打印输出########### #[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,还需要花费很长时间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种调用时才会生成相应数据的机制,称为生成器:generator
要创建一个generator,有很多种方法,第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个生成器
#生成器 l = [ i*2 for i in range(10)] print(l) g = (i*2 for i in range(10)) print(g) ###########打印输出########### #[0, 2, 4, 6, 8, 10, 12, 14, 16, 18] #<generator object <genexpr> at 0x0064AAE0>
print(g) 打印出来的信息显示g是一个生成器,创建l
和g
的区别仅在于最外层的[]
和()
,l
是一个list,而g
是一个generator;我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?如果要一个一个打印出来,可以通过next()
函数获得generator的下一个返回值
#生成器next打印 print(next(g)) #......... 不断next 打印10次 #.......... print(next(g)) ###########打印输出########### #0 #........ #18 #Traceback (most recent call last): # File "<stdin>", line 1, in <module> #StopIteration
我们讲过,generator保存的是算法,每次调用next(g)
,就计算出g
的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration
的错误。
上面这种不断调用next(g)
实在是太变态了,正确的方法是使用for
循环,因为generator也是可迭代对象,所以,我们创建了一个generator后,基本上永远不会调用next()
,而是通过for
循环来迭代它,并且不需要关心StopIteration
的错误
#生成器for调用 g = (i*2 for i in range(10)) #不用担心出现StopIteration错误 for i in g: print(i) ###########打印输出########### # 0 # 2 # 4 # 6 # 8 # 10 # 12 # 14 # 16 # 18
generator非常强大。如果推算的算法比较复杂,用列表生成式转换的生成器无法去实现时,我们还可以用函数来实现。比如,著名的斐波拉契数列(Fibonacci)
#函数表示斐波拉契数列 def fib(max): n, a, b = 0, 0, 1 while n < max: print(b) a, b = b, a + b n += 1 return 'done' fib(5) ###########打印输出########### # 1 # 1 # 2 # 3 # 5
仔细观察,可以看出,fib
函数实际上是定义了斐波那契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator;也就是说,上面的函数和generator仅一步之遥,那我们能不能把上面的函数变成一个生成器呢?
#斐波拉契数列转换为generator def fib(max): n, a, b = 0, 0, 1 while n < max: #print(b) yield b a, b = b, a + b n += 1 return 'done' print(type(fib(5))) #打印fib(5)的类型 for i in fib(5): #for循环去调用 print(i) ###########打印输出########### # <class 'generator'> # 1 # 1 # 2 # 3 # 5
要把fib
函数变成generator,只需要把print(b)
改为yield b
就可以了,这就是定义generator的另一种方法。如果一个函数定义中包含yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator
但是用for
循环调用generator时,会发现拿不到generator的return
语句的返回值,也就是return的值没有打印出来,现在我们来看看怎么去打印generator的返回值
#获取generator的返回值 def fib(max): n, a, b = 0, 0, 1 while n < max: #print(b) yield b a, b = b, a + b n += 1 return 'done' g = fib(5) while True: try: x = next(g) print( x) except StopIteration as e: print(e.value) break ###########打印输出########### # 1 # 1 # 2 # 3 # 5 # done
如果想要拿到返回值,必须捕获StopIteration
错误,返回值包含在StopIteration
的value
中,关于如何捕获错误,后面的错误处理还会详细讲解。
还可通过yield实现在单线程的情况下实现并发运算的效果
import time def consumer(name): print("%s 准备吃包子啦!" %name) while True: baozi = yield print("包子[%s]来了,被[%s]吃了!" %(baozi,name)) def producer(name): c = consumer('A') c2 = consumer('B') c.__next__() #c.__next__()等同于next(c) c2.__next__() print("老子开始准备做包子啦!") for i in range(10): time.sleep(1) print("%s做了2个包子!"%(name)) c.send(i) c2.send(i) producer("lzl")
6、迭代器
迭代:是一个重复的过程,每一次重复,都是基于上一次的结果而来
# while True: #单纯的重复 # print('你瞅啥') # l=['a','b','c','d'] # count=0 # while count < len(l): # print(l[count]) # count+=1 dic={'name':'egon','sex':'m',"age":18} #上述按照索引的取值方式,不适于没有索引的数据类型
迭代器:
可迭代对象iterable:凡是对象下有__iter__方法:对象.__iter__,该对象就是可迭代对象
# s='hello' # l=['a','b','c','d'] # t=('a','b','c','d') # dic={'name':'egon','sex':'m',"age":18} # set1={1,2,3} # f=open('db.txt') # s.__iter__() # l.__iter__() # t.__iter__() # dic.__iter__() # set1.__iter__() # f.__iter__()
迭代器对象:可迭代对象执行内置的__iter__方法,得到的结果就是迭代器对象
# dic={'name':'egon','sex':'m',"age":18} # # i=dic.__iter__() # # print(i) #iterator迭代器 # # # i.__next__() #next(i) # print(next(i)) # print(next(i)) # print(next(i)) # print(next(i)) #StopIteration # # l=['a','b','c','d'] # # i=l.__iter__() # print(next(i)) # print(next(i)) # print(next(i)) # print(next(i)) # print(next(i)) #StopIteration
不依赖于索引的取值方式
# l=['a','b','c','d'] # dic={'name':'egon','sex':'m',"age":18} # iter_l=iter(l) # iter_dic=iter(dic) # while True: # try: # # print(next(iter_l)) # k=next(iter_dic) # print(k,dic[k]) # except StopIteration: # break
什么是迭代器对象:
1 有__iter__,执行得到仍然是迭代本身
2 有__next__
迭代器对象的优点
1:提供了一种统一的(不依赖于索引的)迭代方式
2:迭代器本身,比起其他数据类型更省内存
# l=['a','b','c','d'] # i=iter(l) # dic={'a':1,'b':2} # x=dic.keys() # print(x) # i=x.__iter__() # # with open('a.txt') as f: # # print(next(f)) # # print(next(f)) # # print(next(f)) # f.read()
迭代器对象的缺点
1:一次性,只能往后走,不能回退,不如索引取值灵活
2:无法预知什么时候取值结束,即无法预知长度
# l=['a','b','c','d'] # i=iter(l) # print(next(i)) # print(next(i)) # print(next(i))