发布时间:2018-04-01 11:31:21编辑:Run阅读(3901)
python函数的嵌套和作用域链
函数的嵌套调用
def max2(x,y): m = x if x > y else y # 三元运算 return m def max4(a,b,c,d): res1 = max2(a,b) res2 = max2(res1,c) res3 = max2(res2,d) print(res1) # 23 print(res2) # 31 print(res3) # 31 max4(23,-7,31,11)
执行结果
23
31
31
例子2
def f1(): print("in f1") def f2(): print("in f2") f2() f1()
执行结果
in f1
in f2
例子3
def f1(): def f2(): def f3(): print("in f3") print("in f2") f3() print("in f1") f2() f1()
执行结果
in f1
in f2
in f3
函数的嵌套无非就是搞清楚函数的执行顺序(不清楚可以看前面的python全栈开发10)
函数的作用域链:小范围作用域可以使用大范围的变量,但是反之不行,它是单向的
def f1(): a = 1 def f2(): def f3(): print(a) f3() f2() f1()
执行结果,小范围可以使大范围改变
1
例子2
def f1(): a = 1 def f2(): a = 2 # 单向的,小范围可以改变大范围,反之不行 f2() print('a in f1 :', a) f1()
执行结果
a in f1 : 1
函数名的本质
函数名本质上就是函数的内存地址
1,可以被引用
def func(): print('in func') f = func print(f)
执行结果,这里f等于func,而func又是一个函数名,但是后面没有加(),所以上面的func没有被执行,最后打印f,实际上就是打印出func函数名的内存地址
2,可以被当做容器类型的元素
def f1(): print('f1') def f2(): print('f2') def f3(): print('f3') l = [f1,f2,f3] d = {'f1':f1,'f2':f2,'f3':f3} # 调用 l[0]() d['f2']()
执行结果,后面的l[0](),实际上就是先取列表l里面的f1,然后就变成了f1(),其实就是执行了f1函数,而d['f2'](),同理,先取出字典'f2'对应的值f2,变成了f2(),函数名加上(),就是执行函数,所以最后的结果就是f1,f2
f1
f2
例2,用for循环批量执行函数
def f1(): print(111) def f2(): print(222) def f3(): print(333) def f4(): print(444) # 如果有100个这样的函数,写100个?使用for循环批量执行函数 l1 = [] for i in range(1, 5): l1.append('f' + str(i)) for i in l1: eval(i)() # eval会去掉引号,后面加个(),执行函数
执行结果
111
222
333
444
3,可以当作函数的参数和返回值
第一类对象(first-class object)指
1,可在运行期创建
2,可用作函数参数或返回值
3,可存入变量的实体
不明白?就当普通变量用,如果后面加(),就执行该函数
def fl(): print('f1') # 4 执行f1函数,打印一次f1 def funcl(argv): # 2 接收f1参数,所以argv = fl argv() # 3 所以变成f1(), 执行f1()函数 return argv # 5 返回argv,即调用者,也就是funcl(fl)这个整体,等于f1 f = funcl(fl) # 1 先算等号右边funcl(f1),执行funcl函数,把f1参数,传给argv # 6 在算等号左边的,也就是 f = f1 f() # 7 因为f=f1,所以就变成f1(),也就是执行f1函数, # 所以在打印一次f1,最终结果打印2次f1
执行结果
f1
f1
例2
def f1(): # 4 打印 666,最终结果就是666 print(666) def f2(x): # 2 接收传过来的实参f1,那么x = f1 x() # 3 就变成 f1(),执行f1()函数 f2(f1) # 1执行f2(),把实参f1,传给f2里面的x
执行结果
666
例3
def f1(): # 5 打印666,最终结果666 print(666) def f2(x): # 2 接收实参f1,所以x = f1 return x # 3 返回x也就是f1给函数调用者,也就是f2(f1) f2(f1)() # 1 执行f2()函数,把实参f1传给f2里面的x # 4 所以就变成f1(),也就是执行了f1函数
执行结果
666
闭包
例
def func(): name = 'hello world' def inner(): print(name)
闭包函数
内部函数包含对外部作用域而非全局作用域变量的引用,该内部函数称为闭包函数
函数内部定义的函数称为内部函数
由于有了作用域的关系,我们就不能拿到函数内部的变量和函数了,如果我们就是想拿怎么办呢?返回呀
我们都知道函数内的变量我们想在函数外部用,可以直接返回这个变量,那么如果我们想在函数外部调用函数内部的函数呢?
是不是直接就把这个函数的名字返回就好了?
这才是闭包函数最常用的用法
def func(): name = 'eva' # 2 定义一个name = 'eva' def inner(): # 6 执行函数,打印name的值,之前定义了name= 'eva',所以打印eva print(name) return inner # 3 返回inner值给调用者,也就是func()这个整体 f = func() # 1 先算等号右边,执行func()函数 # 4 在算等号左边,也就是f = inner f() # 5 因为f=inner,也就是执行了inner()这个函数
执行结果
eva
判断闭包函数的方法__closure__
# 输出的__closure__有cell元素:是闭包函数 def func(): name = 'eva' def inner(): print(name) print(inner.__closure__) return inner f = func() f()
执行结果
输出的__closure__有cell 则是闭包函数
例子2
name = 'eqon' def func(): def inner(): print(name) print(inner.__closure__) return inner f2 = func() f2()
执行结果
输出的__closure__为None,则不是闭包函数
例3
def wrapper(): money = 1000 def func(): name = 'eva' def inner(): print(name, money) print(inner.__closure__) return inner return func f = wrapper() # f = func i = f() # func() i = inner i() # inner()
执行结果,是闭包函数
例4
from urllib.request import urlopen def index(): url = 'http://www.xiaohua100.cn/index.html' def get(): return urlopen(url).read() # decode('utf-8')解码成utf-8 print(get.__closure__) return get xiaohua = index() # xiaohua = get content = xiaohua() # get() content = read.url的内容 print(content) # 打印网站内容
执行结果,有cell则属于闭包函数
闭包函数的好处
闭包:当函数开始执行时,如果遇到了闭包,它有一个机制,它会永远开辟一个内存空间,将闭包中的变量等值放入其中,不会随着函数的执行完毕而消失,上面的例子就是典型。
python装饰器
什么是装饰器?
装饰器本质上就是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。
装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景
装饰器的形成过程
现在有一个需求,我想让你测试这个函数的执行时间,在不改变这个函数代码的情况下:
# 装饰器 import time def funcl(): # 8 执行此函数 print("hello world") # 9 打印hello world def timer(f): # 2 接收传过来的参数funcl,所以f = funcl def inner(): start_time = time.time() # 6 计算开始时间 f() # 7 f = funcl ,看第二部的值,f加()就是执行了funcl()这个函数 time.sleep(0.3) # 10 模拟延迟 end_time = time.time() # 11 计算结束后的时间 print('此函数的执行效率{}'.format(end_time - start_time)) # 12 打印出函数执行的效率(end-start) return inner # 3 把inner值返回给调用者,调用者是timer(funcl)这个整体 funcl = timer(funcl) # 1 执行等号右边的timer()函数,把funcl参数传给timer里面的变量f # 4 在算等号左边的,所以 funcl = inner funcl() # 5 即inner加(),就是执行inner()这个函数
执行结果
hello world
此函数的执行效率0.3008289337158203
但是如果有多个函数,我都想让你测试它们的执行时间,你每次是不是都得func1=timer(funcl)?这样还是有点麻烦,因为这些函数的函数名可能是不相同,有funcl,func2,graph等,所以更简单的方法,python给你提供了,那就是语法糖。
import time def timer(f): def inner(): start_time = time.time() f() time.sleep(0.3) end_time = time.time() print("此函数的执行效率{}".format(end_time-start_time)) return inner @timer # 即 func = timer(func) def func(): print("大家好,我是渣渣辉!") func()
执行结果
大家好,我是渣渣辉!
此函数的执行效率0.3001282215118408
想测试谁,前面加@装饰器函数,即可
这就是最简单版本的装饰器
利用return制造了一个假象,func()执行,其实是执行了inner(),而func变量名传给了timer里的f,func()已经把原来的func()覆盖了
刚才讨论的装饰器都是不带参数的函数,现在要装饰一个带参数的函数怎么办呢?
例1,装饰一个带参数的函数
import time def timer(f): # f = func def inner(a): # a = 1 start_time = time.time() f(a) time.sleep(0.5) end_time = time.time() print(end_time - start_time) return inner @timer # func = timer(func) # inner def func(a): print(a) #打印1 func(1)
执行结果
1
0.5007100105285645
例2,装饰一个带两个参数的函数
import time def timer(f): def inner(a,b): start_time = time.time() f(a,b) time.sleep(0.5) end_time = time.time() print("此函数的执行效率{}".format(end_time - start_time)) return inner @timer # func = timer(func) 实际上就是 func = inner def func(a, b): print(a, b) func('大家好','渣渣辉')
执行结果
大家好 渣渣辉
此函数的执行效率0.500657320022583
可以接收任意参数,动态传参
例子
import time def timer(f): def inner(*args, **kwargs): start_time = time.time() f(*args, **kwargs) time.sleep(0.4) end_time = time.time() print("此函数的执行效率为{}".format(end_time - start_time)) return inner @timer def func1(a, b): print("I am func1") @timer def func2(a, b, c): print("in func2 and get a={},b={},c={}".format(a,b,c)) func1(10, 20) func2("大家好", "我是", "渣渣辉")
执行结果
I am func1
此函数的执行效率为0.400327205657959
in func2 and get a=大家好,b=我是,c=渣渣辉
此函数的执行效率为0.40012335777282715
面试题,手写装饰器
def wrapper(func): def inner(*args, **kwargs): '''被装饰函数之前''' ret = func(*args, **kwargs) '''被装饰函数之后''' return ret return inner() @wrapper def func(a, b): pass return 123 #写成这样基本就差不多了
练习题
先让用户选择,是登陆还是注册
选择序号完毕之后,运行相应的程序,
验证成功之后,可以让其继续选择,登陆还是注册,还可以选择退出(自己增加一个可修改密码功能)
先在同目录下创建一个user_pwd.txt账号密码文件,代码如下
#!/usr/bin/env python # coding: utf-8 import time import os import platform __author__ = 'www.py3study.com' '''此函数为装饰器''' def timer(func): def inner(*args, **kwargs): start_time = time.time() func(*args, **kwargs) end_time = time.time() print('|| 此程序执行总时间:{}秒'.format(end_time - start_time)) return inner '''语法糖,计算程序运行总时长,退出程序时执行''' @timer class LandingCertification(object): def __init__(self, *args, **kwargs): self.timeout = 3 self.flag = True self.result = False self.run = { '0': self.drop_out, '1': self.register, '2': self.landing, '3': self.account_password, '4': self.transfer_parameters, } self.main() '''程序入口''' def main(self, *args, **kwargs): while True: self.mainMenu() op = input(u'|| 输入选项:').strip() '''map判断输入是否符合条件''' if op == '0': return True elif op in map(str, range(len(self.run))): self.run.get(op)() else: self.Error() continue '''此函数用于打印菜单''' def mainMenu(self, *args, **kwargs): self.clear() print(u'='*40) print(u'|| 0:退出程序') print(u'|| 1:注册') print(u'|| 2:登陆') print(u'|| 3:查看已存在账号') print(u'|| 4:修改密码(流程:账号名-->老密码-->新密码-->修改成功)') print(u'='*42) '''此函数用于清屏''' def clear(self, *args, **kwargs): OS = platform.system() if (OS == u'Windows'): os.system('cls') else: os.system('clear') '''此函数用于设置延迟''' def Error(self, *args, **kwargs): print(u'|| 只能输入0-3的整数,等待{}秒后重新输入'.format(self.timeout)) time.sleep(self.timeout) '''此函数用于文件操作,查询''' def account_password(self, *args, **kwargs): with open('user_pwd.txt', encoding='utf-8', mode='r') as f1: for i in f1: print('|| {}'.format(i.strip())) '''此函数用于账号注册''' def register(self, *args, **kwargs): while self.flag: username = input("|| 请输入用户名:").strip() with open('user_pwd.txt', encoding='utf-8', mode='r') as f2: for i in f2: li = i.strip().split() if username == li[0] or not username.strip(): print('|| (账号名已存在)or(账号名不能为空)') break else: password = input('|| 请输入密码:').strip() again_password = input('|| 再次输入密码,确认:').strip() if password == again_password: with open('user_pwd.txt', encoding='utf-8', mode='a') as f3: f3.write('\n{} {}'.format(username, password)) print('|| 注册成功') return self.flag else: print("|| 两次密码不一样") '''此函数用于账号登陆''' def landing(self, *args, **kwargs): max = 3 count = 0 while count < max: count += 1 landing_username = input("|| 输入账号名:").strip() if not landing_username.strip(): print('|| 账号名不能为空') else: landing_password = input("|| 输入密码:").strip() with open('user_pwd.txt', encoding='utf-8', mode='r') as f4: for i in f4: s3 = i.strip().split() if landing_username == s3[0] and landing_password == s3[1]: self.result = True f4.close() break else: self.result = False if self.result: print('|| 登陆成功!') return True else: print('|| 账号或者密码错误,还有{}次机会'.format(max - count)) if max - count == 0: return True '''此函数用于传参给modify进行密码的修改''' def transfer_parameters(self, *args, **kwargs): while True: transfer_username = input("|| 输入需要更改的账号名:").strip() if not transfer_username.strip(): print("|| 账号不能为空") else: old_password = input("|| 输入老的密码:").strip() new_password = input('|| 输入新的密码:').strip() with open("user_pwd.txt", encoding='utf-8', mode='r') as f9: for i in f9: s4 = i.strip().split() if transfer_username == s4[0] and old_password == s4[1]: self.result = True f9.close() self.modify(old_password, new_password) break else: self.result = False if self.result: print("|| 密码修改成功!") return True else: print("|| 此账号不存在或密码错误") '''此函数用于修改密码''' def modify(self, *args, **kwargs): with open('user_pwd.txt', encoding='utf-8', mode='r') as f6, \ open('user_pwd.bak', encoding='utf-8',mode='w') as f7: for i in f6: new_i = i.replace(*args, **kwargs) f7.write(new_i) f6.close() f7.close() os.remove('user_pwd.txt') os.rename('user_pwd.bak', 'user_pwd.txt') '''此函数用于退出程序''' def drop_out(self, *args, **kwargs): print(u"|| Good bye!") self.main() return True if __name__ == '__main__': LandingCertification()
运行效果
47605
45985
36909
34469
29080
25713
24566
19714
19245
17756
5565°
6155°
5691°
5737°
6705°
5483°
5484°
5988°
5965°
7295°