05 python 要点 (函数式编程)

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

复习时先看看这个https://blog.csdn.net/weixin_39880623/article/details/110153616?


第一章 函数式编程

一、生成器  (generator)

  • 通过列表⽣成式我们可以直接创建⼀个列表。如果列表元素可以按照某种算法推算出来那我们是否可以在循环的过程中不断推 算出后续的元素呢这样就不必创建完整的list从⽽节省⼤量的空间。在Python中这种⼀边循环⼀边计算的机 制称为⽣成器generator

    [x + 1 for x in range(10) if x %2 == 0]  # 列表生成式
    y = (x + 1 for x in range(10) if x %2 == 0)  # 生成器 
    # <generator object <genexpr> at 0x000001F93FF9BCF0>
    next(y)   # 指针
  • 创建生成器列表生成式我们知道只要将最外一层的中括号改为小括号就是生成器.
  • 引出生成器对象保存元素的算法同时记录游标的位置
  • ⽣成器保存的是算法每次调⽤ next(y) 就计算出 g 的下⼀个元素的值直到计算到后⼀个元素没有更多的元素时抛出 StopIteration 的异常。
  • 创建一个生成器
    • 1、通过列表生成式来创建
    • 2、通过函数创建生成器yield
  • 遍历生成器中的元素内容
    • 1、通过next(y)        # 超出范围会报错
    • 2、通过for遍历
    • 3、通过object内置函数__next__进行输出      # 超出范围会抛出异常
    • 4、send函数           # 但生成器的第一个值必须是send(None),后面的send里面的值无所谓但不能为空且会被打印出来
  • 可以通过列表进行输出、查看print(list((i+1 for i in range(1,11))))

如果推算的算法比较复杂用类似列表生成式的 for 循环无法实现的时候还可以用函数来实现。比如著名的斐波拉契数列Fibonacci除第一个和第二个数外任意一个 数都可由前两个数相加得到([1, 1, 2, 3, 5, 8, 13, 21, 34, ...])

仔细观察可以看出fib_a函数实际上是定义了斐波拉契数列的推算规则可以从第一个元素开始推算出后续任 意的元素这种逻辑其实非常类似generator。也就是说上面的函数和generator仅一步之遥。要把 fib 函数变 成generator只需要把 print(b) 改为 yield(b) 就可以了

def fib_a(times):
    # 初始化    
    n = 0
    a, b = 0, 1
    while n < times:
        yield(b)  # 改为生成器  # print(b) 
        a, b = b, a + b
        n += 1
    return 'done'
 
z = fib_a(5) 
print('返回值', z)

for i in fib_a(5):
    print(i)

在上⾯fib 的例⼦我们在循环过程中不断调⽤ yield 就会不断中断。当然要给循环设置⼀个条件来退出循环不然就会产⽣⼀个⽆限数列出来。同样的把函数改成generator后我们基本上从来不会⽤ next() 来获取下⼀个返回值⽽是直接使⽤ for 循环来迭代

但是⽤for循环调⽤generator时发现拿不到generator的return语句的返回值。如果想要拿到返回值必须捕获 StopIteration错误返回值包含在StopIteration的value中

def fib_a(times):
    # 初始化    
    n = 0
    a, b = 0, 1
    while n < times:
        yield(b)  # 改为生成器
        a, b = b, a + b
        n += 1
    return 'done'
 
z = fib_a(5) 
print('返回值', z) # 返回值 <generator object fib_a at 0x000001F9410399E0>

while True:
    try:
        x = next(z)
        print("value:%d" % x)   # value:5
    except StopIteration as e:
        print("⽣成器返回值:%s" % e.value)   # ⽣成器返回值:done
        break

这样的到结果是可以拿到返回值的我们通过有限的值捕获异常拿到return的值.

⽣成器是这样⼀个函数它记住上⼀次返回时在函数体中的位置。对⽣成器函数的第⼆次或第 n 次调⽤跳转⾄该函数中间⽽上次调⽤的所有局部变量都保持不变。⽣成器不仅“记住”了它数据状态⽣成器还“记住”了它在流控制构造在命令式编程中这种构造不只是数据值中的位置。⽣成器的特点

  • 1. 节约内存

  • 2. 迭代到下⼀次的调⽤时所使⽤的参数都是第⼀次所保留下的在整个所有函数调⽤的参数都是第⼀次所调⽤时保留的⽽不是新创建的.

二、迭代器  (Iterator)

迭代是访问集合元素的⼀种⽅式。迭代器是⼀个可以记住遍历的位置的对象。迭代器对象从集合的第⼀个元素开始访问直到所有的元素被访问完结束。迭代器只能往前不会后退

可以被next()函数调⽤并不断返回下⼀个值的对象称为迭代器Iterator.

list 、 dict 、 str 不是 Iterator(迭代器) 因为Python的 Iterator 对象表示的是一个数据流Iterator对象可以被 next() 函数调用并不断返回下一个数据直到没有数据时抛出 StopIteration 错误。可以把这个数据流看做是一个有序序列但我们却不能提前知道序列的长度只能不断通过 next() 函数实现按需计算下一个数据所以 Iterator 的计算是惰性的只有在需要返回下一个数据时它才会计算

  • 1. 节约内存
  • 2. 迭代到下⼀次的调⽤时所使⽤的参数都是第⼀次所保留下的在整个所有函数调⽤的参数都是第⼀次所调⽤时保留的⽽不是新创建的
    • 注意 Iterator 对象Iterable 对象一个是迭代器一个是可迭代对象
    • 所以可作用于 for 循环的对象都是 Iterable 类型
    • 可作用于 next( ) 函数的对象都是 Iterator 类型它们表示一个惰性计算的序列
    • 集合数据类型list 、 dict 、 str 等是 Iterable (可迭代对象)但不是 Iterator 不过可以通过 iter( ) 函数获得⼀个 Iterator 对象。
    • Iterator 甚至可以表示一个无限大的数据流例如全体自然数。而使用list是永远不可能存储全体自然数的

  • 通过迭代器读取大文件
def read_lines(f, newline):
    buf = ''
    while True:
        while newline in buf:
            # 找到字符串位置
            pos = buf.index(newline)
            yield buf[:pos]
            buf = buf[pos + len(newline):]
        # 读取该长度的字符串
        chunk = f.read(512)
        if not chunk:
            # 文件读取完成
            yield buf
            break
        buf += chunk

with open('NumPy作业答案.txt', 'r', encoding='utf-8') as f:
    # 用||分隔段落记录位置
    for line in read_lines(f, '||'):   # f 为打开的文件
        print(line)

三、高阶函数  (Higher-order function)

  • 函数是Python内建支持的一种封装我们通过把大段代码拆成函数通过一层一层的函数调用就可以把复杂任务分解成简单的任务这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。
  • 函数式编程请注意多了一个“式”字——Functional Programming虽然也可以归结到面向过程的程序设计但其思想更接近抽象的计算。
  • 我们首先要搞明白计算机Computer和计算Compute的概念。
    在计算机的层次上CPU执行的是加减乘除的指令代码以及各种条件判断和跳转指令所以汇编语言是贴近 计算机的语言。而计算则指数学意义上的计算越是抽象的计算离计算机硬件越远。
  • 函数式编程就是一种抽象程度很高的编程范式。
  • 函数式编程的一个特点就是允许把函数本身作为参数传入另一个函数还允许返回一个函数
  • Python对函数式编程提供部分支持。由于Python允许使用变量因此Python不是纯函数式编程语言。
  • 函数名也可以是变量
  • 函数是由def定义函数名括号括号参数冒号函数体组成
  • 函数名是指向函数的变量例如abs()这个函数可以将abs看成变量它指向一个可以求绝对值的函数如果把abs指向其他的对象就会失去原有的功能.

我们想到函数可以传参数而函数名可以做变量那我们函数里面的参数也可以传入函数名

那我们可以看到将函数作为参数传给另一个参数就是高阶函数.

def add_1(i):
    return i*2

def total(x, y, fun):
    return fun(x) + fun(y)

add_sum = total(1, 2, add_1)
print(add_sum)

1、map函数

把一个可迭代对象中的每个元素转换为一个新的对象最后返回一个新的可迭代对象

来看一下map函数的参数与返回值

map(func, *iterables) --> map object
def fun(i):
    return i*3

for i in map(fun, [1, 2, 3]):
    print(i)    # 3, 6, 9

map()参数详解

  • func代表传入参数为函数这里的函数指定指向函数的函数名
  • iterables代表参数指定的可迭代的, 列表
  • 返回值返回处理好的数据
  • map()函数是将传入的func函数作用于可迭代的数据里面每个元素并将处理好的新的结果返回

map( ) 传入的第一个参数是 fun_a 即函数对象本身。由于结果 list_a 是一个 Iterator Iterator 是惰性序列因此通过 list() 函数让它把整个序列都计算出来并返回一个list。

很多情况下也可以使用for循环也可以解决问题但实际上map作为高级函数将运算抽象化还可计算复杂的函数例如将列表的元素int类型转换为int类型只需要一行代码

list(map(str, [1, 2, 3, 4, 5]))    # ['1', '2', '3', '4', '5']

2、reduce函数

reduce函数把一个可迭代对象中的每个元素做聚合处理最后返回一个聚合之后的值

from functools import reduce
list_a = [1, 2, 3, 4, 5]
 
def fun_b(x, y):
    return x + y
reduce(fun_b, list_a)   # 运算结果如下 15

reduce() 参数详解

  • function:一个有两个参数的函数, 聚合方式
  • sequence:是一个序列是一些数据的集合或者是一组数据可迭代对象
  • initial可选初始参数
  • 返回值返回函数计算的结果
  • reduce()函数, 使用function函数有两个参数先对集合中的sequence第 1、2 个元素进行操作如果存在。
  • initial参数则将会以sequence中的第一个元素和initial作为参数用作调用得到的结果再与sequence中的下一个数据用 function 函数运算最后得到一个结果。

依次按照顺序从列表list_a中提取两个元素作为参数进入fun_b中进行运算得到的结果作为下次运算时的其中一个参数再从列表中取出一个元素再进行运算。最终得到的结果是总和的计算。

3、filter函数

Python内建的 filter() 函数用于过滤序列和 map() 类似 filter() 也接收一个函数和一个序列

但是不同的是 filter() 把传入的函数依次作用于每个元素然后根据返回值是 True 还是 False 决定元素的保留与丢弃

filter的参数:  filter(function, iterable)

def not_odd(num):
    return num % 2 == 0

newlist = filter(not_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(newlist)    # <filter object at 0x000001F9419D7C40>
print([i for i in newlist])    # [2, 4, 6, 8, 10]

参数列表:

  • function判断函数。
  • iterable序列可迭代对象。
  • 返回值返回列表
  • filter函数序列可迭代对象的每个元素作为参数传递给函数进行判断然后返回 True 或 False最后将返回 True 的元素放到新列表中

这里filter函数的两个参数第一个是过滤方法第二个是需要过滤的列表将列表里面的元素依次带入函数中进行运算得到的结果如果为True时将此结果作为新的filter对象保留等待函数里面的列表执行完成后返回最终的值这里的值为列表也就是过滤掉了False的数据或元素。

那可以用filter来计算素数计算素数的其中一个方法是埃氏筛法。 给出要筛数值的范围n找出以内的素数。先用2去筛即把2留下把2的倍数剔除掉再用下一个质数也就是3 筛把3留下把3的倍数剔除掉接下去用下一个质数5筛把5留下把5的倍数剔除掉不断重复下去......
用Python来实现这个算法我们先写一个生成器构造一个从3开始的无限奇数序列首先偶数列先排除

def oddnum():
    n = 1
    while True:
        n = n + 2
        yield n

def undivisible(n):
    print('迭代', n)
    return lambda x: x % n > 0

def primes():
    yield 2
    it = oddnum() # 初始序列
    while True:
        n = next(it) # 返回序列的第一个数
        yield n
        it = filter(undivisible(n), it) # 构造新序
        
for n in primes():
    if n < 100:
        print(n)
    else:
        break

生成器与迭代器、匿名函数、filter函数其中:

  • 第一段代码生成了以3开始的奇数序列
  • 第二段代码自定义过滤函数包含匿名函数判断值的取余是否能被整除
  • 第三段代码用来返回素数这里先返回一个2为素数因为偶数都被排除了所以整除3为基础进行排除将数据不断 地迭代生成留下对应的素数序列。
  • 那这里就对应的filter函数就是用来过滤的方法进行返回数据。

3.1、max and min

#  print(max(emps,key=lambda x:x['salary']))

3.2、sorted函数

把一个可迭代对象里面的每个元素做排序返回一个列表

# 根据员工的年龄降序排序print(sorted(emps,key=lambda x : x['age'],reverse=True))

4、匿名函数

lambda [list] : 表达式

我们可以看到对于比较单行返回的函数使用 lambda 表达式可以省去定义函数的过程让代码更加简洁针对不需要多次复用的函数使用 lambda 表达式可以在用完之后立即释放提高程序执行的性能。而且还能配合其他的一些高阶函数配合使用。

语法lambda [list] : 表达式

lambda  参数列表

  • [list]表示参数列表
  • 注意参数与表达式之间需要冒号来区分
  • 表达式 表达式方法非常多表达形式也非常多
  • 返回值 为表达式的结果value

对于比较单行返回的函数使用 lambda 表达式可以省去定义函数的过程让代码更简洁针对不需要多次复用的函数使用 lambda 表达式可以在用完之后立即释放提高程序执行的性能。而且还能配合其他的一些高阶函数配合使用。

def add(x, y):
    return x+ y
print('add_1', add(3,4))  # add_1 7

add_2 = lambda x,y:x+y
print('add_2',add_2(3, 4))   # add_2 7

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: python