函数式编程(一)

函数式编程总体介绍

函数式编程(functional programming)其实是个很古老的概念,诞生距今快60年啦!

最古老的函数式编程语言Lisp

新出现的函数式编程语言:比如Erlang、Scala、clojure等

热门语言:Python、java、JavaScript、C++等都增加了函数式编程的一些特性。

高阶函数和内存分析_可变参数的传递处理

  1. 函数式编程最鲜明的特点就是:函数是一等公民(first class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。
  2. 一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数。
  3. Python内建的高阶函数有mapreducefiltersorted

高阶函数_内存状态分析

【示例】高阶函数案例

#coding=utf-8
def test1():print("I'm test!")
​
def test2(func):  # test2就是一个高阶函数func()print("test2 running...")
​
if __name__ == '__main__':print(test1) # print(type(test1)) # a = test1  # a和test1都指向了同一个函数对象a()   # I'm test!test2(a)  #a作为参数传递给test2()

上面代码,内存示意图如下:

image-20211212183912236

lambda表达式和匿名函数 

lambda表达式可以用来声明匿名函数。lambda函数是一种简单的、在同一行中定义函数的方法。lambda函数实际生成了一个函数对象。

lambda表达式只允许包含一个表达式,不能包含复杂语句,该表达式的计算结果就是函数的返回值。

lambda表达式的基本语法如下:

lambda arg1,arg2,arg3...  : <表达式>

arg1 arg2 arg3为函数的参数。<表达式>相当于函数体。运算结果是:表达式的运算结果。

【操作】lambda表达式使用

f = lambda a,b,c:a+b+c
print(f)
print(f(2,3,4))
​
g = [lambda a:a*2,lambda b:b*3,lambda c:c*4]
print(g[0](6),g[1](7),g[2](8))

偏函数

Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。要注意,这里的偏函数和数学意义上的偏函数不一样。

偏函数: 作用就是把一个函数某些参数固定住(也就是设置默认值),返回一个新的函数,调用这个新的函数会更简单。

举例如下:

int()函数可以把字符串转换为整数,当仅传入字符串时,int()函数默认按十进制转换,代码如下:

print(int('12345'))

但int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做N进制的转换:

#base参数
print('转换为八进制',int('12345', base=8))
print('转换为十六进制',int('12345', 16))

假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,我们想到,可以定义一个int2()的函数,默认把base=2传进去,现在定义一个int2函数,代码如下:

def int2(x, base=2):return int(x, base)
​
print(int2('1000000')) #64
print(int2('1010101')) #85

functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2

#coding=utf-8
import functools
int2 = functools.partial(int, base=2)
print(int2('1000000')) #64
print(int2('1010101')) #85
print(int2('1000000', base=10)) #也可以修改base的值

闭包核心概念_内存分析

  • 局部变量:如果名称绑定再一个代码块中,则为该代码块的局部变量,除非声明为nonlocal或global
  • 全局变量:如果模块绑定在模块层级,则为全局变量
  • 自由变量:如果变量在一个代码块中被使用但不是在其中定义,则为自由变量

闭包概念和第一个闭包程序

我们知道,函数作用域是独立的、封闭的,外部的执行环境是访问不了的,但是闭包具有这个能力和权限。

闭包是一个函数,只不过这个函数有[超能力],可以访问到另一个函数的作用域。

「函数」和「自由变量」的总和,就是一个闭包。

闭包的特点:

第一,闭包是一个函数,而且存在于另一个函数当中

第二,闭包可以访问到父级函数的变量,且该变量不会销毁

【示例】一个简单的闭包

#coding=utf-8
"""
闭包的特点:
1. 存在内外层函数嵌套的情况
2. 内层函数引用了外层函数的变量或者参数(自由变量)
3. 外层函数把内层的这个函数本身当作返回值进行返回,而不是返回内层函数产生的某个值
"""
def outer():a = 1def inner():nonlocal a#闭包是由于函数内部使用了函数外部的变量。这个函数对象不销毁,则外部函数的局部变量也不会被销毁!print("a:",a)a += 1return inner
​
inn = outer()
inn()
inn()
​

闭包的内存分析(重要)

  1. 执行完inn = outer()的内存图。 outer()栈帧执行完后实际已经消失了,画上去,是为了展现关系。

    image-20211214100335414

  2. 执行完inn = outer()的内存图。由于inner()内部函数的调用,outer()栈帧消失后,局部变量a指向的对象1仍然存在。从而形成了"闭包"。

    image-20211213112450732

  3. 第一次调用inn(),从而调用内部函数,仍然可以拿到以前局部变量指向的对象1

    image-20211213112713065

  4. 第二次调用inn(),仍然可以继续拿到以前局部变量指向的对象1,并将值变为2

image-20211213112807602

闭包可以当成两个部分组成的整体:

  1. 函数
  2. 自由变量

闭包的作用

作用1:隐藏变量,避免全局污染

作用2:可以读取函数内部的变量

同时闭包使用不当,优点就变成了缺点:

缺点1:导致变量不会被垃圾回收机制回收,造成内存消耗

缺点2:不恰当的使用闭包可能会造成内存泄漏的问题

闭包和自由变量_全局变量污染问题的解决

【示例】使用全局变量实现变量自增,但污染了其他程序

#coding=utf-8
#需求:实现变量a 自增
#通过全局变量,可以实现,但会污染其他程序
a = 10
def add():global aa+=1print("a:",a)
​
def print_ten():if a==10:print("ten!")else:print("全局变量a,不等于10")
​
add()
add()
add()
print_ten()
"""
运行效果:
a: 11
a: 12
a: 13
全局变量a,不等于10
"""

【示例】定义局部变量,不污染,但无法递增

#coding=utf-8
#需求:实现变量a 自增
#通过局部变量,不能实现递增
a = 10
def add():a = 10a += 1print("a:",a)
​
def print_ten():if a==10:print("ten!")else:print("全局变量a,不等于10")
​
add()
add()
add()
print_ten()
"""
运行结果:
a: 11
a: 11
a: 11
ten!
"""

【示例】通过闭包,可以是函数内部局部变量递增,也不会影响全部变量,完美!!

#coding=utf-8
#需求:实现变量a 自增
#通过闭包,也没有污染全局变量a。也实现了自增
a = 10
def add():a = 10def increment():nonlocal aa +=1print("a:",a)return increment
​
def print_ten():if a==10:print("ten!")else:print("全局变量a,不等于10")
​
increment = add()
increment()
increment()
increment()
print_ten()
print("global a:",a)
"""
运算结果:
a: 11
a: 12
a: 13
ten!
global a:10
"""
  1. 闭包在爬虫以及web应用中都有很广泛的应用
  2. 闭包也是装饰器的基础

闭包实现不修改源码实现添加功能_装饰器的基础

用闭包实现不修改源码添加功能

# coding=utf-8
# 本次内容,是装饰器的基础
​
def outfunc(func):def infunc(*args,**kwargs):print("日志纪录 start...")func(*args,**kwargs)print("日志纪录 end...")return infunc
​
def fun1():print("使用功能1")
​
def fun2(a,b,c):print("使用功能2",a,b,c)
​
print(id(fun1))
fun1 = outfunc(fun1)
print(id(fun1))
fun1()
fun2 = outfunc(fun2)
fun2(100,200,300)

map函数的使用

map()函数接收两种参数,一是函数,一种是序列(可以传入多个序列),map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回。

比如我们有一个函数f(x)=x2,要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()实现如下:

当然,不需要map()函数,也可以计算出结果,写一个循环,实现代码如下:

def f(x):return x * x
L = []
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]:L.append(f(n))
print(L)

【示例】map高阶函数的使用案例

def f(x):return x * x
​
L=map(f,[1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(L))

【示例】map高阶函数的使用案例(用匿名函数)

L=map(lambda n:n*n,[1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(L))

【示例】map函数传入两个列表

def f2(x,y):return x+y
L=map(f2,[1,2,3,4],[10,20,30])
print(list(L))

【示例】map函数传入两个列表(用匿名函数)

L=map(lambda x,y:x+y,[1,2,3,4],[10,20,30])
print(list(L))

reduce函数的使用

reduce位于functools模块

reduce把一个函数作用在一个序列[x1, x2, x3...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

【示例】reduce实现对一个序列求和

from functools import reduce
def add(x, y):return x + y
sum=reduce(add, [1, 3, 5, 7, 9])
print(sum)

filter函数的使用

内置函数filter()用于过滤序列。filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False, 决定保留还是丢弃该元素。

【示例】filter过滤列表,删掉偶数,只保留奇数

# 在一个list中,删掉偶数,只保留奇数
def is_odd(n):return n % 2 == 1
L=filter(is_odd, [1, 2, 4, 5])
print(list(L))

或者用匿名函数实现:

L=filter(lambda n:n%2==1, [1, 2, 4, 5])
print(list(L))

【示例】filter序列中的空字符串删掉

def not_empty(s):return s and s.strip()
​
L=filter(not_empty, ['A', '', 'B', None, 'C', '  '])
print(list(L))

或者用匿名函数实现:

L=filter(lambda s:(s and s.strip()), ['A', '', 'B', None, 'C', '  '])

sorted排序和自定义对象的排序

sorted函数

排序算法,排序也是在程序中经常用到的算法。无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小。

  1. 如果是数字,我们可以直接比较
  2. 如果是自定义对象呢?直接比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽象出来。通常规定,对于两个元素x和y,如果认为x < y,则返回-1,如果认为x == y,则返回0,如果认为x > y,则返回1,这样,排序算法就不用关心具体的比较过程,而是根据比较结果直接排序。

【示例】sorted对list进行排序

sorter1 = sorted([1,3,6,-20,34])
print("升序排列:",sorter1)

sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序

【示例】sorted函数接收一个key自定义排序

sorter1 = sorted([1,3,6,-20,34])
print("升序排列:",sorter1)
​
# sorted()函数也是高阶函数,它还可以接收一个key函数来实现自定义的排序
sorter2 = sorted([1,3,6,-20,-70],key=abs)
print("自定义排序:",sorter2)
​
sorter2 = sorted([1,3,6,-20,-70],key=abs,reverse=True)
print("自定义反向排序:",sorter2)
# 4.2 字符串排序依照ASCII
sorter3 = sorted(["ABC","abc","D","d"])
print("字符串排序:",sorter3)
# 4.3 忽略大小写排序
sorter4 = sorted(["ABC","abc","D","d"],key=str.lower)
print("忽略字符串大小写排序:",sorter4)
# 4.4 要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True:
sorter5 = sorted(["ABC","abc","D","d"],key=str.lower,reverse=True)
print("忽略字符串大小写反向排序:",sorter5)

【示例】sorted对自定义对象的排序

from functools import cmp_to_key
​
class Student:def __init__(self, age, name):self.name = nameself.age = age
​
def custom_sorted(stu1,stu2):if stu1.age < stu2.age:return -1if stu1.age > stu2.age:return 1return 0
​
stu1 = Student(41, 'aaa')
stu2 = Student(21, 'ccc')
stu3 = Student(31, 'bbb')
# student_list = sorted([stu1, stu2, stu3], key=lambda x: x.age)
student_list = sorted([stu1, stu2, stu3], key=cmp_to_key(custom_sorted))
for stu in student_list:print('name:', stu.name, 'age:', stu.age)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/593805.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【洛谷 P8655】[蓝桥杯 2017 国 B] 发现环 题解(邻接表+并查集+路径压缩)

[蓝桥杯 2017 国 B] 发现环 题目描述 小明的实验室有 N N N 台电脑&#xff0c;编号 1 ∼ N 1 \sim N 1∼N。原本这 N N N 台电脑之间有 N − 1 N-1 N−1 条数据链接相连&#xff0c;恰好构成一个树形网络。在树形网络上&#xff0c;任意两台电脑之间有唯一的路径相连。 …

拓扑排序--有向无环图中一个节点的所有祖先

题目描述 给你一个正整数 n &#xff0c;它表示一个 有向无环图 中节点的数目&#xff0c;节点编号为 0 到 n - 1 &#xff08;包括两者&#xff09;。 给你一个二维整数数组 edges &#xff0c;其中 edges[i] [fromi, toi] 表示图中一条从 fromi 到 toi 的单向边。 请你返…

OAuth 2.0 的四种方式

RFC 6749 OAuth 2.0 的标准是 RFC 6749 文件。该文件先解释了 OAuth 是什么。 OAuth 引入了一个授权层&#xff0c;用来分离两种不同的角色&#xff1a;客户端和资源所有者。…资源所有者同意以后&#xff0c;资源服务器可以向客户端颁发令牌。客户端通过令牌&#xff0c;去请…

K8S - Deployment 的版本回滚

当前状态 先看deployment rootk8s-master:~# kubectl get deploy -o wide --show-labels NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES …

Xen Server 8 Install

Xen Sevrer 前言 XenServer&#xff08;以前称为 Citrix Hypervisor&#xff09;是业界领先的平台&#xff0c;实现了经济高效的桌面、服务器和云虚拟化基础结构。XenServer 支持任意规模或类型的组织整合计算资源&#xff0c;以及将计算资源转换为虚拟工作负载&#xff0c;从…

代码随想录|Day32|动态规划01|509.斐波那契数列、70.爬楼梯、746.使用最小花费爬楼梯

509.斐波那契数列 动规五步曲&#xff1a; 确定 dp[i] 含义&#xff1a;第 i 个斐波那契数值为 dp[i]递推公式&#xff1a;dp[i] dp[i - 1] dp[i - 2]dp数组初始化&#xff1a;dp[0] dp[1] 1遍历顺序&#xff1a;从前向后打印dp数组 class Solution:def fib(self, n: int) …

【拓扑空间】示例及详解2

设A是X的一个子集&#xff0c; 内点、邻域&#xff1a;&#xff0c;则x是A的一个内点&#xff0c;A是x的一个领域 内部&#xff1a;A的所有的内点的集合称为A的内部&#xff0c;记 例1 证明&#xff1a; Proof: 例2 证明&#xff1a;是包含在A中的所有开集的并集&#xff0c…

Waifu2x:使用深度卷积神经网络的动漫风格艺术的图像超分辨率

Github网址&#xff1a;nagadomi/waifu2x&#xff1a;动漫风格艺术的图像超分辨率 (github.com) 该项目主要讲述的是如何利用预训练的深度学习模型来达到无损扩大收缩和去噪&#xff0c;对于一般训练图像的小伙伴应该很清晰图像经常要通过resize操作固定大小&#xff0c;然后c…

spring事务那些事

实际工作中还会面临千奇百怪的问题&#xff0c;看下面返个例子&#xff08;注意MySql数据库测试&#xff09;&#xff1a; //1.hello1Service 调用 hello2Service Transactional(propagation Propagation.REQUIRED,rollbackFor Exception.class) public void doUpdate() {//…

【代码随想录】链表

203. 移除链表元素 // public class ListNode { // int val; // ListNode next; // ListNode() {} // ListNode(int val) { this.val val; } // ListNode(int val, ListNode next) { this.val val; this.next next;} // }class Solution {public ListNo…

Linux 安装系统可视化监控工具 Netdata

目录 About 监控工具 NetdataLinux 系统安装 Netdata关于 openEuler1、查看内核信息2、查看主机信息3、查看 dnf 包管理器的版本 Netdata 安装1、更新系统环境相关 rpm 包2、查看 netdata 包信息3、安装 netdata 包4、编辑 netdata.conf 配置5、启动 netdata 服务6、查看 netda…

TCP的十个重要的机制

注&#xff1a;TCP不是只有十个机制 TCP 可靠传输是tcp最为重要的核心&#xff08;初心&#xff09; 可靠传输&#xff0c;并不是发送方把数据能够100%的传输给接收方 而是退而求其次 让发送方发送出去数据之后&#xff0c;能够知道接收方是否收到数据。 一但发现对方没有…