函数式编程:一等对象、作用域和高阶函数的综合指南

函数介绍

  • 函数式编程
    • 一等对象的特点
    • 作用域(scope)
      • 全局作用域
      • 函数作用域
    • 命名空间(namespace)
    • 练习实操
      • 求阶乘
      • 递归函数
      • 幂运算函数
      • 测试代码
    • 高阶函数
      • 接收函数作为参数,或者将函数作为返回值的函数是高阶函数
      • 将函数作为返回值返回,也是一种高阶函数
      • 求多个数的平均值
      • filter()
      • map()
    • sort()
      • sorted()
      • reduce()
    • 练习
    • 装饰器
      • 创建几个函数
    • 总结

函数式编程

在Python中,函数是一等对象。这意味着函数可以像其他对象一样被操作和使用。在函数式编程中,我们可以将函数作为参数传递给其他函数,将函数赋值给变量,甚至将函数作为返回值返回。

一等对象的特点

一等对象一般都会具有以下特点:

  1. 对象是在运行时创建的。
  2. 能够赋值给变量或作为数据结构中的元素。
  3. 能够作为参数传递。
  4. 能够作为返回值返回。

作用域(scope)

作用域指的是变量生效的区域。

b = 20 # 全局变量def fn():a = 10 # a定义在了函数内部,所以他的作用域就是函数内部,函数外部无法访问print('函数内部:','a =',a)print('函数内部:','b =',b)# fn()    # print('函数外部:','a =',a)
# print('函数外部:','b =',b)

在Python中一共有两种作用域:

全局作用域

  • 全局作用域在程序执行时创建,在程序执行结束时销毁。
  • 所有函数以外的区域都是全局作用域。
  • 在全局作用域中定义的变量,都属于全局变量,全局变量可以在程序的任意位置被访问。

函数作用域

  • 函数作用域在函数调用时创建,在调用结束时销毁。
  • 函数每调用一次就会产生一个新的函数作用域。
  • 在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问。

变量的查找:

  • 当我们使用变量时,会优先在当前作用域中寻找该变量,如果有则使用,如果没有则继续去上一级作用域中寻找,如果有则使用,如果依然没有则继续去上一级作用域中寻找,以此类推,直到找到全局作用域,依然没有找到,则会抛出异常 NameError: name 'a' is not defined
def fn2():def fn3():print('fn3中:','a =',a)fn3()# fn2()    a = 20def fn3():# a = 10 # 在函数中为变量赋值时,默认都是为局部变量赋值# 如果希望在函数内部修改全局变量,则需要使用global关键字,来声明变量global a # 声明在函数内部的使用a是全局变量,此时再去修改a时,就是在修改全局的aa = 10 # 修改全局变量print('函数内部:','a =',a)# fn3()
# print('函数外部:','a =',a)

命名空间(namespace)

命名空间指的是变量存储的位置,每一个变量都需要存储到指定的命名空间当中。每一个作用域都会有一个它对应的命名空间。全局命名空间,用来保存全局变量。函数命名空间用来保存函数中的变量。命名空间实际上就是一个字典,是一个专门用来存储变量的字典。

# locals()用来获取当前作用域的命名空间
# 如果在全局作用域中调用locals()则获取全局命名空间,如果在函数作用域中调用locals()则获取函数命名空间
# 返回的是一个字典
scope = locals() # 当前命名空间
print(type(scope))
# print(a)
# print(scope['a'])
# 向scope中添加一个key-value
scope['c'] = 1000 # 向字典中添加key-value就相当于在全局中创建了一个变量(一般不建议这么做)
# print(c)def fn4():a = 10# scope = locals() # 在函数内部调用locals()会获取到函数的命名空间# scope['b'] = 20 # 可以通过scope来操作函数的命名空间,但是也是不建议这么做# globals() 函数可以用来在任意位置获取全局命名空间global_scope = globals()# print(global_scope['a'])global_scope['a'] = 30# print(scope)fn4()

练习实操

  • 求阶乘
  • 递归函数
  • 幂运算函数
  • 测试代码

求阶乘

# 创建一个函数,可以用来求任意数的阶乘
def factorial(n):'''该函数用来求任意数的阶乘参数:n 要求阶乘的数字'''# 创建一个变量,来保存结果result = nfor i in range(1,n):result *= ireturn result    

递归函数

# 创建一个函数,用来检查一个任意的字符串是否是回文字符串,如果是返回True,否则返回False
def hui_wen(s):'''该函数用来检查指定的字符串是否回文字符串,如果是返回True,否则返回False参数:s:就是要检查的字符串'''# 基线条件if len(s) < 2 :# 字符串的长度小于2,则字符串一定是回文return Trueelif s[0] != s[-1]:# 第一个字符和最后一个字符不相等,不是回文字符串return False    # 递归条件    return hui_wen(s[1:-1])

幂运算函数

# 创建一个函数 power 来为任意数字做幂运算 n ** i
def power(n , i):'''power()用来为任意的数字做幂运算参数:n 要做幂运算的数字i 做幂运算的次数'''# 基线条件if i == 1:# 求1次幂return n# 递归条件return n * power(n , i-1)

测试代码

# 求10的阶乘    
print(factorial(10))# 检查字符串是否回文
print(hui_wen('abcdefgfedcba'))# 对10进行5次幂运算
print(power(10, 5))

高阶函数

高阶函数是指满足以下条件之一的函数:

  1. 接收一个或多个函数作为参数。
  2. 将函数作为返回值返回。

高阶函数在函数式编程中非常常见。它们使得我们能够更灵活地处理函数,将函数作为数据进行操作和传递。

接收函数作为参数,或者将函数作为返回值的函数是高阶函数

当我们使用一个函数作为参数时,实际上是将指定的代码传递进了目标函数

# 创建一个列表
l = [1,2,3,4,5,6,7,8,9,10]# 定义一个函数
#   可以将指定列表中的所有的偶数,保存到一个新的列表中返回# 定义一个函数,用来检查一个任意的数字是否是偶数
def fn2(i) :if i % 2 == 0 :return Truereturn False    # 这个函数用来检查指定的数字是否大于5
def fn3(i):if i > 5 :return True    return Falsedef fn(func , lst) :'''fn()函数可以将指定列表中的所有偶数获取出来,并保存到一个新列表中返回参数:lst:要进行筛选的列表'''# 创建一个新列表new_list = []# 对列表进行筛选for n in lst :# 判断n的奇偶if func(n) :new_list.append(n)# 返回新列表return new_list

将函数作为返回值返回,也是一种高阶函数

在Python中,我们可以将函数作为返回值返回,这种函数被称为高阶函数。通过高阶函数,我们可以创建一些只有当前函数能访问的变量,这种函数称为闭包。

def fn():a = 10# 函数内部再定义一个函数def inner():print('我是fn2', a)# 将内部函数inner作为返回值返回   return inner# r是一个函数,是调用fn()后返回的函数
# 这个函数实在fn()内部定义,并不是全局函数
# 所以这个函数总是能访问到fn()函数内的变量
r = fn()

在上面的代码中,我们定义了一个函数fn(),它内部定义了另一个函数inner(),然后将inner()函数作为返回值返回。当我们调用fn()函数后,会得到一个函数对象r,我们可以通过r来调用inner()函数,并且inner()函数可以访问到fn()函数内部的变量a

求多个数的平均值

def make_averager():nums = []def averager(n):nums.append(n)return sum(nums)/len(nums)return averageraverager = make_averager()print(averager(10))
print(averager(20))
print(averager(30))
print(averager(40))

在上面的代码中,我们定义了一个函数make_averager(),它返回了一个内部函数averager()。通过调用make_averager()函数,我们可以得到一个计算平均值的闭包averager。每次调用averager()函数时,我们将一个数值添加到列表nums中,并返回当前所有数值的平均值。

我们可以多次调用averager()函数来计算不同数值序列的平均值,由于闭包的特性,它会记住之前的所有数值,从而得到正确的平均值。

filter()

filter()可以从序列中过滤出符合条件的元素,保存到一个新的序列中
参数:

  1. 函数,根据该函数来过滤序列(可迭代的结构)
  2. 需要过滤的序列(可迭代的结构)
    返回值:
    过滤后的新序列(可迭代的结构)
def fn4(i):return i % 3 == 0r = filter(lambda i : i > 5 , l)

map()

map()函数可以对可迭代对象中的所有元素做指定的操作,然后将其添加到一个新的对象中返回

l = [1,2,3,4,5,6,7,8,9,10]
r = map(lambda i : i ** 2 , l)

sort()

该方法用来对列表中的元素进行排序
sort()方法默认是直接比较列表中的元素的大小
在sort()可以接收一个关键字参数 , key
key需要一个函数作为参数,当设置了函数作为参数
每次都会以列表中的一个元素作为参数来调用函数,并且使用函数的返回值来比较元素的大小

l = ['bb','aaaa','c','ddddddddd','fff']
# l.sort(key=len)l = [2,5,'1',3,'6','4']
l.sort(key=int)

sorted()

这个函数和sort()的用法基本一致,但是sorted()可以对任意的序列进行排序
并且使用sorted()排序不会影响原来的对象,而是返回一个新对象

l = [2,5,'1',3,'6','4']
# l = "123765816742634781"print('排序前:',l)
print(sorted(l,key=int))
print('排序后:',l)

reduce()

这个方法和map()、filter()不太一样
reduce()需要导入functools模块

from functools import reducel = [1,2,3,4,5]
r = reduce(lambda x,y : x + y , l)

练习

使用filter函数找到列表中所有的偶数,使用map函数将每个偶数变成字符串类型,并使用sorted函数进行排序。

l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]even_nums = filter(lambda n: n % 2 == 0, l)
str_nums = map(str, even_nums)
sorted_nums = sorted(str_nums)
print(sorted_nums)

输出:

['2', '4', '6', '8', '10']

装饰器

装饰器是一种特殊类型的函数,它可以用于修改或扩展其他函数的功能。装饰器通常使用Python的@语法来应用于目标函数。装饰器可以在不修改原函数代码的情况下,通过包裹原函数来添加额外的行为。

以下是一个示例装饰器的代码:

def decorator(func):def wrapper(*args, **kwargs):print("装饰器添加的额外功能")return func(*args, **kwargs)return wrapper@decorator
def target_function():print("目标函数")target_function()

输出:

装饰器添加的额外功能
目标函数

在上面的代码中,decorator是一个装饰器函数,它接收一个函数作为参数,并返回一个新的包装函数wrapper。包装函数在调用目标函数之前添加了额外的功能。通过将装饰器应用于target_function,我们可以在调用target_function时获得额外的功能。

创建几个函数

def add(a , b):'''求任意两个数的和'''r = a + breturn rdef mul(a , b):'''求任意两个数的积'''r = a * breturn r    

希望函数可以在计算前,打印开始计算,计算结束后打印计算完毕。我们可以直接通过修改函数中的代码来完成这个需求,但是会产生以下一些问题:
① 如果要修改的函数过多,修改起来会比较麻烦;
② 并且不方便后期的维护;
③ 并且这样做会违反开闭原则(OCP),即程序的设计要求开发对程序的扩展,要关闭对程序的修改。

r = add(123,456)
print(r)

我们希望在不修改原函数的情况下,来对函数进行扩展。

def fn():print('我是fn函数....')# 只需要根据现有的函数,来创建一个新的函数
def fn2():print('函数开始执行~~~')fn()print('函数执行结束~~~')fn2() 
def new_add(a,b):print('计算开始~~~')r = add(a,b)print('计算结束~~~')return rr = new_add(111,222)    
print(r)

上边的方式,已经可以在不修改源代码的情况下对函数进行扩展了。但是,这种方式要求我们每扩展一个函数就要手动创建一个新的函数,实在是太麻烦了。为了解决这个问题,我们创建一个函数,让这个函数可以自动的帮助我们生成函数。

def begin_end(old):'''用来对其他函数进行扩展,使其他函数可以在执行前打印开始执行,执行后打印执行结束参数:old 要扩展的函数对象'''# 创建一个新函数def new_function(*args , **kwargs):print('开始执行~~~~')# 调用被扩展的函数result = old(*args , **kwargs)print('执行结束~~~~')# 返回函数的执行结果return result# 返回新函数        return new_functionf = begin_end(fn)
f2 = begin_end(add)
f3 = begin_end(mul)r = f()
r = f2(123,456)
r = f3(123,456)
print(r)

begin_end()这种函数我们就称它为装饰器。通过装饰器,可以在不修改原来函数的情况下来对函数进行扩展。在开发中,我们都是通过装饰器来扩展函数的功能的。

在定义函数时,可以通过@装饰器,来使用指定的装饰器,来装饰当前的函数。可以同时为一个函数指定多个装饰器,这样函数将会按照从内向外的顺序被装饰。

def fn3(old):'''用来对其他函数进行扩展,使其他函数可以在执行前打印开始执行,执行后打印执行结束参数:old 要扩展的函数对象'''# 创建一个新函数def new_function(*args , **kwargs):print('fn3装饰~开始执行~~~~')# 调用被扩展的函数result = old(*args , **kwargs)print('fn3装饰~执行结束~~~~')# 返回函数的执行结果return result# 返回新函数        return new_function@fn3
@begin_end
def say_hello():print('大家好~~~')say_hello()

总结

函数式编程是一种编程范式,它将计算过程视为数学函数的组合。在函数式编程中,函数被视为一等对象,具有以下特点:

  1. 作为参数传递:函数可以作为参数传递给其他函数,以实现更灵活的功能。
  2. 作为返回值返回:函数可以作为另一个函数的返回值,以实现可定制的行为。
  3. 可以赋值给变量:函数可以被赋值给变量,以便进一步使用。

作用域是指在程序中定义变量的区域,它决定了变量的可见性和生命周期。常见的作用域包括全局作用域和函数作用域。

全局作用域是在整个程序中都可以访问的作用域,而函数作用域只在函数内部可见。

命名空间是一个用于存储变量和函数名称的容器。它提供了一种将名称与特定作用域中的对象关联起来的方式。

在实操练习中,使用函数式编程的概念来实现了以下功能:

  1. 求阶乘:通过递归函数实现了求阶乘的功能。
  2. 幂运算函数:通过高阶函数实现了对一个数进行幂运算的功能。
  3. 求多个数的平均值:通过高阶函数和reduce()函数实现了求多个数的平均值的功能。

装饰器是一种用于修改已有函数行为的函数。它可以在不修改原函数代码的情况下,给函数添加新的功能。在实操练习中,使用装饰器实现了对函数进行扩展的功能。

总结:函数式编程通过将函数视为一等对象,使得程序更具灵活性和可扩展性。它包括了一等对象、作用域、命名空间等概念,并可以通过高阶函数、递归函数、装饰器等实现各种功能。函数式编程是一种强大的编程范式,能够简化复杂问题的解决过程。

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

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

相关文章

Gin-swaggo为gin框架提供Swagger 文档

官方: https://github.com/swaggo/gin-swagger 开始使用 为API方法增加注释,加在controller(api)层, See Declarative Comments Format.运行下面命令下载swgo: go get -u github.com/swaggo/swag/cmd/swag Go 1.17后的版本, 使用 go get 安装可执行文件已被废弃. 用go ins…

华为云云耀云服务器L实例评测|带宽,磁盘,CPU,内存以及控制台监控测试

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;AWS/阿里云资深使用…

Python从零到一构建项目

随着互联网的发展&#xff0c;网络上的信息量急剧增长&#xff0c;而获取、整理和分析这些信息对于很多人来说是一项艰巨的任务。而Python作为一种功能强大的编程语言&#xff0c;它的爬虫能力使得我们能够自动化地从网页中获取数据&#xff0c;大大提高了效率。本文将分享如何…

LeetCode 449. Serialize and Deserialize BST【树,BFS,DFS,栈】困难

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

【技术分享】RK Android11系统SD卡启动方法

本文基于Purple Pi OH 3566主板&#xff0c;介绍Android11源码的修改&#xff0c;获得可从SD卡启动的Android11系统镜像。 Purple Pi OH作为一款兼容树莓派的开源主板&#xff0c;采用瑞芯微RK3566 (Cortex-A55) 四核64位超强CPU,主频最高达1.8 GHz,算力高达1Tops&#xff0c;…

R Removing package报错(as ‘lib’ is unspecified)

remove.packages(ggpubr) Removing package from ‘/Library/Frameworks/R.framework/Versions/4.0/Resources/library’ (as ‘lib’ is unspecified) 解决办法&#xff1a; > .libPaths() [1] "/Library/Frameworks/R.framework/Versions/4.0/Resources/library&qu…

海外商城小程序如何开发

随着全球化的发展和人们对跨境购物的需求逐渐增加&#xff0c;海外商城小程序成为了众多电商平台的重要组成部分。本文将深入探讨如何搭建海外商城小程序&#xff0c;从技术实现到用户体验设计&#xff0c;为开发者提供专业且有深度的思考&#xff0c;以帮助他们打造出色的跨境…

手写RPC框架--13.优雅停机

优雅停机 优雅停机a.优雅停机概述b.服务端实现优雅停机c.客户端实现优雅停机d.优雅启动 优雅停机 a.优雅停机概述 当我们快速关闭服务提供方时&#xff0c;注册中心感知、以及通过watcher机制通知调用方一定不能做到实时&#xff0c;一定会有延时&#xff0c;同时我们的心跳检…

回文链表判断

回文字符串和数组我们会经常遇到&#xff0c;今天讲一个相关问题&#xff0c;叫回文链表&#xff0c;还是和以前一样&#xff0c;先把代码提上来。 // need O(1) extra spacepublic static boolean isPalindrome3(Node head) {if (head null || head.next null) {return true…

如何把视频格式转换成mp4?支持的格式种类非常多。

如何把视频格式转换成mp4&#xff1f;随着计算机技术的迅猛发展&#xff0c;我们现在有着各种各样的视频格式可供选择&#xff0c;平时我们都知道的mp4、flv、mov、mkv、avi、wmv等&#xff0c;都是视频格式的种类。其中&#xff0c;MP4是一种具有极佳兼容性的视频格式&#xf…

TikTok魔法:揭秘那个“神奇”的算法

嘿&#xff0c;你是不是每次打开TikTok&#xff0c;都感觉这个应用好像了解你的内心世界一样&#xff1f;没错&#xff0c;背后有一个不为人知、神奇的算法正在起作用&#xff0c;让你欲罢不能。在这篇文章中&#xff0c;我们将揭开TikTok算法的神秘面纱&#xff0c;看看它是如…

车机多用户系统的适配问题

多用户问题出现背景 记录一下多用户的适配问题&#xff1a; 背景是system/app下面新push了两个apk&#xff0c;一个是我们的业务场景apk一个是虚拟车CarService服务的apk&#xff0c;我们的apk需要链接CarService服务通过AIDL通信。 下面这两张图是未roo的情况&#xff08;当…

Python之Xlwings操作excel

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、xlwings简介二、安装与使用1.安装2.使用3.xlwings结构说明 二、xlwings对App常见的操作App基础操作工作簿的基础操作工作表的基础操作工作表其他操作 读取单元格…

JDK常用诊断工具

工具描述javapJava 反编译工具,主要用于根据 Java 字节码文件反汇编为 Java 源代码文件jcmdJava 命令行(Java Command),用于向正在运行的 JVM 发送诊断命令请求jconsole图形化用户界面的监测工具,主要用于监测并显示运行于 Java 平台上的应用程序的性能和资源占用等信息jdeps用…

移动端调试工具vConsole

安利一款好用的移动端调试工具vConsole vConsole 是腾讯推出的一个轻量、可拓展、针对手机网页的前端开发者调试面板。 官网:https://alloyteam.github.io/AlloyLever/ 特性 查看 console 日志查看网络请求查看页面 element 结构查看 Cookies、localStorage 和 SessionStor…

MOV导出序列帧并在Unity中播放

MOV导出序列帧并在Unity中播放 前言项目将MOV变成序列帧使用TexturePacker打成一个图集将Json格式精灵表转换为tpsheet格式精灵表导入Unity并播放总结 鸣谢 前言 收集到一批还不错的MG动画&#xff0c;想要在Unity中当特效播放出来&#xff0c;那首先就得把MOV变成序列帧&…

堆排序与TopK问题

一、堆排序 堆排序(升序)&#xff1a;堆排序的思想就是先用数组模拟建大堆&#xff0c;然后把根结点与最后一个结点值交换&#xff0c;最后一个结点的值就是最大值&#xff0c;然后再把前(n-1)个元素重新建大堆&#xff0c;然后根结点与最后一个结点值交换&#xff0c;就找出了…

小红书笔记爬虫

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

LNMP架构搭建论坛

目录 一、LNMP简介&#xff1a; 二、LNMP搭建&#xff1a; 1.前提准备&#xff1a; 关闭防火墙和安全机制&#xff1a; 2.编译安装nginx&#xff1a; 3.编译安装mysql&#xff1a; 3.1 安装依赖环境&#xff1a; 3.2 创建mysql运行用户&#xff1a; 3.3 编译安装&#xff1a…

c语言练习题52:写一个函数判断当前机器是大端还是小端

代码&#xff1a; #include<stdio.h> int check_sys() {int a 1;return *(char*)&a;//小端retrun 1 大端return 0&#xff1b; } int main() {if (check_sys() 1) {printf("小端\n");}elseprintf("大端\n"); } 这里首先取a的地址&#xff0c…