当前位置: 首页 > 后端技术 > Python

10分钟让你彻底学懂Python函数

时间:2023-03-26 15:01:15 Python

函数式编程到底是什么?本文将详细解释其概念,并分享如何在Python中使用函数式编程。主题包括列表理解和其他形式的理解。功能模型在命令式模型中,程序是通过向计算机提供一系列要执行的指令来执行的。计算机在执行过程中改变状态。比如A的初始值是5,后面要改变A的值。那么A就是一个变量,变量就是包含的值会发生变化。在功能模式中,您不是告诉计算机该做什么,而是告诉计算机该做什么。比如数的最大公约数是多少,1与n的乘积是多少等等。因此,不能更改变量。变量一旦设置,就永远保持相同的值(请注意,在纯函数式语言中,它们不称为变量)。因此,在函数模型中,函数没有副作用。副作用是函数对函数外部世界所做的更改。看看下面的Python代码示例:a=3defsome_func():globalaa=5some_func()print(a)代码的输出是5。在函数模型中,完全不允许更改变量的值,并且函数也不会影响函数之外的世界。函数唯一能做的就是做一些计算并返回一个值。您可能会想:“没有变量也没有副作用?这有什么好处呢?”好问题。如果用相同的参数调用一个函数两次,那么我们可以保证它会返回相同的结果。如果您研究过数学函数,您就会知道这样做是个好主意。这称为引用透明性。由于函数没有副作用,因此我们可以加速计算某些东西的程序。例如,如果程序知道func(2)返回3,它可以将这个值存储在一个表中,这样它就不需要重复运行我们已经知道结果的函数。通常,函数式编程不使用循环,而是使用递归。递归是一个数学概念,通常表示“将结果作为自己的输入”。使用递归函数,函数可以重复调用自身。下面是一个用Python定义的递归函数的例子:deffactorial_recursive(n):#Basecase:1!=1ifn==1:return1#递归情况:n!=n*(n-1)!else:returnn*factorial_recursive(n-1)函数式编程语言也是惰性的。懒惰意味着他们不到最后一刻才进行计算或做任何事情。如果代码要求计算2+2,那么函数程序只会在实际使用计算结果时才进行计算。稍后我们将介绍Python中的这种惰性。映射要理解映射(map),首先需要理解什么是可迭代对象。可迭代对象是任何可以迭代的对象。通常是列表或数组,但Python还有许多其他可迭代对象。甚至可以通过实施特定的魔术方法将对象定制为可迭代对象。魔术方法就像使对象更符合Pythonic的API。要使对象成为可迭代对象,您需要实现以下两个魔术方法:self.high=高def__iter__(self):#使此对象的第一个魔术方法返回自defselfdef__next__(self):#第二魔法方法,如果self.current>self.self.high。1returnself.current-1第一个魔术方法“iter”(双下划线iter)返回迭代器,通常在循环开始时调用。__next__返回迭代的下一个对象。您可以打开命令行并尝试以下代码:forcinCounter(3,8):print(c)这段代码将输出:345678在Python中,迭代器是只实现了__iter__魔术方法的对象。即可以访问对象包含的位置,但不能遍历整个对象。有些对象实现了__next__魔术方法但没有实现__iter__魔术方法,例如集合(在本文后面讨论)。在本文中,我们假设涉及的所有对象都是可迭代对象。现在我们知道什么是可迭代对象,让我们回到映射函数。映射可以对可迭代对象中的每个元素执行指定的功能。通常,我们对列表中的每个元素执行一个函数,但请注意映射实际上可用于大多数可迭代对象。map(function,iterable)假设有一个由以下数字组成的列表:[1,2,3,4,5]我们要得到每个数字的平方,那么代码可以这样写:x=[1,2,3,4,5]defsquare(num):returnnum*numprint(list(map(square,x)))Python中的函数式函数是惰性的。如果我们不添加“list()”,那么该函数只会保存可迭代对象,而不是结果列表。我们需要明确地告诉Python“将其转换为列表”以获取结果。最近整理了一套编程学习资料分享给大家。都是干货内容,包括教程视频、电子书、源码笔记、学习路线图、实战项目、面试题等,关注gzh【Python编程学习圈】免费获取,回复关键词【学习】即可材料],快点!在Python中一次从非惰性函数求值到惰性函数求值似乎有点不舒服。但如果你能从功能上而不是程序上思考,你最终会适应的。这个“square(num)”确实不错,但总感觉哪里不对。我们是否必须为只使用一次的地图定义一个完整的函数?实际上我们可以使用lambda函数(匿名函数)。Lambda表达式Lambda表达式是只有一行的函数。例如,下面的lambda表达式可以找到给定数字的平方:square=lambdax:x*x运行以下代码:>>>square(3)9你一定会问:“参数去哪儿了?什么“这是什么意思?看起来一点都不像函数?”嗯,不太好理解……不过应该是可以理解的。我们上面的代码为变量“square”分配了一些东西。就是这个东西:lambdax:它告诉Python这是一个lambda函数,输入名称是x。冒号后面的都是对输入的操作,然后自动返回操作的结果。这样,我们的平方代码就可以简化为一行:x=[1,2,3,4,5]print(list(map(lambdanum:num*num,x)))用lambda表达式,所有参数都在左边,操作在右边。虽然它看起来有点乱,但不可否认它是有效的。能够编写出只有懂函数式编程的人才能看懂的代码,其实还是有点小激动的。将函数合并成一行也很酷。归纳一个reduce是一个函数,它把一个可迭代对象变成一个事物。通常,我们对列表进行计算,将列表简化为一个数字。简化后的代码如下所示:reduce(function,list)上面的函数可以使用lambda表达式。列表的乘积只是将所有数字相乘。你可以这样写代码:product=1x=[1,2,3,4]fornuminx:product=product*num但是使用归纳法,你可以这样写:fromfunctoolsimportreduceproduct=reduce((lambdax,y:x*y),[1,2,3,4])得到相同的结果。这段代码更短,而且得益于函数式编程,这段代码更加简洁。Filter过滤器函数接受一个可迭代对象并过滤掉对象中不需要的所有内容。通常过滤器接受一个函数和一个列表。它为列表中的每个元素执行该函数,如果函数返回True,则不执行任何操作。如果函数返回False,则该元素将从列表中删除。语法如下:filter(function,list)让我们看一个简单的例子。如果没有过滤,代码应该这样写:x=range(-5,5)new_list=[]fornuminx:ifnum<0:new_list.append(num)使用过滤可以这样写:x=range(-5,5)all_less_than_zero=list(filter(lambdanum:num<0,x))高阶函数高阶函数接收函数作为参数并返回另一个函数。一个非常简单的示例如下所示:defsummation(nums):returnsum(nums)defaction(func,numbers):returnfunc(numbers)print(action(summation,[1,2,3]))#Outputis6或更简单的“返回函数”示例:defrtnBrandon():return"brandon"defrtnJohn():return"john"defrtnPerson():age=int(input("What'syourage?"))ifage==21:returnrtnBrandon()else:returnrtnJohn()还记得函数式编程语言没有变量吗?事实上,高阶函数可以很容易地做到这一点。如果你只是需要围绕一系列函数传递数据,那么数据根本不需要存储在变量中。Python中的所有函数都是顶级对象。顶级对象是具有以下一个或多个特征的对象:在运行时生成分配给数据结构中的变量或元素作为参数传递给函数作为函数的结果返回因此,所有函数在Python是对象,都可以作为高阶函数使用。偏函数偏函数有点难懂,但是很酷。有了它,你不需要提供完整的参数来调用函数。让我们看一个例子。我们要创建一个函数,它有两个参数,一个是底,一个是指数,然后返回底的指数次方,代码如下:defpower(base,exponent):returnbase**exponent现在我们需要一个解决方案square函数可以这样写:defsquare(base):returnpower(base,2)这段代码没问题,但是如果你需要一个cubic函数呢?或者四次函数呢?我必须一直定义新功能吗?这样做是可以的,但是程序员总是偷懒的。如果你需要经常重复一件事情,那就意味着必须有办法提高速度,避免重复。我们可以使用部分函数来实现这一点。下面是一个使用偏函数求平方的例子:fromfunctoolsimportpartialsquare=partial(power,exponent=2)print(square(2))#outputis4这个是苦的吗?我们提前将第二个参数告诉Python,这样我们就可以通过只提供一个参数来调用需要两个参数的函数。您还可以使用循环生成所有函数的1000次方。fromfunctoolsimportpartialpowers=[]forxinrange(2,1001):powers.append(partial(power,exponent=x))print(powers[0](3))#outputis9函数式编程还不够您可能已经注意到,我们在这里在函数式编程中大量使用列表。除了归纳函数和偏函数外,所有其他函数都生成列表。Guido(Python的发明者)不喜欢在Python中使用函数式的东西,因为Python有自己的生成列表的方式。在PythonIDLE中敲“importthis”,可以看到下面的内容:>>>importthisThePythonZen,byTimPeters美丽胜于丑陋。明确胜于隐含。简单胜于复杂。复杂胜于复杂。扁平比嵌套好。稀疏比密集好。可读性很重要。特殊情况不足以打破规则。尽管实用性胜过纯粹性。错误永远不应悄无声息地过去。除非明确沉默。面对歧义,拒绝猜测的诱惑。应该有一种——最好只有一种——显而易见的方法来做到这一点。尽管这种方法一开始可能并不明显,除非你是荷兰人。现在总比没有好。虽然从来没有经常比*现在就*。如果实现很难解释,那是个坏主意。如果实现容易解释,那可能是个好主意。命名空间是一个很棒的主意——让我们做更多的事情!这就是Python之禅。这首诗表明了什么叫做Python风格。我们要指出的是这句话:应该有一个——并且最好只有一个——obviouswaytodoit。(任何事情都应该只有一种方式来解决。)在Python中,映射和过滤能做的,列表推导(后述)也能做。这就打破了Python之禅,所以我们说函数式编程部分还不够“Pythonic”。另一个经常提到的地方是lambda。在Python中,lambda函数只是一个普通函数。lambda只是语法糖。两者是等价的:foo=lambdaa:2deffoo(a):return2普通函数可以做lambda可以做的所有事情,但反之则不行。Lambda不能做普通函数可以做的所有事情。讨论了为什么函数式编程不适合Python生态系统。大家可能注意到我前面提到了列表推导,那么现在就来介绍一下什么是列表推导。列表理解我之前说过,任何可以用映射或过滤器完成的事情都可以用列表理解来完成。这就是我们要学习的。列表推导式是Python生成列表的方式。语法如下:[functionforiteminiterable]要计算列表中每个数字的平方,可以这样写:print([x*xforxin[1,2,3,4]])正如你可以看到,我们对列表中的每个元素应用了一个函数。那么如何实现过滤呢?我们看一下前面的代码:x=range(-5,5)all_less_than_zero=list(filter(lambdanum:num<0,x))print(all_less_than_zero)可以转换成下面的listcomprehension的方式公式:x=range(-5,5)all_less_than_zero=[numfornuminxifnum<0]这样,列表推导公式支持if语句。这样就不用写一堆函数来实现了。事实上,如果您需要生成某种列表,列表推导式很可能更方便和简洁。如果你想对所有小于0的数进行平方怎么办?使用Lambda,map和filter可以写成:x=range(-5,5)all_less_than_zero=list(map(lambdanum:num*num,list(filter(lambdanum:num<0,x))))似乎就像它很长而且有点复杂。对于列表推导式,只需编写:x=range(-5,5)all_less_than_zero=[num*numfornuminxifnum<0]但列表推导式只能用于列表。映射和过滤可以应用于任何可迭代对象。那么为什么要使用列表理解呢?事实上,解析表达式可以用在任何可迭代对象上。其他解析器可以在任何可迭代对象上使用解析器。任何可迭代对象都可以分析生成。从Python2.7开始,甚至可以分析地生成字典(哈希表)。#摘自LucianoRamalho的FluentPython第70页第3章DIAL_CODES=[(86,'China'),(91,'India'),(1,'UnitedStates'),(62,'Indonesia'),(55,'巴西'),(92,'巴基斯坦'),(880,'孟加拉国'),(234,'尼日利亚'),(7,'俄罗斯'),(81,'日本'),]>>>country_code={country:codeforcode,countryinDIAL_CODES}>>>country_code{'巴西':55,'印度尼西亚':62,'巴基斯坦':92,'俄罗斯':7,'中国':86,'美国States':1,'Japan':81,'India':91,'Nigeria':234,'Bangladesh':880}>>>{code:country.upper()forcountry,codeincountry_code.items()ifcode<66}{1:'UNITEDSTATES',7:'RUSSIA',62:'INDONESIA',55:'BRAZIL'}只要是可迭代对象,都可以解析生成。让我们看一个集合的例子,简单来说:集合是一个元素列表,但是列表中没有重复的元素元素的顺序并不重要#取自《流利的Python》第3章第87页LucianoRamalho>>>fromunicodedataimportname>>>{chr(i)foriinrange(32,256)if'SIGN'inname(chr(i),'')}{'×','¥','°','£','?','#','?','%','μ','>','¤','±','?','§','<','=','?','$','÷','¢','+'}可以看到集合使用了和字典一样的大括号。Python非常聪明。它看你是否在花括号中提供额外的值来判断它是集合推导还是字典推导。结论函数式编程美丽而纯粹。函数式代码可以写得很干净,但也可以写得很乱。一些Python程序员不喜欢在Python中使用函数模型,但每个人都可以按照自己的喜好,记住使用最适合工作的工具。