函数(二)

闭包

闭包现象

正常来说,函数调用结束, 函数体里的变量都会被销毁, 而有一种现象是当函数执行完毕, 函数体里的变量不会被销毁, 因为这个变量被内嵌函数的一段代码调用着,无法销毁, 这种现象叫做闭包

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循环
  1. 集合类型数据 , list, tuple, dict, set, str等
  2. 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
posted @ 2019-09-29 17:15  cjw1219  阅读(153)  评论(0编辑  收藏  举报