第2章 函数

参考: 函数的基本使用, 函数的参数, 名称空间与作用域, 函数对象+函数嵌套+闭包, 装饰器, 迭代器, 生成器, 函数递归, 二分法, 面向过程与函数式, 函数标注, 内置函数

一. 什么是匿名函数? 匿名函数一般结合什么一起使用,请列举几个常用的

"""
匿名函数与有名函数有相同的作用域,但是匿名意味着引用计数为0,使用一次就释放,所以匿名函数用于临时使用一次的场景,匿名函数通常与其他函数配合使用. 如下
    max(),min()sorted(),map(),reduce(),filter()
    补充1: sorted()排序最好排序的对象是同一种类型. 
    补充2: map(),filter()返回的都是生成器对象.
    补充3: 使用reduce()需要导入functools模块拿到reduce.
"""

二. 描述下列常见内置函数的作用可用代码说明map,zip,filter,sorted,reduce

"""
map(func, iterable)  遍历可迭代对象, 将可迭代对象遍历过程中的每一个值作为func的实参传入, 将func每次执行的结果保存, 返回一个迭代器对象.

zip(*iterables) 遍历每组的可迭代对象, 将每组每次遍历的结果保存到元组中, 且每组的元素个数需要相同, 最后返回一个迭代器对象.

filter(func, iterable) 遍历可迭代对象, 将可迭代对象遍历过程中的每一个值作为func的实参传入, 将func每次执行的结果布尔值为True的保存, 返回一个迭代器对象.

sorted(iterable, key=func[, reverse]) 遍历可迭代对象, 将可迭代对象遍历过程中的每一个值作为func的实参传入, 将func每次执行的结果保留与下一次遍历的值进行比较, 按照最后一个参数指定是降序还是升序. (注意: 遍历的结果主要都是同种类型之间)

reduce(func, sequence[, initial])  遍历序列对象, 将序列对象遍历过程中的每一个值作为func的实参传入, 传入的第一个值赋值给第一个形参, 传入的第二个值以及之后的值都赋值给第二个形参, 如果指定了初始值那么第一个形参将会是它. 每次执行完毕都保留给第一个形参, 接着与遍历的下一次的值赋值给第二个形参的参数与之前保留的第一个形参进行运算. 最终返回运算后的结果. 
"""
# zip 拉链
res = zip([1, 2], (1, 2))
print(res)   # <zip object at 0x000001CD79090BC0>
print([i for i in res])  # [(1, 1), (2, 2)]

# map 映射
res = map(lambda x: x+1, [1, 2, 3])
print(res)  # <map object at 0x000002C641BFE430>
print([i for i in res])  # [2, 3, 4]

# filter 过滤
res = filter(lambda x: x > 1, [1, 2, 3])
print(res)  # <filter object at 0x000002C641C0A8B0>
print([i for i in res])  # [2, 3]

# sorted 排序
salaries = {
    'siry': 3000,
    'tom': 7000,
    'lili': 10000,
    'jack': 2000
}
res = sorted(salaries, key=lambda k: salaries[k])  # 升序
print(res)   # ['jack', 'siry', 'tom', 'lili']

res = sorted(salaries, key=lambda k: salaries[k], reverse=True)  # 降序
print(res)   # ['lili', 'tom', 'siry', 'jack']

# reduce 合并
from functools import reduce
res = reduce(lambda x,y: x+y, [1, 3, 4], 0)
print(res)  # 8

三. 简述可迭代对象,迭代器对象,生成器对象及应用场景

"""
可迭代对象: 从语法形式上讲,内置有__iter__方法的对象
    应用:  字符串,列表,元组。对于没有索引的字典、集合等非序列类型,必须找到一种不依赖索引来进行迭代取值的方式.
    
迭代器对象: 从语法形式上讲,内置有__iter__和__next__方法的对象. 
    迭代器对象执行__iter__方法得到的仍然是迭代器本身,而执行迭代器__next__方法就会计算出迭代器中的下一个值.
    迭代器是Python提供的一种统一的、不依赖于索引的迭代取值方式,只要存在多个“值”,无论序列类型还是非序列类型都可以按照迭代器的方式取值.
    
    实例: for循环内部将所有可迭代对象使用__iter__方法得到迭代器对象, 再使用__next__方法迭代取值, 将每次取出来的值赋值给in前面自定义的变量, 周而复始, 直到迭代完毕抛出异常StopIteration才跳出循环.
    
    应用:
        1. 为序列和非序列类型提供了统一的迭代取值方式
        2. 惰性计算: 对迭代器本身来说, 同一时刻在内存中只有一个值, 它可以存放无限大的数据流.     
    
生成器对象: 函数体内包含yield关键字, 生成器表达式
    生成器内置有__iter__和__next__方法,所以生成器本身就是一个迭代器
    应用:
        1. 有了yield关键字, 就有了一种自定义迭代器的实现方式.  
        2. 生成器表达式在内存中一次只产生一个值, 更加节省内存空间.
"""

四. 现有三个普通函数a,b,c都需要用户登陆之后才能访问。现需要你写一个装饰器校验用户是否登陆,并且用户只要登陆一次其他函数在调用时也无需再校验(提示:全局标志位)?

import time

login_stat = False
accounts = {
    'egon': '123',
    'tank': '123',
    'alex': '123',
}


def auth(func):
    def wrapper(*args, **kwargs):
        if login_stat:
            return func(*args, **kwargs)
        else:
            print("对不起, 请先登录!")
            login()
    return wrapper


def login():
    name = input('请输入账户名: ').strip()
    pwd = input('请输入账户密码: ').strip()
    if name in accounts:
        password = accounts.get(name)
        if pwd == password:
            print("登录成功!")
            global login_stat
            login_stat = True
        else:
            print('登录失败! 密码输入错误!')
    else:
        print("登录失败! 用户名不存在!")

@auth
def index():
    print('正在执行任务...')
    time.sleep(1)
    print('执行任务完毕...')
index()

五. 什么是函数的递归调用?书写递归函数需要注意什么?你能否利用递归函数打印出下面列表中每一个元素(只能打印数字),l = [1,[2,[3,[4,[5,[6,[7,[8,[9]]]]]]]]]

"""
什么是函数的递归调用?
    是函数嵌套调用的一种特殊形式.
    指的是在调用一个函数的过程中又直接或间接的调用到本身.

书写递归函数需要注意什么?
    函数的递归调用一定要有一个结束条件: 递归调用都是一个无限循环的过程,虽然python对函数的递归调用的深度做了限制并不会进入无限循环, 只是会抛出异常, 但是要明白函数递归不应该无限的调用下去, 必须在满足某种条件下结束递归调用    
"""
li = [1, [2, [3, [4, [5, [6, [7, [8, [9]]]]]]]]]


def func(sequence):
    for item in sequence:
        if isinstance(item, int):
            print(item, end=' ')  # 1 2 3 4 5 6 7 8 9
        else:
            func(item)


func(li)

六. 下面这段代码的输出结果是什么,并给出你的解释(难点,重点)

def index():
    return [lambda x: i * x for i in range(4)]


print([m(2) for m in index()])
"""
提示: for循环内部将拿到的无论是可迭代对象,还是迭代器对象都会一律调用__iter__转换成迭代器对象, 使用__next__方法取循环值, 直到检测异常抛出的StopIteration退出循环. 下面我就不多赘述了, 直接忽略.

1. 定义index(): 开辟一块内存空间将index中的代码块丢进去, 将内存地址与index函数名绑定
     index = <function index at 0x0000013AB693C9D0>
     
2. 执行到print: 发现它是一个列表生成式, 我们把列表生成式理解为先定义一个空列表, 然后执行for循环in后面的可迭代对象, 发现index()是函数调用语句, 那么将触发index函数的运行.
    list1 = []
    for m in index():  --> 触发index函数的运行
        ...
        
    2.1 触发运行先执行[lambda x: i * x for i in range(4)]中的语句, 发现又是一个列表生成式. 
    这时在函数内部又将按照步骤2的方式. 先定义空列表, 再for循环range(4), for循环会将每次迭代取值的结果赋值给变量i, !!注意!!这时只是简单的循环, i并没有赋值给前面的匿名函数, 我们要知道触发函数的执行必须加括号调用. 
    这么几步我们理解为for循环只是简单的提供了循环次数4次, 在这循环4次的时候, 每次都会申请一块内存空间, 将匿名函数体代码扔进去, 而内存地址就被append到了定义的列表中, 要知道列表中存的并不是值, 而是值对内存地址的引用, 真正的值是在栈区:
        list2   = []
        for i in range(4):
            res = lambda x: i * x
            li.append(res) 
        
        list2   = [<function index.<locals>.<lambda> at 0x000002309F9FCF70>, 
            <function index.<locals>.<lambda> at 0x000002309FA09040>, 
            <function index.<locals>.<lambda> at 0x000002309FA090D0>, 
            <function index.<locals>.<lambda> at 0x000002309FA09160>]
    
    2.2 接着return将返回list2, 接着执行print中的列表生成式, 之前的list1空列表以及定义了, 现在for循环将遍历list2列表将列表中的内存地址赋值给前面定义的变量m, 接着发现变量m进行的操作是m(2).
    !!!注意!!! 这时的m是被赋值了一个函数的内存地址这时加了括号, 我们因该窃喜, 这不就是函数的调用吗! 接着就是函数的传参m传给了x, 接着执行函数体代码 lambda x: i * x 发现这里有个i, 那么这个i是多少呢? 
    我们知道之前for循环对i进行了赋值, for循环会保留最后的赋值i, 因为前面的变量i被后面的覆盖了. 如下: 
        for j in range(4):
            ...
        print(j)  # 3
    好, 我们回到执行函数的地方, print语句在全局, 但是i在index局部, 它是怎么拿到i的呢? 我们别忘了 匿名函数在申请内存地址的的时候在局部index中申请的, 匿名函数和i在同一个作用域, index只是将列表它们返回到了全局. 
    这种打破作用域访问的机制, 使我们在全局访问到了局部的匿名函数. 如下接着执行: 
        list1 = []
        for m in index():  --> 触发index函数的运行
            m = <function index.<locals>.<lambda> at 0x000002309F9FCF70>
            res = m(2) = lambda x: 3 * x 
            list1.append(res)
"""
def index():
    sequence = []
    for i in range(4):
        sequence.append(lambda x: i * x)
    # print(i)  # for循环已经执行完毕,i的值等于3
    # print(locals())
    '''
    {'sequence': [<function index.<locals>.<lambda> at 0x000001A222BEC8B0>, 
    <function index.<locals>.<lambda> at 0x000001A222BECF70>,
     <function index.<locals>.<lambda> at 0x000001A222BECD30>, 
     <function index.<locals>.<lambda> at 0x000001A222BECE50>], 
     'i': 3}
    '''
    return sequence


sequence1 = []
for m in index():
    '''
    for m in [lambda x: i * x, lambda x: i * x, lambda x: i * x, lambda x: i * x]
    for m in [<function index.<locals>.<lambda> at 0x000002309F9FCF70>, 
    <function index.<locals>.<lambda> at 0x000002309FA09040>, 
    <function index.<locals>.<lambda> at 0x000002309FA090D0>, 
    <function index.<locals>.<lambda> at 0x000002309FA09160>]
    m = lambda x: i * x  -->   i都是3
    m = lambda x: i * x
    m = lambda x: i * x
    m = lambda x: i * x
    '''
    # print(index())
    sequence1.append(m(2))
print(sequence1)  # [6, 6, 6, 6]
print(globals())
'''
{...'index': <function index at 0x000001E276DACA60>, 
'sequence1': [6, 6, 6, 6], 
'm': <function index.<locals>.<lambda> at 0x000001E276DACF70>}
'''

参考网址: https://segmentfault.com/a/1190000016577391

posted @ 2020-05-11 20:40  给你加马桶唱疏通  阅读(157)  评论(1编辑  收藏  举报