目录
- 函数补充进阶
- 函数对象
- 函数的嵌套
- 名称空间与作用域
- 闭包函数
- 函数之装饰器
- 函数之可迭代对象
- 函数之迭代器
- 函数之生成器
- 面向过程的程序设计思想
一、函数进阶之函数对象
1. 函数对象
秉承着一切皆对象的理念,我们再次回头来看函数(function)。函数也是一个对象,具有属性(可以使用dir()查询)。作为对象,它还可以赋值给给一个变量、可以作为元素添加到集合对象中、可作为参数值传递给其它函数,还可以当做函数的返回值,这些特性就是第一类对象所特有的。
- 函数身为一个对象,拥有对象模型的三个通用属性:id、类型、和值。
#!/usr/bin/env python #-*- coding:utf-8 -*- def foo(): print('from foo') print(id(foo)) # id属性 print(type(foo)) # 类型 func=foo # 将函数名赋值给一个变量 print(foo) # 输出函数的内存地址 print(func) # func变量指向foo函数的内存地址 func() # 加()调用执行 # 结果 ''' 7266512 <class 'function'> <function foo at 0x00000000006EE0D0> <function foo at 0x00000000006EE0D0> from foo '''
- 函数可以被引用
#!/usr/bin/env python # -*- coding:utf-8 -*- def foo(): print('from foo') foo() func=foo #引用,赋值 print(foo) print(func) func() #结果 ''' from foo <function foo at 0x10eed8f28> <function foo at 0x10eed8f28> from foo '''
- 可以当作参数传递,即赋值给变量或当作实参传递
def foo(): print('from foo') def bar(func): # 参数func为foo print(func) # func参数的内存地址 func() # 加()调用func bar(foo) # 将foo函数对象作为参数传递给bar函数并调用 #结果 ''' <function foo at 0x00000000007EE0D0> from foo '''
- 函数可以是返回值
def foo(): print('from foo') def bar(func): # 参数func为foo函数名 return func # 返回foo函数的内存地址 f=bar(foo) # 调用bar函数并将返回的foo函数内存地址赋值给f变量 print(f) # 输出对应的foo内存地址 f() # 加()即调用,此处相当于调用foo函数,即f()=foo() # 结果 ''' <function foo at 0x0000000000D4E0D0> from foo '''
- 可以当作容器类型的元素
注: 函数对应可以存放在容器元素(元组、列表、字典)和变量中
def foo(): print('from foo') dic = {'func':foo} # 将foo函数对象作为value放入字典中 foo() # 调用foo函数 print(dic['func']) # 字典dic的key值func对应的value为foo函数的内存对象,即foo函数对象作为容器中的一个元素存放 dic['func']() # 内存地址加()便可以调用,即dic['func]()=foo() #结果 ''' from foo <function foo at 0x00000000006EE0D0> from foo '''
- 应用
# def select(sql): # print('----〉select:%s' % sql) # # def insert(sql): # print('----〉insert:%s' % sql) # # def update(sql): # print('----〉update:%s' % sql) # # def delect(sql): # print('----〉delect:%s' % sql) # # sql_dic = { # 'select':select, # 'delect':delect, # 'insert':insert, # 'update':update # } # def main(): # while True: # sql = input('sql>>>').strip() # if not sql:continue # sql_l = sql.split(' ') # if sql_l[0] in sql_dic: # sql_dic[sql_l[0]](sql_l) # # main() ''' 结果: sql>>>select * form fafafa ----〉select:['select', '*', 'form', 'fafafa'] sql>>>insert * faormafa faf a ----〉insert:['insert', '*', 'faormafa', '', 'faf', 'a'] sql>>> '''
2. 函数嵌套
- 函数的嵌套定义
定义: 所谓嵌套,并不像其他语言中的在一个函数中调用另一个函数,而是在定义一个函数的时候,函数体里还能定义另一个函数。
为什么?因为函数是用def语句定义的,凡是其他语句可以出现的地方,def语句同样可以出现。
像这样定义在其他函数内的函数叫做内部函数,内部函数所在的函数叫做外部函数。当然,我们可以多层嵌套,这样的话,除了最外层和最内层的函数之外,其它函数既是外部函数又是内部函数。
特性: 内函数可以访问外函数的作用域。
def f1(): # def 关键字 函数f1 def f2(): # 函数f2 print('from f2') def f3(): # 函数f3 print('from f3') f3() # 调用f3 f2() # 调用 f1() # 调用f1函数 # 结果 ''' from f2 from f3 '''
- 函数的嵌套调用
# 判断两个数字的大小,返回最大值 def max2(x,y): return x if x > y else y # 判断4个数字的大小 def max4(a,b,c,d): res1=max2(a,b) # 调用msx2函数 res2=max2(res1,c) res3=max2(res2,d) return res3 print(max4(10,99,31,22)) # 结果 ''' 99 '''
3. 命名空间与作用域
共三种名称空间:
-
内置名称空间
-
全局名称空间
-
局部名称空间
a. 内置名称空间: 随着python解释器的启动而产生
当使用内建模块中函数或其它功能时,可以直接使用,不用添加内建模块的名字;但是,如果想要向内建模块中添加一些功能,以便在任何函数中都能直接使用而不 用再进行import,这时,就要导入内建模块,在内建模块的命名空间(即__dict__字典属性)中添加该功能。
print(sum) print(max) print(min) # built-in ''' <built-in function sum> <built-in function max> <built-in function min> ''' # 在Python2.X版本中,内建模块被命名为__builtin__,而到了Python3.X版本中,却更名为builtins。 import builtins for i in dir(builtins): print(i)
b. 全局名称空间:文件的执行会产生全局名称空间,指的是文件级别定义的名字都会放入该空间
x=1 # 全局 def func(): # 全局 money=2000 x=2 # 局部 print('from func') print(x) print(func) func() print(x) print(money) # 输出失败:NameError: name 'money' is not defined 未定义 # 结果 ''' 1 <function func at 0x0000000000A5E1E0> from func 1 '''
c. 局部名称空间:调用函数时会产生局部名称空间,只在函数调用时临时绑定,调用结束解绑定
x=10000 # 全局 def func(): x=1 # 局部,调用结束后即刻销毁 def f1(): pass
4. 作用域
命名空间的可见性就是作用域
- 全局作用域:内置名称空间,全局名称空间
- 局部作用域:局部名称空间
代码中名字的查找顺序:局部名称空间--->全局名层空间--->内置名称空间
a. 查看全局作用域内的函数:gloabls()
b. 查看局部作用域内的函数:locals()
生命周期
a. 全局作用域的名字:全局有效,在任何位置都能被访问到,除非del删掉,否则会一直存活到文件执行完毕
b. 局部作用域的名字:局部有效,只能在局部范围调用,只在函数调用时才有效,调用结束就失效
x=1000 x=1000 def func(): x=2 print(globals()) # 查看全局作用域内的名字:gloabls() print(locals()) # 查看全局作用域内的名字:gloabls(),此处由于在文件级别查看,所以globals() is locals()=True print(globals() is locals()) ''' {'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000000685B70>, '__package__': None, '__doc__': None, '__name__': '__main__', 'func': <function func at 0x0000000000B5E1E0>, '__spec__': None, '__cached__': None, 'x': 1000, '__file__': 'E:/YQLFC/study/day04/day4/名称空间与作用域.py', '__builtins__': <module 'builtins' (built-in)>} {'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000000685B70>, '__package__': None, '__doc__': None, '__name__': '__main__', 'func': <function func at 0x0000000000B5E1E0>, '__spec__': None, '__cached__': None, 'x': 1000, '__file__': 'E:/YQLFC/study/day04/day4/名称空间与作用域.py', '__builtins__': <module 'builtins' (built-in)>} True '''
x=1000 def func(y): x=2 print(locals()) # 查看全局作用域内的名字 print(globals()) # 查看全局作用域内的名字 print(globals() is locals()) func(1) #结果 ''' {'y': 1, 'x': 2} {'__name__': '__main__', '__builtins__': <module 'builtins' (built-in)>, 'func': <function func at 0x0000000000A5E1E0>, '__doc__': None, '__file__': 'E:/YQLFC/study/day04/day4/名称空间与作用域.py', '__cached__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000000009F5B70>, 'x': 1000, '__spec__': None} False '''
5. 闭包函数
闭包函数必须满足以下两个条件:
- 定义在内部函数
- 包含对外部作用域而非全局作用域的引用,该内部函数就称为闭包函数
简单来说: 在函数A的函数体中定义了函数B,且函数B引用了函数A中的变量,并且调用函数A返回函数B的函数对象,那么此时函数B就是闭包函数。
# 定义实例,对象隐藏,全局不可见 # def f1(): # # x内部隐藏,全局不可见 # x = 1 # def f2(): # print(x) # # return f2 # 返回函数f2对象 # # f=f1() # # print(f) # # x因为隐藏,所以全局作用域无效 # x=100000000 # f() ''' 结果: 1 '''
闭包应用: 惰性计算
#闭包应用:惰性计算 from urllib.request import urlopen def index(url): def get(): return urlopen(url).read() # 此处引用了外层作用域url变量 return get # 返回函数对象 oldboy=index('http://crm.oldboyedu.com') # 调用index函数 print(oldboy().decode('utf-8')) # 加()即调用 # 闭包函数相对与普通函数会多出一个__closure__的属性,里面定义了一个元组用于存放所有的cell对象, # 每个cell对象一一保存了这个闭包中所有的外部变量 print(oldboy.__closure__[0].cell_contents) #结果 ''' http://crm.oldboyedu.com ''' x=1 # y=2 def f1(): # x=1 y=2 def f2(): print(x,y) # 此处引用外部变量y return f2 f=f1() print(f.__closure__[0].cell_contents) # 结果 ''' 2 '''
二、函数之装饰器(Decorators)
1. 定义
器即函数
装饰即修饰,意指为其他函数添加新功能
装饰器定义:本质就是函数,功能是为其他函数添加新功能
装饰器本身可以是任何可调用对象,被装饰的对象也可以是任意可调用对象
2. 装饰器需要遵循的原则
1.不修改被装饰函数的源代码(开放封闭原则)
2.为被装饰函数添加新功能后,不修改被修饰函数的调用方式
3. 装饰器的实现
装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数”
直接看示意图,其中 a 为与装饰器 @a 对应的函数, b 为装饰器修饰的函数,装饰器@a的作用是:
简而言之:@a 就是将 b 传递给 a(),并返回新的 b = a(b)
装饰器(decorator)是干嘛的?对于受到封装的原函数来说,装饰器能够在那个函数执行前或者执行后分别运行一些代码,使得可以再装饰器里面访问并修改原函数的参数以及返回值,以实现约束定义、调试程序、注册函数等目标。装饰器一般返回一个包装器(wrapper),而functools.wraps就是装饰包装器的装饰器。
1. 装饰器的基本实现
import time def timmer(func): # 传入一个函数对象,有且只有一个参数 def wrapper(): # 接收多个值 start_time=time.time() # 开始时间 res=func() # 此处func()=index() stop_time=time.time() # 结束时间 print('run time is %s' %(stop_time-start_time)) return wrapper # 返回函数对象 @timmer # 将装饰器的语法糖,在需要装饰的函数正上方,形如@func index=timmer(index) def index(): time.sleep(3) print('welcome to index') index() # 结果 ''' welcome to index run time is 3.0 '''
2. 装饰多个函数且被装饰函数有参数
''' 2、多实例添加,及传参数函数 ''' import time def timmer(func): # 传递参数,保证通用性,应为可变长参数(*args,**kwargs),可接受所有类型的实参 def wrapper(*args,**kwargs): start_time=time.time() # 传递参数,保证通用性,应为可变长参数(*args,**kwargs),可接受所有类型的实参 # 有返回值,存值 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 #再次调用只需要加@timmer @timmer def foo(name): time.sleep(1) print('from foo') # 有返回值,传值打印 res=index() #wrapper() print(res) res1=foo('shuke') #res1=wrapper('egon') print(res1) ''' 运行结果: welcome to index run time is 3.000380039215088 1 from foo run time is 1.0001308917999268 None '''
3. 模拟用户登陆,一次登陆,多次使用
# 定义一个字典用于维护用户登陆状态 login_user={'user':None,'status':False} def auth(func): def wrapper(*args,**kwargs): # 接收多个参数值 if login_user['user'] and login_user['status']: # 字典key对应的value有值,直接调用 res=func(*args,**kwargs) return res else: name=input('>>: ') password=input('>>: ') if name == 'shuke' and password == '123': # 用户登陆成功,修改字典中的用户登陆状态 login_user['user']='shuke' login_user['status']=True print('