本文共 12007 字,大约阅读时间需要 40 分钟。
[toc]
在python中,函数是一等对象,“一等对象”满足下述条件的程序实体:
def foo(): print('from foo')func=fooprint(foo)print(func)func()# 输出:from foo
def foo(): print('from foo')def bar(func): print(func) func() # 此时func() == foo()bar(foo) #将函数foo内存对象当做参数传给bar()# 输出:from foo
def foo(): print('from foo')def bar(func): return funcf=bar(foo) # 此时f = fooprint(f)f() # 此时执行f() 相当于执行foo()# 输出:from foo
def foo(): print('from foo') dic={'func':foo} # 定义一个字典dic,将函数foo的内存对象做为valueprint(dic['func']) # 相当于打印foo的内存对象dic['func']() # 相当于执行foo()函数# 输出:from foo
应用:
def select(sql): print('========>select')def insert(sql): print('========>add')def delete(sql): print('=======>delete')def update(sql): print('=======>update')func_dic={ 'select':select, 'update':update, 'insert':insert, 'delete':delete} # 定义一个字典,将上面定义的函数名做为valuedef main(): while True: sql = input('>>: ').strip() if not sql:continue l = sql.split() cmd=l[0] if cmd in func_dic: func_dic[cmd](l)main()
简单理解就是在定义一个函数时,函数体内又定义一个子函数,示例如下:
def f1(): def f2(): print('from f2') def f3(): print('from f3') f3() f2()f1()# 输出:from f2from f3
下面示例是一个求4个数中最大值的小程序:
# 嵌套函数def mymax(x,y): return x if x > y else ydef four(a,b,c,d): res1=mymax(a,b) # 调用mymax()函数先比较a和b,较大的值给res1 res2=mymax(c,res1) res3=mymax(d,res2) return res3print(four(8,45,9,34))# 输出:45
import time
name = 'caigy'
def func(): pass
class foo: pass
随着Python解释器的启动而产生,比如python内置的一些函数:sun()、max()、min()等,这些函数随着python的启动就定义好了,所以在定义名称时不要与这些关键字重名
可以用以下方法查看python的内置函数:import builtinsfor i in dir(builtins): print(i)
py文件的执行会产生全局名称空间,指的是文件级别定义的名字都会放入该空间
调用函数时会产生局部名称空间,只在函数调用时临时绑定,函数调用结束后解除绑定。
如下示例:
# !/user/bin/env python# -*- coding:utf-8 -*-name = 'caigy' # 1def func(): x = 1 # 2
代码中:
python中名字的查找顺序:
局部名称空间 ---> 全局名称空间 ---> 内置名称空间示例代码:
name = 'caigy'def foo(): name = 'egon' print(name) print(locals()) print(globals())foo()
以上代码输出结果:
egon{'name': 'egon'}{'__file__': 'D:/PycharmProjects/s17/day04/test.py', 'foo':, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x01755A90>, 'name': 'caigy', '__name__': '__main__', '__spec__': None, '__package__': None, '__builtins__': , '__doc__': None, '__cached__': None}
作用域的有效范围
注:当全局变量与局部变量同名时,在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用
闭包函数的特性:
该内部函数就称为闭包函数
如下示例:def func(): name = 'caigy' def foo(): print(name) return foof = func() # func()返回结果是foo ,所以f = fooprint(f) # 打印的实际上是foo的内存对象地址f()# 输出结果:.foo at 0x01342150>caigy
如上述代码中,foo()就是一个闭包函数
闭包函数的应用:随性计算
爬取一个网页from urllib.request import urlopendef index(url): def get(): return urlopen(url).read() return getf= index('http://www.360duohui.com')res = f().decode('utf-8')print(res)
上述代码中get()函数就是一个闭包函数
总结:闭包函数实现了内部函数在外部可以被调用
获取闭包函数所引用的外部变量(非全局变量)的值
def func(): name='caigy' def foo(): print(name) return foof = func()print(f.__closure__[0].cell_contents)# 输出:caigy
装饰器,顾名思义,就是用来装饰用的,具体点说就是给其它程序添加功能的。其本质就是函数,功能是为其他函数添加新功能。
装饰器本身可以是任何可调用对象,被装饰的对象也可以是任意可调用的对象。
源于程序开的开放封装原则:
- 对修改是封闭的,对扩展是开放的。
- 装饰器就是为了在不修改被装饰对象的源代码以及调用方式的前提下,为其添加新功能
如下示例:
def run(): time.sleep(3) print('already test 3s')run()
为上述代码添加一个打印日志的功能:
无参装饰器import timedef timer(func): def wrapper(*args,**kwargs): start_time = time.time() func() #3 stop_time = time.time() print('run is %ss'%(stop_time-start_time)) return wrappertimer #1def run(): time.sleep(3) print('already test 3s')run() #2
#1 @timer 实际上就是做了run = timer(run) 的操作;
#2 因为timer()函数的返回值是wrapper,所以此时的run = wrapper , 所run()实际就相当于调用了wrapper()#3 run 做为参数传给timer()函数,所以此时func=run,此时的func()相当于调用了run()
以上代码的输出结果如下:
already test 3srun is 3.0033679008483887s
有参装饰器
import timedef timer(func_1): def wrapper_1(*args,**kwargs): start_time = time.time() func_1(*args,**kwargs) #3 stop_time = time.time() print('run is %s秒 '%(stop_time-start_time)) return wrapper_1user_stat = {'name':None,'stats':False}def auth(func): def wrapper(*args,**kwargs): if user_stat['name'] and user_stat['stats']: print('login successful') func(*args, **kwargs) else: name = input('name: ') pwd = input('password: ') if name == 'egon' and pwd == '123': # 登录成功后记录用户名和登录状态,下次调用时就不用再输入用户名登录了 user_stat['name'] = 'egon' user_stat['stats'] = True print('login successful') func(*args,**kwargs) # time.sleep(3) else: print('login error') return wrapper@timer #2@auth #1 def index(): print('Welcome to Oldboyedu.com')@authdef home(name): print('Welcome to %s homepage'%name)index() #4home('egon')
输出结果:
name: egonpassword: 123login successfulWelcome to Oldboyedu.comrun is 3.2893900871276855秒 login successfulWelcome to egon homepage
#1 @auth 实际上就是做了index = auth(index)的操作,因为auth()函数的返回值是wrapper,所以index此时等于wrapper,在下方加括号()就可以直接调用wrapper()函数了
#2 由于此时@auth 为wrapper, 所以@timer 就是wrapper = timer(wrapper) , timer()的返回值是wrapper_1,所以此时wrapper = wrapper_1#3 由于wrapper被当作参数传给timer(),所以此时func_1 = wrapper#4 在执行index()时,相于当于执行wrapper_1() ;当程序执行到func_1(args,*kwargs)时,此时的func_1 就是 wrapper,所以就会调用wrapper(args,*kwargs);当wrapper()函数内的程序执行到func(args,*kwargs)时,此时的func = index ,所以用调用真实定义的index()函数;然后返回wrapper_1(),继续执行func_1()后的代码。
重复的过程称为迭代,每次重复即为一次迭代,并且每次迭代的结果作为下一次迭代的初始值;
不是迭代:while True: #只满足重复,因而不是迭代 print('====>')
下面才为迭代
l = [1, 2, 3]count = 0while count < len(l): print('====>', l[count]) count += 1# 输出:====> 1====> 2====> 3
注:以上这种方式迭代的是列表,而列表是有序的对象,那像无序的对象,比如:字典,集合,文件等如何迭代呢?
对于没有索引的数据类型,必须提供一种不依赖索引的迭代方式
可迭代的对象:内置__iter__
方法的对象,都是可迭代的对象 [1,2].__iter__() # 列表'hello'.__iter__() # 字符串(1,2,).__iter__() # 元组{'a':1,'b':2}.__iter__() # 字典{1,2,3}.__iter__() # 集合
迭代器: 执行__iter__
方法,得到的结果就是迭代器,迭代对象都具体__next__
方法
i = [1,2,3].__iter__()print(i) # 打印可迭代对象的内存地址print(i.__next__())print(i.__next__())print(i.__next__())print(i.__next__()) # 超出迭代的对象个数后会抛出异常:StopIteration# 输出:123Traceback (most recent call last): File "D:/PycharmProjects/s17/day04/test.py", line 209, in print(i.__next__())StopIteration
迭代字典
i={'a':1,'b':2,'c':3}.__iter__()print(i.__next__()) # 迭代字典中的keyprint(i.__next__())print(i.__next__())print(i.__next__()) # 超出字典i中的键值对的个数,会抛出异常:StopIteration
换一种方法:
i={'a':1,'b':2,'c':3}I = iter(i)print(next(I))print(next(I))print(next(I))
注:
__iter__()
=iter()
,__next__()
=next()
,两种方法得到的结果是一样的。
利用while循环迭代字典
dic={'a':1,'b':2,'c':3}i=dic.__iter__() # 生成迭代器对象while True: try: key=i.__next__() print(dic[key]) except StopIteration: # 遇到指定异常后,执行子代码 break
__iter__
方法,执行该方法得到的是迭代器对象__next__
方法和__iter__
方法,迭代器对象执行__iter__方法,
得到的结果仍然是它本身。判断可迭代的对象需要导入Iterable
模块
from collections import Iterable,Iteratorf = open('a.txt','w')f.__iter__()# 下列数据类型都是可迭代的对象print(isinstance('abc',Iterable)) # 字符串print(isinstance([1,2,3],Iterable)) # 列表print(isinstance({'a':1,},Iterable)) # 字典print(isinstance({1,2,3},Iterable)) # 集合print(isinstance((1,2,),Iterable)) # 元组print(isinstance(f,Iterable)) # 文件 # 输出:TrueTrueTrueTrueTrueTrue
判断迭代器对象需要导入Iterator
模块
from collections import Iterable,Iteratorf = open('a.txt','w')f.__iter__()# 只有文件是迭代器对象print(isinstance('abc',Iterator))print(isinstance([],Iterator))print(isinstance((),Iterator))print(isinstance({'a':1},Iterator))print(isinstance({1,2},Iterator))print(isinstance(f,Iterator))# 输出:FalseFalseFalseFalseFalseTrue
验证迭代器对执行__iter__
方法得到的结果仍然是其本身
f = open('a.txt','w')f1 = f.__iter__()print(f)print(f1)print(f1 is f) # 输出`True`表示结论正确 # 输出:<_io.TextIOWrapper name='a.txt' mode='w' encoding='cp936'><_io.TextIOWrapper name='a.txt' mode='w' encoding='cp936'>True
其特点:
优点:
缺点:
验证:只能往后取值,不能往前退
l=[10000,2,3,4,5] i=iter(l) for item in i: print(item) print('=====================') for item in i: print(item) # 输出: 10000 2 3 4 5 ==================== # 通过上述代码 的结果可以看出,通过列表l生成的迭代器i ,通过for循环迭代后,再次通过for循环迭代,就取不到值了
扩展:enumerate()
方法生成的也是迭代器对象
l=[2,3,4]i=enumerate(l)print(next(i))print(next(i))print(next(i))# 输出:(0, 2)(1, 3)(2, 4)
总结:python中for循环就是通过迭代器的方式来实现的,而while只是普通的循环,for迭代 == while + try ... except(异常处理)
只要函数体内包含yield关键字,该函数就是生成器函数
生成器就是迭代器
def index(): print('first') yield 1 print('second') yield 2 print('third') yield 3g = index()for i in g: print(i)
上述代码输出:
first1second2third3
通过next()方法:
def index(): print('first') yield 1 print('second') yield 2 print('third') yield 3g = index()print(next(g)) # next()方法触发迭代器g的执行,进而触发函数的执行print(next(g))print(next(g))# 输出:first1second2third3
生成器应用 :
def counter(n): print('start...') i=0 while i < n: yield i i+=1 print('end...')g=counter(5)print(g) # 这一步不会执行counter()函数,如果是普通函数,就会被执行print(next(g))print(next(g))print(next(g))print(next(g))print(next(g))# 输出:start...01234
注:上述代码中,迭代器对象g,只有通过next()方法(调用时,才会触发函数counter()的执行
总结:
yield的功能:
- 相当于为函数封装好
__iter__
和__next__
return
只返回一次值,函数就终止了,而yield
能返回多次值,每次返回都会将函数暂停,下一次next()
会从上一次暂停的位置继续执行
def foo(): print('in the foo ...') food = yield '您好' print('food >>>',food) print('我在后面') food1= yield '你坏' print('food1 >>> ',food1)g= foo()res = next(g)print(res)res1 = g.send('x')print(res1)##res2= g.send('xx')'''生成器执行流程:1.g=foo(),只是将foo()生成器对象赋值给g,然后继续往下执行;2.遇到next()或g.send('x')就开始执行foo()内部的代码,\ 执行遇到第一个yield时,就暂停(我也理解为进入休眠状态),\ 并将yield后面的值返回给next(g),并跳出到foo()外面next(g)所在的那一行,\ 将yield返回的值赋值给res3.res接收yield返回给next(g)的值,然后往下执行代码,打印res的值;4.当再次遇到next()或g.send('x')时,唤醒foo()继续从上次 \ 暂停的位置开始执行, 同时将g.send(‘x’)中的'x'发送 \ 给第一个yield,并赋值给food,然后继续往下执行;5.当遇到第二个yield时,进入暂停(休眠),\ 同时将yield后面的值返回给g.send('x'),\ 跳出到g.send('x')所在的那一行,并将yield返回的值赋值给res1,\ 然后继续执行至结束。 注意: print(res1)后面没有代码了,此时foo()中的food1是空,\ 如果print(res1)后面再出现g.send('xx')代码,\ 才会将'xx'发送给第二个yield,并赋值给food1; 但是,foo()内部会从第二个yield那一行继续往下执行,\ 如果后面没有yield关键字了,程序就会抛出一个StopIteration异常。'''
生成器的应用:
实现Linux命令tail -f a.txt | grep 'python'
的功能 import timedef tail(filepath): with open(filepath,encoding='utf-8') as f: f.seek(0,2) # 跳到文件末尾 while True: line=f.readline().strip() if line: yield line #3 else: time.sleep(0.2)def grep(pattern,lines): #1 for line in lines: #2 if pattern in line: #4 yield line #5g=grep('python',tail('a.txt'))print(g)for i in g: #6 print(i)
程序解析:
#1 pattern = 'python' , lines = tail('a.txt')#2 此时的lines是一个迭代器,经过for循环会触发tail('a.txt')函数的执行,这时运行tail()函数内的程序,会通过while循环监控文件末尾是否有内容#3 如果有新内容,则通过yield返回#4 第#3通过yield返回的line传给#4行的line#5 这里将line通过yield返回给#6#6 这时i = line
转载地址:http://rgfsx.baihongyu.com/