一、函数对象
函数是第一类对象,即函数可以当作数据传递
1 可以被引用2 可以当作参数传递3 返回值可以是函数4 可以当作容器类型的元素
def foo(): print('from foo')func=foo #将函数赋值给funcprint(foo) #打印函数,将返回函数在内存中的地址print(func) #此时func = foofunc() #加上大括号才能引用函数
运行结果:from foo
def foo(): print('from foo')def bar(foo): #给函数bar一个参数foo print(foo) #打印foo,其实就是打印foo函数在内存中的地址 foo() #引用foo函数bar(foo)
运行结果:from foo
def foo(): print('from foo')def bar(foo): return foo #返回值是foo函数在内存中的地址f=bar(foo)print(f) #取bar函数的返回值f()
运行结果:from foo
def foo(): print('from foo')dic={'func':foo}print(dic['func']) #打印字典key名为func的值foo,也就是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}def 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 main(): sql = input('>>: ') l = sql.split() print(l) if l[0] == 'select': select(l) elif l[0] == 'insert': insert(l) elif l[0] == 'delete': delete(l) elif l[0] == 'update': update(l)
二、函数的嵌套
1.函数的嵌套调用
def max2(x,y): #取最大值 return x if x > y else ydef max4(a,b,c,d): res1=max2(a,b) #两两比较,取最大值 res2=max2(res1,c) res3=max2(res2,d) return res3print(max4(10,99,31,22))
运行结果:99
name = "Alex"def change_name(): #先调用这层 name = "Alex2" def change_name2(): #再调用这层 name = "Alex3" print("第3层打印", name) change_name2() # 调用内层函数 print("第2层打印", name)change_name()print("最外层打印", name) #最后到这层
运行结果:第3层打印 Alex3第2层打印 Alex2最外层打印 Alex
2.函数的嵌套定义
注意:定义函数在运行程序时不会执行,仅判断语法是否有错误
def f1(): def f2(): print('from f2') def f3(): print('from f3') f3() f2()f1()
运行结果:from f2from f3
三、名称空间与作用域
1.定义名字的方法
import timename='egon'def func(): passclass Foo: pass
2.三种名称空间
(1)内置名称空间
随着python解释器的启动而产生,內建函数属于内置名称空间内
print(sum)print(max)print(min)print(max([1,2,3]))import builtinsfor i in dir(builtins): #查看內建函数 print(i)
(2)全局名称空间
文件的执行会产生全局名称空间,指的是文件级别定义的名字都会放入该空间
x=1if x ==1 : y=2import timename='egon'def func(): passclass Foo: pass
x=1def func(): money=2000 x=2 print('func')print(x)print(func)func()print(money) #money属于func函数内的局部命名空间func()print(x)
报错信息:Traceback (most recent call last):1 File "E:/s17/day04/名称空间与作用域.py", line 59, inprint(money) NameError: name 'money' is not definedfunc
(3)局部名称空间
调用函数时会产生局部名称空间,只在函数调用时临时绑定,调用结束解绑定
x=10000 #全局def func(): x=1 #局部 def f1(): pass
3.作用域
(1)全局作用域:内置名称空间,全局名层空间
(2)局部作用:局部名称空间名字的查找顺序:局部名称空间---》全局名层空间---》内置名称空间
x=1def func(): x=2 print(x) sum=123123 print(sum)func()
运行结果:2123123
def func(): x=2func()print(x) #此时调用不到x
报错信息:Traceback (most recent call last): File "E:/s17/day04/名称空间与作用域.py", line 94, inprint(x)NameError: name 'x' is not defined
查看全局作用域内的名字:gloabls()
查看局局作用域内的名字:locals()
x=1000def func(): x=2print(globals())print(locals())print(globals() is locals())
运行结果:{'func':, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000000626DA0>, '__spec__': None, 'x': 1000, '__cached__': None, '__doc__': '\n作用域:\n 1. 全局作用域:内置名称空间,全局名层空间\n 2. 局部作用:局部名称空间\n', '__builtins__': , '__package__': None, '__file__': 'E:/s17/day04/名称空间与作用域.py', '__name__': '__main__'}{'func': , '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000000626DA0>, '__spec__': None, 'x': 1000, '__cached__': None, '__doc__': '\n作用域:\n 1. 全局作用域:内置名称空间,全局名层空间\n 2. 局部作用:局部名称空间\n', '__builtins__': , '__package__': None, '__file__': 'E:/s17/day04/名称空间与作用域.py', '__name__': '__main__'}True
x=1000def func(y): x=2 print(locals()) print(globals())func(1)
运行结果:{'y': 1, 'x': 2}{'__file__': 'E:/s17/day04/名称空间与作用域.py', '__doc__': '\n作用域:\n 1. 全局作用域:内置名称空间,全局名层空间\n 2. 局部作用:局部名称空间\n', '__package__': None, 'func':, '__name__': '__main__', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000000646DA0>, '__builtins__': , 'x': 1000, '__cached__': None, '__spec__': None}
全局作用域:全局有效,在任何位置都能被访问到,除非del删掉,否则会一直存活到文件执行完毕
局部作用域的名字:局部有效,只能在局部范围调用,只在函数调用时才有效,调用结束就失效
x=1def f1(): print(x)def foo(): print(x)def f(x): # x=4 def f2(): # x=3 def f3(): # x=2 print(x) f3() f2()f(4)
运行结果:4
四、闭包
- 定义在内部函数
- 包含对外部作用域而非全局作用域的引用,该内部函数就成为闭包函数
def f1(): x = 1 def f2(): print(x) return f2f=f1()print(f)x=100000000000000000000000000f()
运行结果:.f2 at 0x00000000006B01E0>1
闭包应用:惰性计算
抓取网页
from urllib.request import urlopendef index(url): def get(): return urlopen(url).read() return getoldboy=index('http://crm.oldboyedu.com')print(oldboy().decode('utf-8'))print(oldboy.__closure__[0].cell_contents)res=urlopen('http://crm.oldboyedu.com').read()print(res.decode('utf-8'))
x=1y=2def f1(): x=1 y=2 def f2(): print(x,y) return f2f=f1()print(f.__closure__[0].cell_contents)
运行结果:1
五、装饰器
装饰器:修饰别人的工具,修饰添加功能,工具指的是函数
装饰器本身可以是任何可调用对象,被装饰的对象也可以是任意可调用对象
为什么要用装饰器:
- 开放封闭原则:对修改是封闭的,对扩展是开放的
- 装饰器就是为了在不修改被装饰对象的源代码以及调用方式的前提下,为期添加新功能
在修改代码时,为了不对源代码产生影响而使用装饰器
import timedef 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 wrapper@timmer #对源代码进行装饰,index=timmer(index)def index(): time.sleep(3) print('welcome to index')f=timmer(index)print(f)f() #wrapper()---->index()index=timmer(index) #index==wrapperindex() #wrapper()----->
流程分析
import timedef 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 #装饰器 index=timmer(index)def index(): time.sleep(3) print('welcome to index') return 1@timmerdef foo(name): time.sleep(1) print('from foo')res=index() #wrapper()print(res)res1=foo('egon') #res1=wrapper('egon')print(res1)
运行结果:welcome to indexrun time is 3.0128052234649661from foorun time is 1.0140018463134766None
应用
login_user={'user':None,'status':False}def auth(func): def wrapper(*args,**kwargs): if login_user['user'] and login_user['status']: res=func(*args,**kwargs) return res else: name=input('>>: ') password=input('>>: ') if name == 'egon' and password == '123': login_user['user']='egon' login_user['status']=True print('\033[45mlogin successful\033[0m') res=func(*args,**kwargs) return res else: print('\033[45mlogin err\033[0m') return wrapper@authdef index(): print('welcome to index page')@authdef home(name): print('%s welcome to home page' %name)index()home('egon')
有参装饰器
import timedef timmer(func): def wrapper(*args,**kwargs): print('====>timmer.wrapper') start_time=time.time() res=func(*args,**kwargs) #auth_wrapper stop_time=time.time() print('run time is %s' %(stop_time-start_time)) return res return wrapperlogin_user={'user':None,'status':False}def auth(driver='file'): def auth2(func): def wrapper(*args,**kwargs): print('=======>auth.wrapper') time.sleep(5) if driver == 'file': if login_user['user'] and login_user['status']: res=func(*args,**kwargs) return res else: name=input('>>: ') password=input('>>: ') if name == 'egon' and password == '123': login_user['user']='egon' login_user['status']=True print('\033[45mlogin successful\033[0m') res=func(*args,**kwargs) return res else: print('\033[45mlogin err\033[0m') elif driver == 'ldap': print('==========ldap的认证') elif driver == 'mysql': print('==========mysql的认证') return func(*args,**kwargs) else: print('=========未知的认证来源') return wrapper return auth2@auth('file') #@auth2====>index=auth2(index)===>index=auth_wrapper@timmer #index=timmer(auth_wrapper) #index=timmer_wrapperdef index(): time.sleep(3) print('welcome to index page')@auth(driver='mysql')def home(name): print('%s welcome to home page' %name)index() #timmer_wrapper()home('egon') #wrapper('egon')
六、迭代器
迭代的概念:重复+上一次迭代的结果为下一次迭代的初始值,重复的过程称为迭代,每次重复即一次迭代,并且每次迭代的结果是下一次迭代的初始值
迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件
特点:
- 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
- 不能随机访问集合中的某个值 ,只能从头到尾依次访问
- 访问到一半时不能往回退
- 便于循环比较大的数据集合,节省内存
while True: #只满足重复,因而不是迭代 print('====>')
真正的迭代如下:
l = [1, 2, 3]count = 0while count < len(l): print('====>', l[count]) count += 1l = (1, 2, 3)count = 0while count < len(l): print('====>', l[count]) count += 1s='hello'count = 0while count < len(s): print('====>', s[count]) count += 1
为什么要有迭代器?对于没有索引的数据类型,必须提供一种不依赖索引的迭代方式
可迭代的对象:内置__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
dic={'a':1,'b':2,'c':3}i=dic.__iter__()while True: try: key=i.__next__() print(dic[key]) except StopIteration: break
如何判断一个对象是可迭代的对象,还是迭代器对象
from collections import Iterable,Iterator
'abc'.__iter__()
().__iter__() [].__iter__() {'a':1}.__iter__() {1,2}.__iter__()f=open('a.txt','w')
f.__iter__()下列数据类型都是可迭代的对象,只有文件是迭代器对象
print(isinstance('abc',Iterable))
print(isinstance([],Iterable)) print(isinstance((),Iterable)) print(isinstance({'a':1},Iterable)) print(isinstance({1,2},Iterable)) print(isinstance(f,Iterable))可迭代对象:只有__iter__方法,执行该方法得到的迭代器对象
迭代协议:
- 对象有__next__
- 对象有__iter__,对于迭代器对象来说,执行__iter__方法,得到的结果仍然是它本身
迭代器的优点和缺点
优点:
- 提供了一种不依赖下标的迭代方式
- 就跌迭代器本身来说,更节省内存
缺点:
- 无法获取迭代器对象的长度
- 不如序列类型取值灵活,是一次性的,只能往后取值,不能往前退
七、生成器
生成器函数:只要函数体包含yield关键字,该函数就是生成器函数
生成器就是迭代器一个函数调用时返回一个迭代器,那这个函数就叫做生成器(generator);如果函数中包含yield语法,那这个函数就会变成生成器;
def func(): yield 1 yield 2 yield 3 yield 4
上述代码中:func是函数称为生成器,当执行此函数func()时会得到一个迭代器。
运行结果:>>> temp = func()>>> temp.__next__()1>>> temp.__next__()2>>> temp.__next__()3>>> temp.__next__()4>>> temp.__next__()Traceback (most recent call last): File "", line 1, in StopIteration
def foo(): print('first') yield 1 print('second') yield 2 print('third') yield 3 print('fourth') yield 4 print('fifth')g=foo()for i in g: print(i)print(next(g)) #触发迭代器g的执行,进而触发函数的执行
运行结果:first1second2third3fourth4fifth
yield的功能:
- 相当于为函数封装好__iter__和__next__
return只能返回一次值,函数就终止了,而yield能返回多次值,每次返回都会将函数暂停,下一次next会从上一次暂停的位置继续执行
应用: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 else: time.sleep(0.2)t=tail('a.txt')for line in t: print(line) def grep(pattern,lines): for line in lines: if pattern in line: yield line g=grep('python',tail('a.txt'))print(g)for i in g: print(i)
八、递归
在函数内部,可以调用其他函数。如果在调用一个函数的过程中直接或间接调用自身本身
def calc(n): print(n) if int(n/2) ==0: return n return calc(int(n/2)) calc(10)
运行结果:10521
应用:递归问路
#_*_coding:utf-8_*___author__ = 'Linhaifeng'import timeperson_list=['alex','wupeiqi','yuanhao','linhaifeng']def ask_way(person_list): print('-'*60) if len(person_list) == 0: return '没人知道' person=person_list.pop(0) if person == 'linhaifeng': return '%s说:我知道,老男孩就在沙河汇德商厦,下地铁就是' %person print('hi 美男[%s],敢问路在何方' %person) print('%s回答道:我不知道,但念你慧眼识猪,你等着,我帮你问问%s...' %(person,person_list)) time.sleep(3) res=ask_way(person_list) # print('%s问的结果是: %res' %(person,res)) return resres=ask_way(person_list)print(res)
递归特性:
- 必须有一个明确的结束条件
每次进入更深一层递归时,问题规模相比上次递归都应有所减少
递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
九、内置函数
内置参数详解 https://docs.python.org/3/library/functions.html?highlight=built#ascii