2 函数进阶
函数的嵌套调用
函数的嵌套调用: 在调用一个函数的过程,又调用了其他函数。
def bar():
print('from bar')
def foo():
print('from foo') # foo 内部又调用了另外一个函数bar
foo()
# 函数的嵌套调用: 在调用一个函数的过程,又调用了其他函数。 # 定义第一个函数 def my_max(x,y): if x > y: return x else: return y # 定义第二个函数 def my_max4(a,b,c,d): ret = my_max(a,b) # 调用第一个函数 ret2 = my_max(ret,c) # 调用第一个函数 ret3 = my_max(ret2,d) # 调用第一个函数 return ret3 ret = my_max4(4,5,6,9) print(ret) # 可以把一个复杂的问题分解成一个小的问题。
函数的嵌套定义
在一个函数内部,又定义了另外一个函数
def f1(): x = 1 # 函数内部定义的变量,在函数执行的生效,函数结束,绑定关系失效 def f2(): # 内部定义的函数,只在函数内部生效,外部无法调用。 print('from f2') f1()
名称空间与作用域
一 什么是名称空间?
#名称空间:存放名字的地方,三种名称空间,(之前遗留的问题x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方)
二 名称空间的加载顺序
python test.py #1、python解释器先启动,因而首先加载的是:内置名称空间 #2、执行test.py文件,然后以文件为基础,加载全局名称空间 #3、在执行文件的过程中如果调用函数,则临时产生局部名称空间
三 名字的查找顺序
局部名称空间--->全局名称空间--->内置名称空间 #需要注意的是:在全局无法查看局部的,在局部可以查看全局的,如下示例 # max=1 def f1(): # max=2 def f2(): # max=3 print(max) f2() f1() print(max)
名称 空间 :存放名字的地方,准确的说名称空间是存放名字与变量值绑定关系的地方
内置名称空间:在python解释器启动时产生,存放一些python内置的名字
全局名称空间:在执行文件时产生,存放文件级别定义的名字
x=1 # 全局命名空间 def func(): y=2 # 局部名称空间 def f1():pass print import os # 内置 class Foo: # 局部 pass if x==1:z=3
局部名称空间:在执行文件的过程中,如果调用了函数,则会产生该函数的局部名称空间
用来存放该函数内定义的名字,该名字在函数调用时生效,在函数调用结束后失效
加载顺序:内置---》全局---》局部
作用域
#1、作用域即范围 - 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效 - 局部范围(局部名称空间属于该范围):临时存活,局部有效 #2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下 x=1 def f1(): def f2(): print(x) return f2 x=100 def f3(func): x=2 func() x=10000 f3(f1()) #3、查看作用域:globals(),locals() LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__ locals 是函数内的名字空间,包括局部变量和形参 enclosing 外部嵌套函数的名字空间(闭包中常见) globals 全局变量,函数定义所在模块的名字空间 builtins 内置模块的名字空间
闭包函数
大前提:作用域关系,在函数定义时就已经固定
,于调用位置无关,在调用函数时,必须必须必须
回到函数原来定义的位置去找作用域关系
闭包函数:
1. 定义在函数内部的函数
2. 包含对外部作用域名字的引用,而不是对全局作用域名字的引用
那么该内部函数就称为闭包函数
def f1(): x = 100 def f2(): print(x) return f2 # 必包函数 ,f2 不是单独存在的,还带着一层作用域。 func=f1() # 接收里面的返回值函数,不管在哪里执行都会以自己带的那层包裹为准。 x=11111111111 # 此时在调用函数前在定义x 的值 ,也不会改变X 原来的值,与调用方式无关。作用域还是准寻原来的函数 func() # X=100 # 函数当作返回值,重新赋值调用。大破层级限制。 func 得到的不是单独的f2 还有一层包裹
闭包函数的应用:
import requests def index(url): # url='https://www.python.org' def get(): requests.get(url).text return get # 返回闭包函数 # 闭包函数 python_web=('https://www.python.org') # 传一次永久使用 baidu_web=('https://www.baidu.com') # 传一次永久使用
闭包函数解析
# 内部函数的局部作用域中可以访问外部函数局部作用域中变量的行为,成为闭包。 name='tony 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__) # 闭合函数包裹了那些 print(python_web.__closure__[0].cell_contents) # 返回一个tuple ,索引取值 print(python_web.__closure__[0].cell_contents)# 返回一个tuple ,索引取值 print(python_web.__closure__[1].cell_contents)# 返回一个tuple ,索引取值 print(python_web.__closure__[2].cell_contents)# 返回一个tuple ,索引取值
装饰器
开放封闭原则
对扩展开放的,对修改是封闭。
装饰器:
1 开放封闭原则:对扩展是开放的,对修改是封闭
2 装饰器:装饰它人的工具,
装饰器本身可以是任意可调用对象,被装饰的对象本身也可以是任意可调用对象
2.1 装饰器的遵循的原则:1 不修改被装饰对象的源代码 2 不修改被调用对象的调用方式
2.2 装饰器的目的是:在遵循1和2原则的前提,为其他新功能函数添加
@装饰器名,必须写在被装饰对象的正上方,并且是单独一行
import time def timmer(func): # func = 被装饰的函数 def wrapper(): # 闭包函数 strat_time=time.time() # 调用前扩展功能 func() # 被装饰的函数 end_time = start_time-time.time() # 调用后扩展功能 return wrapper # return 闭包函数 带有局部作用域。 @timmer # index=timmer(index) # 装饰器名,必须写在被装饰的对象的正上方,并且是单独一行。 def index(): time.sleep(3) print('welcome to index') @timmer # home=timmer(home) # 装饰器名,必须写在被装饰对象的正上方,并且单独一行。 def home(): time.sleep(5) print('welcome to home') index() # 不修改源代码,不改变调用方式,还实现了扩展 home() # 不修改源代码,不改变调用方式,还实现了扩展
装饰器结构
装饰器的使用
import time # 有参数的函数被套上装饰器 def timmer(func): # func = 被装饰的函数 def wrapper(*args,**kwargs): # 被装饰的函数的参数接过来 start_time = time.time() # 装饰前的扩展功能 func(*args,**kwargs) # 被装饰的函数有无参数都行,有参数就接过来 end_time = start_time-time.time() # 装饰后扩展功能 return wrapper # 闭包函数 @timmer def index(x,y): # 有参数函数套上了装饰器 time.sleep(5) print('welcome to index %s,%s'%(x,y)) @timmer def home(*arg,**kwargs): # 有无参数或者多个参数的函数套装饰器 start_time = time.time() print('welcome to home {0},{1}'.format(arg,kwargs)) index(5,6) home(1234324,x=2,y=4234) # 有返回值的函数被装饰时 def timmer(func): def wrapper(*args,**kwargs): start_time = time.time() # ret = func(*args,**kwargs) # 被装饰的函数有返回值时,有无参数都行 end_tiem = start_time-time.time() return ret # 被装饰函数的返回值 return wrapper @timmer def index(x,y): # 有参数且有返回值套上装饰器 time.sleep(5) print('welcome to index') return x,y @timmer def home(*args,**kwargs): # 有无参数都行,且有返回值套上装饰器 start_time = time.time() print('welcome to home {0},{1}'.format(args,kwargs)) return args,kwargs # 被装饰函数的返回值 ret = index('tony',29) print(ret) res = home('tony',29,tel='185....') print(res)
装饰器认证实例(装饰器无参数)
#!/usr/bin/env python3 # author:'tony' # file_name:'auth' from functools import wraps # 此模块将原函数的help message import 装饰器 current_user={'user':None} # 记录用户登陆状态 # 装饰器函数: def auth(func): # 外层函数 @wraps(func) # 给闭包函数加装饰器 def wrapper(*args,**kwargs): # 内层函数 if current_user['user']: # 判断用户登陆状态,有登陆用户时 return func(*args,**kwargs) # 直接调用原函数 name = input('input your name >>>').strip() pwd = input('input your password >>>').strip() with open('db.txt',encoding='utf-8') as f: user_dict = eval(f.read()) if name in user_dict and pwd == user_dict[name]: ret = func(*args,**kwargs) # 被装饰的函数 current_user['user'] = name return ret else: print('name or password error ...') return wrapper # 返回内层函数实现闭包 @auth # index=auth(index) 把正下方的函数名当作参数传给装饰器函数,在赋值给原函数。此时原函数其实是装饰器函数的返回值。 def index(x,y): '''访问此函数时需要登陆,如果有登陆可以直接访问; 那么这个注释怎么才能在装饰器里也可以查看呢,需要一个模块''' print('welcome to index ... ') return '{0} and {1}is index items'.format(x,y) @auth # home=auth(home) 把正下方的函数名当作参数传给装饰器函数,在赋值给原函数。此时原函数其实是装饰器函数的返回值。 def home(*args,**kwargs): '''访问此函数时需要登陆,如果有登陆可以直接访问; 那么这个注释怎么才能在装饰器里也可以查看呢,需要一个模块''' print('welcome to home ....') return '%s and %s is index items'%(args,kwargs) ret = index(123,456) print(ret) ret = home(789,name='tony',pwd=123) print(ret)
装饰器认证实例(装饰器有参数)
#有参装饰器版本 current_user={'user':None} def auth(auth_type='file'): # 装饰器函数外再套一层为了接受装饰器的参数,作用域的原因,局部都可处理此参数 '''装饰器有参数,根据装饰器的参数判断进行那一步的逻辑''' def deco(func): # 装饰器外层函数,也就是闭包函数在闭包函数一下,就是包两层 def ... return 呗 def wrapper(*args, **kwargs): # 装饰器第二层,就两句 def .. return 在复杂的逻辑都无关。 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()) # eval :执行字符串中的命令,有些命令用字符串抱起来,eval可以直接处理 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 # 又一层闭包函数 @auth(auth_type='mysql') #@deco #index=deco(index) def index(): print('from index') @auth(auth_type='file') def home(name): print('welcome %s' %name) index() #wrapper() home('tony')
装饰器执行过程
装饰器其他功能
函数被多套多个装饰器