一、函数名应用
函数名是什么?函数名是函数的名字,本质:变量,特殊的变量。
函数名(),执行此函数。
python 规范写法
2.一行代码写完,下面一行的的内容要空 2 行,
3.逗号 2 个边的内容要有空格。
如果是不规范的写法,Pycharm 编辑器,会有灰色的波浪线显示。
1.单独打印函数名
def func1():print(666)print(func1)
执行输出:
打印的是一个函数内存地址
2.函数名的赋值
def func2():print(666)f = func2print(f())
执行输出:
666
None
3.函数名可以作为容器类数据的元素
下面的函数都要执行
def f1():print(111)def f2():print(222)def f3():print(333)def f4():print(444)
写 4 个执行代码就可以了
f1()f2()f3()f4()
如果是 100 个呢?
使用 for 循环批量执行函数
def f1():print(111)def f2():print(222)def f3():print(333)def f4():print(444)l1 = []for i in range(1, 5):l1.append('f' + str(i))for i in l1:eval(i)()
执行输出:
111
222
333
444
4.函数名可以作为参数
def f1():print(666)def f2(x):x()f2(f1)
执行输出:
666
分析如下:
def f1():print(666)def f2(x): # x = f1x() # f1()f2(f1) #将 f1 函数作为变量传参给 f2,x()表示执行函数 f1(),输出 666
5.函数名可以作为函数的返回值。
def f11(x):return x #将返回值传给调用者 f11(5),此时 ret = 5ret = f11(5)print(ret)
执行输出:
5
def f1():print(666)def f2(x):return xf2(f1)()
执行输出:
666
代码分析:
def f1():print(666)def f2(x): # x = f1return x #将 f1 返回给函数调用者 f2(f1),此时就相当于 f1 = f2(f1)f2(f1)() #将 f1 函数作为变量传参给 f2,return 之后 f2(f1)等于 f1,f1()就表示执行 f1 函数,输出 666
f1 是一个特殊变量,加()就可以执行了
第一类对象( first-class object)指
1.可在运行期创建
2.可用作函数参数或返回值
3.可存入变量的实体
*不明白?那就记住一句话,就当普通变量用
函数名就是第一类对象
加括号,就可以执行了。
二、闭包
闭包函数:
内部函数包含对外部作用域而非全剧作用域变量的引用,该内部函数称为闭包函数
有如下代码,请补全代码,在函数外部执行 inner()函数
def wrapper():def inner():print(666)
第一种写法
def wrapper():def inner():print(666)inner()wrapper()
执行输出:
666
第二种写法
def wrapper():def inner():print(666)return inner #将 inner 函数返回给调用者 ret,此时 ret = innerret = wrapper()ret() #执行 inner 函数
执行输出:
666
ret = wrapper()ret()
等同于
wrapper()()
return inner 之后,inner 由临时空间,转换为全局空间了。
因为 ret 赋值了
闭包举例
def wrapper():name = '老男孩'def inner():print(name)inner()wrapper()
执行输出:
老男孩
如何判断它是否是一个闭包函数呢? 内层函数名.closure cell 就是=闭包
def wrapper():name = '老男孩'def inner():print(name)inner()print(inner.__closure__)wrapper()
执行输出:
老男孩
(
出现了 cell,就表示它是一个闭包函数
name = '老男还'def wrapper():def inner():print(name)inner()print(inner.__closure__)wrapper()
执行输出:
老男还
None
返回值为 None 表示它不是闭包
因为 name 是一个全局变量
如果函数调用了外层变量而非全局变量,那么它就是闭包
name = '老男还'def wrapper2():name1 = 'alex'def inner():print(name)print(name1)inner()print(inner.__closure__)wrapper2()
执行输出:
老男还
alex
(
只要引用了外层变量至少一次,非全局的,它就是闭包
面试题:
下面的函数,是一个闭包吗?
name = '老男孩'def wraaper2(n):# n = '老男孩' 相当于def inner():print(n)inner()print(inner.__closure__) # Nonewraaper2(name)
它也是一个闭包
虽然 wraaper2 传了一个全局变量,但是在函数 wraaper2 内部,inner 引用了外层变量,相当于在函数 inner 外层定义了 n = ‘老男孩’,所以 inner 是一个闭包函数
闭包的好处
当函数开始执行时,如果遇到了闭包,他有一个机制,他会永远开辟一个内存空间,将必包中的变量等值放入其中,不会随着函数的执行完毕而消失。
举一个例子
from urllib.request import urlopencontent = urlopen('http://www.xiaohua100.cn/index.html').read().decode('utf-8')print(content)
执行输出,是一堆网页源代码。
爬 3 次
from urllib.request import urlopencontent1 = urlopen('http://www.xiaohua100.cn/index.html').read().decode('utf-8')content2 = urlopen('http://www.xiaohua100.cn/index.html').read().decode('utf-8')content3 = urlopen('http://www.xiaohua100.cn/index.html').read().decode('utf-8')
内存开了 3 次,很占用内存
把它封装成闭包
from urllib.request import urlopendef index():url = "http://www.xiaohua100.cn/index.html"def get():return urlopen(url).read()return getxiaohua = index()content = xiaohua()print(content)
这个例子,只有第一遍,是从网站抓取的。
之后的执行,直接从内存中加载,节省内存空间
三、装饰器初识
装饰器本质上就是一个 python 函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。
装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。
现在我有一个需求,我想让你测试这个函数的执行时间,在不改变这个函数代码的情况下:
先写一个雏形
import timedef func1():print('in func1')time.sleep(1) # 模拟程序逻辑start = time.time()func1()print(time.time() - start)
执行输出:
in func1
1.0001070499420166
封装成函数
传一个参数,函数名,就可以查看函数的执行时间了
import timedef func1():print('in func1')time.sleep(1) # 模拟程序逻辑def timmer(f):start_time = time.time()f()end_time = time.time()print('此函数的执行时间为{}'.format(end_time - start_time))timmer(func1)
执行输出:
in func1
此函数的执行时间为 1.0002098083496094
原来 100 个函数,都是调用了 func1(),现在测试函数,需要 timeer(func1)。如果想测试所有调用地方的执行时间,那么需要修改 100 个函数位置,改成 timeer(func1),太麻烦了。
不能更改原来的调用方式,同时需要加一个函数执行时间的功能。怎么办呢?
要无限接近于原来的调用方法,慢慢来
先来讲一个变量赋值的例子
a = 1b = aa = 2 #a 重新赋值了,原来 a 的值不存在了。但是 b 还是等于原来 a 的值,也就是 1print(a,b)
执行输出 2,1

1.更改执行方式
import timedef func1():print('in func1')time.sleep(1) # 模拟程序逻辑def timmer(f):start_time = time.time()f()end_time = time.time()print('此函数的执行时间为{}'.format(end_time - start_time))f1 = func1 #定义 f1 等于 func1 函数func1 = timmer #定义 func1 等于 timmer,等式计算右边的。此时 func1 被覆盖了,和原来的没有任何关系,f1 还是等于原来的 func1func1(f1) #此时相当于执行 timmer(老的 func1)
执行输出:
in func1
此函数的执行时间为 1.0001263618469238
在精简一步
import timedef func1():print('in func1')time.sleep(1) # 模拟程序逻辑def timmer(f): # f = func1def inner():start_time = time.time()f() #执行 func1()end_time = time.time()print('此函数的执行时间为{}'.format(end_time - start_time))return inner #将 inner 函数返回给函数调用者 timmer(func1)a = timmer(func1) #等式计算右边的,将 func1 函数传给 timmer 函数a() #此时相当于执行了 inner 函数
执行输出:
in func1
此函数的执行时间为 1.000577449798584
最终版本
import timedef timmer(f): # 2 接收参数.f = func1def inner():start_time = time.time() #5.进入 inner 函数f() #6.执行 f(),也就是原来的 func1 函数。虽然 func1 被覆盖了,但是之前的值还存在。请参考上面 a,b 赋值的例子end_time = time.time() #10 获取当前时间print('此函数的执行时间为{}'.format(end_time - start_time)) #11.输出差值return inner #3.将 inner 函数返回给函数调用者 timmer(func1),此时程序结束,继续执行 func1()def func1(): #7.进入函数print('in func1') # 8.打印输出time.sleep(1) # 9.等待 1 秒func1 = timmer(func1) #1.等式计算右边的,将 func1 函数传给 timmer 函数,此时 func1 被覆盖了,原来 func1 的不存在了。func1() #4.这里的 func1 是全新的 func1,就是上面的赋值,此时相当于执行 inner 函数
代码从上至下执行,先加载函数变量 timmer 和 func1。在执行上文说的第 1 步以及后面的,请看数字。
执行输出:
in func1
此函数的执行时间为 1.0009608268737793
但是加这一步,还是不太方便。那么 python 给这种情况,加了一个语法糖@
语法糖指那些没有给计算机语言添加新功能,而只是对人类来说更”甜蜜”的语法
import timedef timmer(f): # f = func1def inner():start_time = time.time()f() #执行 func1()end_time = time.time()print('此函数的执行时间为{}'.format(end_time - start_time))return inner #将 inner 函数返回给函数调用者 timmer(func1)#语法糖@@timmer #相当于 func1 = timmer(func1) #这一步就已经覆盖了def func1():print('in func1')time.sleep(1) # 模拟程序逻辑func1() #相当于执行了 inner()函数
想测试谁,前面加@装饰器函数,即可。
装饰器利用 return 制造了一个假象,func1()执行,其实是执行了 inner()
func1()已经把原来的 func1()给覆盖了
这个装饰器,还不够完整,函数不能传参数
先来举个例子
def func2(a1,b1): #4.执行函数,接收位置参数 a1=1,b1=2print(a1,b1) #5. 输出 1,2def func3(a,b): #2. 接收参数,a=1,b=2func2(a,b) #3. 执行函数 func2(1,2)func3(1,2) #1.传位置参数 1,2
执行输出:
1 2
func3 执行一遍后,将 a,b 的值,传给 a1,b1 了
上面装饰器的例子,func1,要传 2 个参数 a,b
import timedef timmer(f):def inner(a,b):start_time = time.time()f(a,b)end_time = time.time()print('此函数的执行时间为{}'.format(end_time - start_time))return inner@timmerdef func1(a,b):print('in func1 {}{}'.format(a,b))time.sleep(1) # 模拟程序逻辑func1(1,2)
执行输出:
in func1 12
此函数的执行时间为 1.0006024837493896
如果有多个参数呢?改成动态参数
import timedef timmer(f):def inner(*args,**kwargs):start_time = time.time()f(*args,**kwargs)end_time = time.time()print('此函数的执行时间为{}'.format(end_time - start_time))return inner@timmerdef func1(*args,**kwargs):print('in func1 {}{}'.format(args,kwargs))time.sleep(1) # 模拟程序逻辑func1(1,2,a='3',b=4)
执行输出:
in func1 (1, 2){‘b’: 4, ‘a’: ‘3’}
此函数的执行时间为 1.000101089477539
面试题,手写一个装饰器
写装饰器,约定俗成,函数名为 wrapper
第一步
def wrapper(func):def inner():func()return inner
第二步
def wrapper(func):def inner(*args,**kwargs):func(*args,**kwargs)return inner
第三步
def wrapper(func):def inner(*args,**kwargs):'''被装饰函数之前'''ret = func(*args,**kwargs)'''被装饰函数之后'''return retreturn inner
完整的
def wrapper(func):def inner(*args,**kwargs):'''被装饰函数之前'''ret = func(*args,**kwargs)'''被装饰函数之后'''return retreturn inner@wrapperdef func(*args,**kwargs):print(args,kwargs)return 666print(func())
作业:
使用函数完成用户登录和注册功能
先让用户选择,是登陆还是注册
选择序号完毕之后,运行相应的程序,
验证成功之后,可以让其继续选择,登陆还是注册,还可以选择退出。
1.先准备函数的雏形
def registered_username(username):'''#判断注册用户名是否存在:param username: 用户名:return: True 存在 False 不存在'''def write_file(username,password):'''#写入用户列表文件:param username: 用户名:param password: 密码:return: True 写入成功 False 写入失败'''def username_password(username,password):'''#判断用户名和密码是否匹配:param username: 用户名:param password: 密码:return: True 匹配成功 False 匹配失败'''def register(*args,**kwargs):'''注册逻辑:return:'''passdef login(*args,**kwargs):'''登录逻辑:return:'''passdef user_menu(*args,**kwargs):'''# 用户菜单'''pass
2.先写菜单
def user_menu(*args,**kwargs):'''# 用户菜单'''# 判断文件是否存在,不存在创建文件file_exists()# 循环while True:# 打印菜单menu = ['注册', '登录', '退出']print('bbs 系统'.center(25, '#'))for i in range(len(menu)):print('{}\t{}'.format(i + 1, menu[i]))print(''.center(27, '#'))number = input('请选择序号: ').strip()if number == '1':# 执行注册程序register()elif number == '2':# 执行登录程序login()elif number == '3':exit()else:print('输入错误,请重新输入!')
3.写判断用户列表文件是否存在
def file_exists(*args,**kwargs):'''# 判断用户列表文件是否存在:return: True 存在 False 不存在'''# 判断文件是否存在if os.path.exists(file_name):return Trueelse:with open(file_name, encoding='utf-8', mode='w') as mk:mk.write('张三 123')return False
4.再写注册逻辑
def register(*args,**kwargs):'''注册逻辑:return:'''while True:username = input('请输入注册的用户名,或输入 q 返回菜单:').strip()if username == '':print('用户名为空,请重新输入!')elif username.upper() == 'Q':breakelse:# 执行判断用户名函数result = registered_username(username)if result:password = input('请输入您的注册的密码:').strip()# 判断密码if password == '':print('密码为空,请重新输入!')else:# 执行写入用户列表文件函数result = write_file(username, password)if result:print('注册成功!,您的用户名为: {}\n 倒计时 2 秒返回菜单!'.format(username))time.sleep(2)user_menu()else:print('注册失败!请重试')else:print('用户名已经存在,请重新输入!')
5.判断注册用户名是否可用
def registered_username(username):'''#判断注册用户名是否可用:param username: 用户名:return: True 可用(用户不存在) False 不可用(用户已存在)'''# 纯用户名列表,不包含密码user_list = []with open(file_name, encoding='utf-8') as f1:for i in f1:# 去空格,以空格切割,转换为列表li = i.strip().split() # [张三,123]# 将用户名追加到列表中user_list.append(li[0])# 判断用户名是否存在列表中if username in user_list:# 返回 Falsereturn Falseelse:return True
6.写入用户列表文件
def write_file(username,password):'''#写入用户列表文件:param username: 用户名:param password: 密码:return: True 写入成功 False 写入失败'''with open(file_name, encoding='utf-8', mode='a') as f2:f2.write('\n{} {}'.format(username, password))return True
7.登录逻辑
def login(count=0,max=3):'''登录逻辑:param count: 初始失败次数:param max: 最大失败次数:return:'''while count < max:count += 1username = input('请输入用户名:').strip()password = input('请输入密码:').strip()# 执行验证用户名和密码函数result = username_password(username,password)if result:print('登陆成功\n 倒计时 1 秒返回菜单!')time.sleep(1)user_menu()breakelse:print('用户名或密码错误,还剩余{}次机会!'.format(max - count))# 返回主菜单user_menu()
8.判断用户名和密码是否匹配
def username_password(username,password):'''#判断用户名和密码是否匹配:param username: 用户名:param password: 密码:return: True 匹配成功 False 匹配失败'''# print(username,password)with open(file_name, encoding='utf-8', mode='r') as f3:for i in f3:# print(i)# 去空格,以空格切割,转换为列表li = i.strip().split() # [张三,123]# 判断用户名和密码是否匹配if username == li[0] and password == li[1]:result = True# 当找到匹配时,跳出循环breakelse:result = False# 当整个用户列表遍历完成之后,再 returnreturn result
最终完整代码如下:
# -*- coding: utf-8 -*-import time,os#文件名file_name = 'user_list.txt'def file_exists(*args,**kwargs):'''# 判断用户列表文件是否存在:return: True 存在 False 不存在'''# 判断文件是否存在if os.path.exists(file_name):return Trueelse:with open(file_name, encoding='utf-8', mode='w') as mk:mk.write('张三 123')return Falsedef registered_username(username):'''#判断注册用户名是否可用:param username: 用户名:return: True 可用(用户不存在) False 不可用(用户已存在)'''# 纯用户名列表,不包含密码user_list = []with open(file_name, encoding='utf-8') as f1:for i in f1:# 去空格,以空格切割,转换为列表li = i.strip().split() # [张三,123]# 将用户名追加到列表中user_list.append(li[0])# 判断用户名是否存在列表中if username in user_list:# 返回 Falsereturn Falseelse:return Truedef write_file(username,password):'''#写入用户列表文件:param username: 用户名:param password: 密码:return: True 写入成功 False 写入失败'''with open(file_name, encoding='utf-8', mode='a') as f2:f2.write('\n{} {}'.format(username, password))return Truedef username_password(username,password):'''#判断用户名和密码是否匹配:param username: 用户名:param password: 密码:return: True 匹配成功 False 匹配失败'''# print(username,password)with open(file_name, encoding='utf-8', mode='r') as f3:for i in f3:# print(i)# 去空格,以空格切割,转换为列表li = i.strip().split() # [张三,123]# 判断用户名和密码是否匹配if username == li[0] and password == li[1]:result = True# 当找到匹配时,跳出循环breakelse:result = False# 当整个用户列表遍历完成之后,再 returnreturn resultdef register(*args,**kwargs):'''注册逻辑:return:'''while True:username = input('请输入注册的用户名,或输入 q 返回菜单:').strip()if username == '':print('用户名为空,请重新输入!')elif username.upper() == 'Q':breakelse:# 执行判断用户名函数result = registered_username(username)if result:password = input('请输入您的注册的密码:').strip()# 判断密码if password == '':print('密码为空,请重新输入!')else:# 执行写入用户列表文件函数result = write_file(username, password)if result:print('注册成功!,您的用户名为: {}\n 倒计时 2 秒返回菜单!'.format(username))time.sleep(2)user_menu()else:print('注册失败!请重试')else:print('用户名已经存在,请重新输入!')def login(count=0,max=3):'''登录逻辑:param count: 初始失败次数:param max: 最大失败次数:return:'''while count < max:count += 1username = input('请输入用户名:').strip()password = input('请输入密码:').strip()# 执行验证用户名和密码函数result = username_password(username,password)if result:print('登陆成功\n 倒计时 1 秒返回菜单!')time.sleep(1)user_menu()breakelse:print('用户名或密码错误,还剩余{}次机会!'.format(max - count))# 返回主菜单user_menu()def user_menu(*args,**kwargs):'''# 用户菜单'''# 判断文件是否存在,不存在创建文件file_exists()# 循环while True:# 打印菜单menu = ['注册', '登录', '退出']print('bbs 系统'.center(25, '#'))for i in range(len(menu)):print('{}\t{}'.format(i + 1, menu[i]))print(''.center(27, '#'))number = input('请选择序号: ').strip()if number == '1':# 执行注册程序register()elif number == '2':# 执行登录程序login()elif number == '3':exit()else:print('输入错误,请重新输入!')# 执行菜单user_menu()
执行输出:

