函数(二)
闭包
闭包现象
正常来说,函数调用结束, 函数体里的变量都会被销毁, 而有一种现象是当函数执行完毕, 函数体里的变量不会被销毁, 因为这个变量被内嵌函数的一段代码调用着,无法销毁, 这种现象叫做闭包
def outer():
name='周泽SB'
def inner():
print(name)
return inner
func_inner=outer() # 返回的是inner这个内层函数对象加上调用的变量name的作用域
func_inner()
函数进阶-装饰器
闭包函数的一种, 主要场景是一段程序执行前需要判断是否是否登录
实现这个功能还要符合开放封闭原则 ( 不改变程序的源代码以及调用方式进行扩展)
源程序
def home():
print('主页')
def america():
print('----欧美专区----')
def japan():
print('----日韩专区----')
home()
america()
japan()
初级版
需要对america, japan加入登录验证
account = {
'is_authenticated': False,
'username': 'zzsb',
'password': '123',
}
def login(func):
if not account['is_authenticated']:
usr, pwd = input('请输入用户名 ').strip(), input('请输入密码 ').strip()
username, password = account.get('username'), account.get('password')
if usr == username and pwd == password:
print('登入成功')
account['is_authenticated'] = True
return func
else:
print('用户名或密码错误')
else:
print('用户已登录,认证通过')
return func
def home():
print('主页')
def america():
print('----欧美专区----')
def japan():
print('----日韩专区----')
america = login(america)
america()
japan = login(japan)
japan()
使用闭包函数实现功能(装饰器的本质)
account = {
'is_authenticated': False,
'username': 'zzsb',
'password': '123',
}
def login(func):
def inner():
if not account['is_authenticated']:
usr, pwd = input('请输入用户名 ').strip(), input('请输入密码 ').strip()
username, password = account.get('username'), account.get('password')
if usr == username and pwd == password:
print('登入成功')
account['is_authenticated'] = True
func()
else:
print('用户名或密码错误')
else:
print('用户已登录,认证通过')
func()
return inner
def home():
print('主页')
def america():
print('----欧美专区----')
def japan():
print('----日韩专区----')
america = login(america)
america()
japan = login(japan)
japan()
高级版
如果被要求登录认证的函数是有参数的,上面的代码就会报错
account = {
'is_authenticated': False,
'username': 'zzsb',
'password': '123',
}
def login(func):
def inner(*args,**kwargs):
if not account['is_authenticated']:
usr, pwd = input('请输入用户名 ').strip(), input('请输入密码 ').strip()
username, password = account.get('username'), account.get('password')
if usr == username and pwd == password:
print('登入成功')
account['is_authenticated'] = True
func(*args,**kwargs)
else:
print('用户名或密码错误')
else:
print('用户已登录,认证通过')
func(*args,**kwargs)
return inner
def home():
print('主页')
def america():
print('----欧美专区----')
def japan(vip_level):
if vip_level > 3:
print('解锁高级玩法')
else:
print('----日韩专区----')
america = login(america)
america()
japan = login(japan)
japan(4)
python装饰器用法
在需要进行登录验证的函数上加上@装饰器函数
account = {
'is_authenticated': False,
'username': 'zzsb',
'password': '123',
}
def login(func):
def inner(*args,**kwargs):
if not account['is_authenticated']:
usr, pwd = input('请输入用户名 ').strip(), input('请输入密码 ').strip()
username, password = account.get('username'), account.get('password')
if usr == username and pwd == password:
print('登入成功')
account['is_authenticated'] = True
func(*args,**kwargs)
else:
print('用户名或密码错误')
else:
print('用户已登录,认证通过')
func(*args,**kwargs)
return inner
def home():
print('主页')
@login
def america():
print('----欧美专区----')
@login
def japan(vip_level):
if vip_level > 3:
print('解锁高级玩法')
else:
print('----日韩专区----')
america()
japan(4)
列表生成式
将列表的值的元素每一个加1
- 使用lambda函数
a = [1, 2, 3, 4, 5]
res = map(lambda x: x + 1, a)
print(list(res))
- 使用列表生成式
a = [1, 2, 3, 4, 5]
ls = [i+1 for i in a]
print(ls)
生成器
range在python2和python3的区别
-
python2环境下自动生成数字存入对象
>>> range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-
python3中只是一个生成器对象
>>> range(10)
range(0, 10)
引子
- for循环生成100000000 个数字
import time
start= time.time()
for i in range(100000000):
if i == 100:
break
print(i)
print(time.time()-start) # 2.91226005554
- while循环生成100000000 个数字
import time
start = time.time()
count = 0
while count < 100000000:
count += 1
if count > 100:
break
print(time.time() - start) # 0.000188112258911
什么是生成器
以上2个例子:
for循环执行效率更慢, 因为先生成1-100000000的列表,再进行判断, 这种占用大量内存空间
while循环是通过某种算法( 每次自加1)计算出下一次循环的数据, 根据需要进行取值, 这种占用内存更少
在python中, 这种一边循环一边计算下一次循环的值的机制就是生成器, 也可理解为一种数据类型
创建生成器
>>> (x*x for x in range(10))
<generator object <genexpr> at 0x10ddf5a50>
生成器取值
通过next取值
>>> test=(x*x for x in range(5))
>>> test
<generator object <genexpr> at 0x103979d58>
>>> next(test)
0
>>> next(test)
1
>>> next(test)
4
>>> next(test)
9
>>> next(test)
16
>>> next(test) # 取不到值就会报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
取出所有值
>>> test=(x*x for x in range(5))
>>> for i in test:
... print(i)
...
0
1
4
9
16
函数生成器
实现斐波那契数列
1 2 3 5 8 13....
count = 0
a = 0
b = 1
while count < 20:
old_a = a
a = b
b = old_a + a
'''
第一次循环: old_a=0 a=1 b=1
第二次循环: old_a=1 a=1 b=2
第三次循环: old_a=1 a=2 b=3
第四次循环: old_a=2 a=3 b=5
'''
print(b)
count += 1
通过生成器实现斐波那契数列
def MakeServer(n):
'''
生成器函数
'''
count = 0
a = 0
b = 1
while count < n:
old_a = a
a = b
b = old_a + a
'''
第一次循环: old_a=0 a=1 b=1
第二次循环: old_a=1 a=1 b=2
第三次循环: old_a=1 a=2 b=3
第四次循环: old_a=2 a=3 b=5
'''
yield b # 暂停并返回b的值, 下面的代码通过next()函数触发才会执行
count += 1
obj=MakeServer(6)
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
print('----生成器----')
print(obj.__next__())
print(obj.__next__())
Yield接收外部输入的值
错误代码
TypeError: can't send non-None value to a just-started generator
def g_test():
while True:
n = yield
print('recieve from outside', n)
g=g_test()
for i in range(10):
g.send(i)
解决方案
先发送一个None给到生成器
def g_test():
while True:
n = yield
print('recieve from outside', n)
g=g_test()
g.send(None) # 调用生成器, 同时会发送None到Yield
for i in range(10):
g.send(i) # 调用生成器, 同时发送i
def g_test():
while True:
n = yield
print('recieve from outside', n)
g=g_test()
g.__next__() # 调用生成器, 同时会发送None到Yield
for i in range(10):
g.send(i) # 调用生成器, 同时发送i
实现单线程下的多并发
def consumer(name):
print('消费者%s准备吃包子了...' % name)
while True:
baozi_num = yield
print('消费者%s吃了包子%s' % (name, baozi_num))
c1 = consumer('c1')
c2 = consumer('c2')
c3 = consumer('c3')
c1.__next__()
c2.__next__()
c3.__next__()
for i in range(1, 6):
print('-------生成了第%s批包子-------' % i)
c1.send(i)
c2.send(i)
c3.send(i)
迭代器
可迭代对象
直接作用于for循环的对象统称为可迭代对象 ( 可以被循环 )
- 判断一个对象是否是Iterable对象
>>> from collections import Iterable
>>> isinstance([],Iterable)
True
>>> isinstance({},Iterable)
True
>>> isinstance('abc',Iterable)
True
>>> isinstance((x for x in range(10)),Iterable)
True
>>> isinstance(100,Iterable)
False
- 哪些数据类型可以直接作用于for循环
- 集合类型数据 , list, tuple, dict, set, str等
- generator, 包括生成器和带yield的generator function
什么是迭代器
可以被next() 函数调用并不断返回下一个值的对象称为迭代器: Iterator
- 判断一个对象是否是Iterator
>>> from collections import Iterator
>>> isinstance(1,Iterator)
False
>>> isinstance({},Iterator)
False
>>> isinstance([],Iterator)
False
>>> isinstance('周泽SB',Iterator)
False
>>> isinstance((i for i in range(10)),Iterator) # 生成器对象是迭代器对象
True
>>> iter({}) # 可迭代对象通过调用函数iter()可转换为迭代器对象
<dict_keyiterator object at 0x10a9ad1d8>
>>> isinstance(iter({}),Iterator)
True
>>> isinstance(iter([]),Iterator)
True
>>> isinstance(iter('周泽SB'),Iterator)
True