ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Python函数和函数式编程(两万字长文警告!一文彻底搞定函数,建议收藏!)

Python函数和函数式编程(两万字长文警告!一文彻底搞定函数,建议收藏!) 在进行函数声明这个行为的时候, 同样能够借助带有双星标识的参数, 比如** , 以此种方式朝着函数去传递具备可变数量的实际参数。在调用函数时从那一点后所有的参数被收集为一个字典。带星或带双星的参数必须位于形参列表的最后位置。利用带星的参数计算各数字的累加和。def my_sum3(a, b, *c): #各数字累加和 total a b for n in c: total total n return total print(my_sum3(1, 2)) #计算12 print(my_sum3(1, 2, 3, 4, 5)) #计算12345 print(my_sum3(1, 2, 3, 4, 5, 6, 7)) #计算1234567利用带星和带双星的参数计算各数字的累加和。def my_sum4(a, b, *c, **d): #各数字累加和 total a b for n in c: #元组中各元素累加和 total total n for key in d: #字典中各元素累加和 total total d[key] return total print(my_sum4(1, 2)) #计算12 print(my_sum4(1, 2, 3, 4, 5)) #计算12345 print(my_sum4(1, 2, 3, 4, 5, male 6, female 7)) #计算12345673.8 强制命名参数后面声明参数要是在带星号的参数之后, 就会引起导致变成强制须命名参数的状况, 这种情况下在调用的时候, 必须要显式地采用该类型参数传递具体的值才行, 原因在于, 按照位置去传递的参数, 默认会被收集成为一个元组, 然后传递给前面带有星号的可变化参数那儿。要是没必要带上带星号的可变参数, 单单只想运用强制命名参数, 能够简要地采用一个星号, 比如。def my_ func(* ab, c)基于期中成绩和期末成绩按照指定的权重计算总评成绩。def my_sum(*, mid_score, end_score, mid_rate 0.4): #期中成绩、期末成绩、期中成绩权重 #基于期中成绩、期末成绩和权重计算总评成绩 score mid_score * mid_rate end_score * (1 - mid_rate) print(format(score, .2f)) #输出总评成绩保留2位小数 my_sum(mid_score 88, end_score 79) #期中88期末79期中权重为默认的40% my_sum(end_score 79, mid_score 88) #期末79期中88期中权重为默认的40% my_sum(88, 79) #报错必须显式使用命名参数传递值3.9参数类型检查一般性来说, 当对函数进行定义之时的时候, 不但得去明确其定义域, 还得去摆明其值域, 就比如说要确切地指明形式参数以及返回值所对应的详细类型。立足于语言的设计理念, 于定义函数之际, 无需对其参数以及返回值的类型加以限定。这般灵活性能够达成多态性, 也就是许可函数适用于各异类型的对象, 好比my (a,b)函数此函数既能够返回两个int对象求出来的平均值, 也能够返回由两个float 对象算出的平均值。在所调用的函数使用了不被支持的类型参数之时, 就会产生错误, 举例来说, (a,b)函数所传递的多数是什么呢, 是str对象, 而在运行这个函数的时候, 将会抛出错误。从原则方面来讲, 是能够增添代码检测这种类型的错误的, 然而程序设计遵循着一种惯例, 那便是, 在用户进行调用所数的时候必须做到理解, 并且要保证传入正确类型的参数值。4、函数的返回值 4.语句和函数返回值在函数的主体部分, 运用一些语句, 能够达成从这样的函数里面, 返回一个具体的值, 同时还具备跳脱出此函数 的那种能力。编写函数利用语句返回函数值求若干数中的最大值。求若干数中最大值的方法一般如下。(1)把最大值的起始值设定成一个相对较小的数, 亦或者选取第一个数作为最大值的起始值。(2)动用循环, 把每一个数跟最大值去作比较, 要是这个数大于最大值的话, 那就将这个数设定成最大值。def my_max(a, b, *c): #求若干数中的最大值 max_value a #假设第一个数为最大值 if max_value b: #如果最大值小于b则b为最大值 max_value b for n in c: #循环迭代c中每个元素n如果最大值小于n则n为最大值 if max_value n: max_value n return max_value #利用return语句返回最大值 #测试代码 print(my_max(1, 2)) #求(1, 2)中的最大值 print(my_max(1, 7, 11, 2, 5)) #求(1, 7, 11, 2, 5)中的最大值程序运行结果如下。4.2多条语句“语向”放置位置不限, 函数中任意处皆可, 程序执行至首个“retum语句”当即返回到调用程序处。对一个数是不是素数进行判断的示例叫作prime py, 首先要去编写一个能够判断一个数是不是素数的函数, 之后还要编写测试代码, 来判断并且输出从1到99中的那些素数。这里所说的素数也就是质数, 它指的是这样一种正整数, 即除开1以及这个数自身外, 无法被任何整数整除的数。要去判断一个正整数是不是素数, 就是要判断n能不能被2到n之间的任何一个整数整除。只要不能被这个范围里的任何一个整数整除, 那么n就是素数, 不然的话n就是合数。def is_prime(n): if n 2: return False #如果n小于2返回False i 2 while i*i n: #一旦n能够被2~i 中的任意整数整除n就不是素数返回False if n % i 0: return False i 1 return True #测试代码 for i in range(100): #判断并输出1~99中的素数以空格分隔 if is_prime(i):print(i, end )4.3 返回多个值运用语句于函数体里, 能够达成从那函数之际返回一个值, 还能跳出该函数。要是存在需要返回多个值的情况, 那么是可以返回一个元组的。构建一个函数, 使其返回一个随机生成的列表, 先行编写一个函数, 该函数能够生成一个由n个随机整数所组成的列表, 接着编写测试代码, 去生成以及输出另一个由5个随机整数构制而成的列表的各个元素的值。import random def randomarray(n): #生成由n个随机数构成的列表 a [] for i in range(n): a.append(random.random()) return a #测试代码 brandomarray(5) #生成由5个随机数构成的列表 for i in b: print(i) #输出列表中每个元素5、变量的作用域变量声明所处位置存在差异,其能够被访问的范畴也不一样。变量可被访问的范畴称作变量的作用域。变量依据其作用域大致能划分成全局变量,局部变量以及类成员变量。5.1全局变量于一个源代码文件里头, 于函数以及类定义之外所声明的变量被称作全局变量, 全局变量的作用域是其定义的那个模块, 从定义所处的位置开始, 一直到文件结束的位置。经过语句导入模块, 借助全限定名称“模块名.变量名”同样能够进行访问, 或是运用从语句导入模块里的变量继而实施访问。各不一样的模块全都均能够去访问全局变量, 这般情形将会致使全局变量呈现出不可预知性的特征。要是存在多个语句在同一时间一同修改一个全局变量的话, 则极有可能会使得程序引发错误状况, 并且是非常难以去发现以及更正的。全局变量致使丽数或者模块之间的通用性有所降低, 还使得代码的可读性有所降低, 在一般情形下, 应当尽可能避免运用全局变量。全局变量一般是当作常量在用。全局变量定义示例。TAX1 0.17 #税率常量17% TAX2 0.2 #税率常量20% TAX3 0.05 #税率常量5% PI 3.14 #圆周率3.14全局变量定义示例。TAX1 0.17 #税率常量17% TAX2 0.2 #税率常量20% TAX3 0.05 #税率常量5% PI 3.14 #圆周率3.14 def tax(x): #根据税率常量20%计算纳税值 return x * TAX2 #测试代码 a [1000, 1200, 1500, 2000] for i in a: #计算并打印4笔数据的纳税值 print(i, tax(i))5.2 局部变量在函数体当中声明的变量, 这里面还包括函数参数, 会被称作局部变量, 它的有效范围也就是作用域, 是在函数体那里。全局范围的代码, 是无法去引用某一个函数内部的局部变量, 或者形式参数变量的而一个函数, 同样也不能够去引用在另外一个函数当中所定义的局部变量, 或者形式参数变量。若是于一个函数之内所定义的局部变量或者形式参数变量跟全局变量出现重名的情况, 那么局部变量或者形式参数变量具有优先性, 也就是函数之中所定义的变量指的是局部变量或者形式参数变量, 并非全局变量。局部变量定义示例。num 100 #全局变量 def f(): num 105 #局部变量 print(num) #输出局部变量的值 #测试代码 f();print(num)5.3全 局语句在函数的主体部分当中, 能够去引用全局范围之内的变量的, 然而要是函数以内的变量的名称, 是首次被提及并且是在赋值语句的前面的情况之下变量进行赋值这个行为, 那么就会被解释成为是定义局部变量的。函数体错误引用全局变量的示例。m 100 n 200 def f(): print(m5) n 10 f()能够为于函数外头定义的全局变量去赋予值的话, 可采用语句, 以此来表明变量乃是于外部定义的全局变量, 语句能够指定好些全局变量, 像“x, y, z”这般, 通常来讲应当竭尽全力去规避如此运用全局变量, 全局变量会致使程序的可读性欠佳。全局语句pi 3.141592653589793 #全局变量 e 2.718281828459045 #全局变量 def my_func(): global pi #全局变量与前面的全局变量pi指向相同的对象 pi 3.14 #改变了全局变量的值 print(global pi , pi) #输出全局变量的值 e 2.718 #局部变量与前面的全局变量e指向不同的对象 print(local e , e) #输出局部变量的值 #测试代码 print(module pi , pi) #输出全局变量的值 print(module e , e) #输出全局变量的值 my_func() #调用函数 print(module pi , pi) #输出全局变量的值该值在函数中已被更改 print(module e , e) #输出全局变量的值5.4非局部语句能在函数体里定义嵌套函数在嵌套函数中要是要替定义于上级函数体的局部变量赋值能够运用语句用以表明那个变量并非是所在块的局部变量而是在上级函数体里定义的局部变量语句能够指明多个非局部变量比如说“, y, z”。非局部语句def outer_func(): tax_rate 0.17 #上级函数体中的局部变量 print(outer func tax rate , tax_rate) #输出上级函数体中局部变量的值 def innner_func(): nonlocal tax_rate #不是所在块的局部变量而是在上级函数体中定义的局部变量 tax_rate 0.05 #上级函数体中的局部变量重新赋值 print(inner func tax rate , tax_rate) #输出上级函数体中局部变量的值 innner_func() #调用函数 print(outer func tax rate , tax_rate) #输出上级函数体中局部变量的值已更改 #测试代码 outer_func()5.5 类成员变量类之中声明出来的变量被称作类成员变量, 这里面涵盖了静态变量以及实例变量, 而它的那具有效力的范围, 也就是所说的作用域, 是被限定在类定义的那个范围以内的。处于外部时, 要先创建类的对象实例, 接着借助“对象.实例变量”这种方式去访问类的实例变量, 又或者凭借“类.静态变量”这种途径访问类的静态变量。5.6 输出局部变量和全局变量在程序, 运行的进程当中, 于上下文里, 会产生各种各样的局部变量, 以及全局变量, 借助内置的函数, 还有, 能够查看之后, 并且输出局部变量, 以及全局变量的列表。局部变量和全局变量列表示例( . py)。a1 b2 def f(a, b): x abc y xyz for i in range(2): #i0~1 j i k i**2 print(locals()) f(1,2) print(globals())6、递归函数 6.1递归函 数的定义递归函数乃是自调用函数, 于函数体以内部直接或者间接的方式自己去调用自身, 此即函数的嵌套调用是函数自身, 并且它很常用以达成数值计算的办法。使 用递归函数实现阶乘def factorial(n): if n 1: return 1 return n * factorial(n - 1) #测试代码 for i in range(1,10): #输出1~9的阶乘 print(i,! , factorial(i))6.2递归函数的原理递归对建立数学模型给出了一种直接办法, 于数学方面它和数学归纳法相对应。每个递归函数必须包括以下两个主要部分。(1)终止条件, 是用来表明递归的结束情形的, 其作用在于返回函数值之后, 便不再进行递归调用了。好比说, 函数所具备的结束条件就是“n等同于1”。(2)递归步骤, 它所要做的是这样一件事情, 将第n步的参数值的函数, 和第n - 1步的参数值的函数, 二者进行关联。例如对于(),其递归步骤为“n * (n- 1)”。此外, 一组参数值得逐步趋向于结束条件, 比如说, 针对, 每一次进行递归调用的时候参数值n都会逐个递减1, 故而一组参数值慢慢趋向于结束条件n 1。例如调和数的计算公式如下。H。1十1/2…1/n故可以使用递归函数实现。(1)终止条件: H。 1当nl时(2)递归步骤: H。 H。-1 1/n当n1时每次递归,n严格递减故逐渐收敛于1。使 用递归函数实现调和数 。def harmonic(n): if n 1: return 1.0 #终止条件 return harmonic(n-1) 1.0/n #递归步骤 #测试代码 for i in range(1,10): #输出1~9阶的调和数 print(H, i, , harmonic(i))6.3编写递归函数时需要注意的问题尽管借由递归函数能够达成简洁、优雅的程序, 不过在撰写递归函数之际应当留意如下数个问题。(1)必须设置终止条件。系统会耗尽内存, 这是缺少终止条件的递归函数致使无限递归函数调用后得到的最终不利结果, 随之发生的状况是函数会抛出所具有的错误, 并且报告“depth (超过最大递归深度)”这条针对错误的信息。一般而言, 于递归函数里通常是要去设置终止条件。在sys模块当中, 函数()跟()是用来获取以及设置最大递归次数的。比如说:。import sys sys. getrecursionlimit()#获取最大递归次数:1000 sys. setrecursionl imit(2000)#设置最大递归次数为2000(2)必须保证收敛。递归进行调用时, 其所处理解决的子问题, 在规模方面必须要比原始问题的规模更小才行, 如果不是这样的话, 就会致使出现无限递归函数被调用的情况。(3)必须保证内存和运算消耗控制在定范围内。递归函数代码虽说看上去简单, 然而常常会致使过量的递归函数调用, 进而消耗过量的内存, 这种情况会导致内存溢出, 或者消耗过量的运算能力, 使得运行时间过长, 致使这种状态产生。6.4 递归函数的应用:最大公约数那个, 针对最大公约数问题来进行计算所运用的递归方法, 被称作欧几里得算法, 它的相关描述是这样滴当出现p大于q这种情况时, 那么p与q二者的最大公约数, 等同于q和p除以q所得到余数之间的最大公约数。故可以使用递归函数实现步骤如下。(1)终止条件: gcd(p,q) p #当q0时(2)递归步骤: gcd(q, p%q) #当q1时每次递归,p%q严格递减故逐渐收敛于0。使用递归函数计算最大公约数。import sys def gcd(p, q): #使用递归函数计算p和q的最大公约数 if q 0: return p #如果q0返回p return gcd(q, p % q) #否则递归调用gcd(q, p % q) #测试代码 p int(sys.argv[1]) #p命令行第一个参数 q int(sys.argv[2]) #q命令行第二个参数 print(gcd(p, q)) #计算并输出p和q的最大公约数6.5 递归函数的应用:汉诺塔源起于印度古老传说的汉诺塔, 也就是河内塔在大梵天创造世界之际被打造, 于世界中心贝拿勒斯的圣庙处放置了三根金刚石柱子, 上面一根柱子自下而上依照大小次序摞满了64片黄金圆盘, 这批圆盘就被叫做汉诺塔。游戏名为汉诺塔益智游戏, 其规则是, 大梵天让婆罗门把圆盘于3根柱子间, 按照从小到大的顺序, 自一根柱子, 转向另一根柱子重新摆放, 且每次仅能移动一个圆盘, 同时小圆盘之上不可安放大圆盘。专门用于锻炼思维的包含汉诺塔的那种益智游戏里所涉及的问题, 极其轻易地能够借助递归函数达成。假定用于放置圆盘的柱子, 它们的编号分别是a、b、c , 去定义具备特定作用的函数hanoi(n, a, b, c), 意思便是要把总数是n个的圆盘从中柱子a移动至柱子c在此过程中能够经由柱子b来达成, 那么就会出现如下情况:。(1)当n等于1的时候, hanoi(n, a, b, c)成为终止条件, 此终止条件是, 如若柱子a之上仅仅只有一个圆盘, 那么便能够直接把它移动到柱子c之上, 这就是终止条件, 是关于终止条件的一种表述。(2)递归的步骤是, hanoi(n,a,b,c)能够被分解成3个步骤, 这3个步骤分别是, hanoi(n-1,a,c,b), 是其中一个步骤, hanoi(1,a,b,c), 这是另一个步骤, 还有hanoi(n1,b,a.c), 这同样是一个步骤。要是柱子a那儿有n个圆盘着, 那可就能当作柱子a之上有一个圆盘呢底盘呀, 还有(n - 1)个圆盘。首先得把柱子a上面的(n - 1)个圆盘挪到柱子b那儿去, 也就是调用hanoi(n - 1,a,c,b)接着把柱子a上的最后那一个圆盘移到柱子c这儿, 也就是调用hanoi(1,a,b.c)然后再把柱子b上面的(n - 1)个圆盘移到柱子c那儿, 也就是调用hanoi(n - 1,b,a,c)。每次递归,n严格递减故逐渐收敛于1.使用递归 函数实现汉诺塔问题。#将n个从小到大依次排列的圆盘从柱子a移动到柱子c上柱子b作为中间缓冲 def hanoi(n,a,b,c): if n1: print(a,-,c) #只有一个圆盘直接将圆盘从柱子a移动到柱子c上 else: hanoi(n-1,a,c,b) #先将n-1个圆盘从柱子a移动到柱子b上采用递归方式 hanoi(1,a,b,c) #然后将最大的圆盘从柱子a移动到柱子c上 hanoi(n-1,b,a,c) #再将n-1个圆盘从柱子b移动到柱子c上采用递归方式 #测试代码 hanoi(4,A,B,C)7、内置函数的使用语言里给出了好些内置函数, 用来达成常用的功能, 能够直接加以使用。7.内置函数一览表, 其中包含内置函数, 有内置函数, 存在内置函数, 具备内置函数, 拥有内置函数。abs()()hash()()set()all()dict()help()min()()any()dir()hex()next()()ascii()()id()()()bin()()input()oct()()bool()eval()int()open()str()()exec()()ord()sum()()()()pow()super()bytes()float()iter()print()tuple()()()len()()type()chr()()list()range()vars()()()()repr()zip()()()map()()()()()max()round()各个内置函数的具体功能和用法可通过访问 进行查看。要加以留意的是, 对于开发者而言并不建议把以上所提到的内置函数之名用作标识符用以称呼比如说当成某个变量、函数、类别、模板或者是其他对象予以命名, 尽管这样去做的话解释器不会出现报错的情况, 然而这却会致使同名的内置函数被覆盖, 进而没办法加以使用。8、函数式编程基础拥有面向对象的程序设计语言性质, 具备面向过程的程序语言特点, 并且还支持函数式编程。标准库, 给出了好些关乎函数的函数, 给出了跟ML里的函数式程序设计工具。8.1作为对象的函数在语言中函数也是对象故函数对象可以赋值给变量。作为对象的函数。fabs type(f) #输出: f(-123) #返回绝对值输出:1238.2 高阶函数函数对象能够当作参数传送给函数, 并且能够作为函数的返回值。当作参数的函数对象的函数乃是高阶函数, 同时返回函数对象的函数亦是高阶函数, 高阶函数也就是函数的函数。高阶函数。def compute(f, s): #f为函数对象,s为系列对象 return f(s) compute(min,(1,5,3,2))8.3map()函数map() 函数的基本语法格式如下map(function, iterable)当中, 参数意味着需传入一个函数过来, 这个函数也许是固有函数, 还或者是针对此特定场景自行设计、定制的函数呢, 除此之外, 它也有可能是匿名构造所而成函数它所指示乃一个, 抑或多个能够做迭代而获取各分项的对象, 比如列表类型数据, 又或者字符串类型数据之类的。map()函数具备这样的功能: 针对可迭代对象里的每一个元素, 均会调用指定的那个函数, 之后返回一个map对象。请注意, 此函数所返回的乃是一个map对象, 其不可以直接进行输出, 是能够借助for循环或者list()函数来予以显示的。【例32】还是对列表中的每个元素乘以 2。listDemo [1, 2, 3, 4, 5] new_list map(lambda x: x * 2, listDemo) print(list(new_list))运行结果为2 4 6 8 10【例33】map() 函数可传入多个可迭代对象作为参数。listDemo1 [1, 2, 3, 4, 5] listDemo2 [3, 4, 5, 6, 7] new_list map(lambda x,y: x y, listDemo1,listDemo2) print(list(new_list))运行结果为4, 6, 8, 10, 12留意, 鉴于map()函数系径直用C语言编写而成, 运行之际无需经由解释器进行间接调用, 而且其内部实施了诸多优化举措, 因而与其他方法比较而言, 此方法的运行效率是最为高的。8.()函数()函数的基本语法格式如下filter(function, iterable)此格式中 参数表示要传入一个函数 表示一个可迭代对象。那函数具备一项功能, 此项功能针对其中的每一个元素, 都运用另一函数去进行判断, 判断之后会返回True或者False, 如此并且最终会把那些返回True的元素组建成为一个新的、能够进行遍历的集合。【例34】返回一个列表中的所有偶数。listDemo [1, 2, 3, 4, 5] new_list filter(lambda x: x % 2 0, listDemo) print(list(new_list))运行结果为【例35】() 函数可以接受多个可迭代对象。listDemo [1, 2, 3, 4, 5] new_list map(lambda x,y: x-y0,[3,5,6],[1,5,8] ) print(list(new_list))运行结果为True, False, False8.()函数其具体是作用于对一个于集合去开展一些进行那种 、累积操作的所为函数 , 而它具备的 、很基本的语法格式那便是 这样一种情况:reduce(function, iterable)当中, 所定的必然是一个涵盖 2 个参量的函数, 其表示的是能够进行迭代的对象。注意, 因 () 函数在3.x里已然被移除, 被放置于 模块之中, 基于此, 在运用该函数以前, 要先去导入 模块。【例36】计算某个列表元素的乘积。import functools listDemo [1, 2, 3, 4, 5] product functools.reduce(lambda x, y: x * y, listDemo) print(product)运行结果为120小结一般来讲, 要是针对集合里的元素开展某些操作, 要是操作极为简易, 像相加、累积这般的, 那么理应优先思索运用map()、()、()予以实现。此外, 处在数据量极为繁多的情形下例如机器学习的运用, 通常更偏向于函数式编程的呈现, 缘由在于效率更高。即便如此, 当数据量并非众多时, 借助for循环诸如此类的途径亦可达到目的。然而呢, 要是针对集合里的元素施行一些相对繁琐复杂的事项, 鉴于代码的可解读性方面的考量, 一般而言都会选用for循环。8.6 表达式匿名函数及用法现代编程语言纷纷引入的一种语法是代码块它被称作语法, 更是表达式也叫匿名函数, 一旦程序里有某代码块, 它可以传递调用, 该代码块功能其实更为灵活, 要是说有命名且方便复用的代码块是函数的话, 那么它就是表达式。回顾局部函数def get_math_func(type) : # 定义一个计算平方的局部函数 def square(n) : # ① return n * n # 定义一个计算立方的局部函数 def cube(n) : # ② return n * n * n # 定义一个计算阶乘的局部函数 def factorial(n) : # ③ result 1 for index in range(2 , n 1): result * index return result # 返回局部函数 if type square : return square if type cube : return cube else: return factorial # 调用get_math_func()程序返回一个嵌套函数 math_func get_math_func(cube) # 得到cube函数 print(math_func(5)) # 输出125 math_func get_math_func(square) # 得到square函数 print(math_func(5)) # 输出25 math_func get_math_func(other) # 得到factorial函数 print(math_func(5)) # 输出120在程序里面, 存在对一个函数的定义, 这个函数带有括号, 它会返回另外一个函数。紧接着, 在带有括号的这个函数的内部里面, 标号为①②③的代码分别对三个局部函数进行了定义。最后, 带有括号的这个函数会依据所传入的参数, 把这三个局部函数当中的一个当作返回值。在对会返回函数的那个括号函数进行定义之后, 随后程序调用那个括号函数的时候, 便能够返回所需的函数了。鉴于局部函数作用域默认只停留在其封闭函数以内, 所以这三个局部函数函数名作用极为有限, 也就是仅在程序if语句里当作返回值运用。一旦脱离了()函数体, 这三个局部函数函数名便失去价值。要是局部函数的那函数名没多大意义, 那就思索着采用表达式去把局部函数的写法予以简化。使用 表达式代替局部函数要是运用表达式去简化函数, 那么能够把程序改写为下面这样的形式:def get_math_func(type) : result1 # 该函数返回的是Lambda表达式 if type square: return lambda n: n * n # ① elif type cube: return lambda n: n * n * n # ② else: return lambda n: (1 n) * n / 2 # ③ # 调用get_math_func()程序返回一个嵌套函数 math_func get_math_func(cube) print(math_func(5)) # 输出125 math_func get_math_func(square) print(math_func(5)) # 输出25 math_func get_math_func(other) print(math_func(5)) # 输出15.0上头代码里头, 后续部分借助关键字予以定义的便是表达式, 规定该表达式仅仅能够是单行表达式。留意: 鉴于, 表达式仅能够是单行表达式, 不答应运用更为繁杂的函数形式, 因而, 上面③号代码处变更为计算1加2加3加…加n的总计和。表达式的语法格式如下lambda [parameter_list] : 表达式从上面的语法格式可以看出 表达式的几个要点表达式必须使用 关键字定义。处于关键字之后, 冒号左边位置的是参数列表。参数列表, 可以不存在参数, 也能够存在多个参数情形。要是存有多个参数的话, 那么就需求使用逗号分开隔开。冒号右边的部分, 是该表达式的返回值。事实上, 表达式的实质乃是匿名的、仅一行函数体的那种函数。所以, 表达式能够撰写成函数那种样式。例如对于如下 表达式lambda x , y:x y可改写为如下函数形式def add(x, y): return x y上面在定义函数之际, 运用了简化语法, 即当函数体仅仅存在一行代码之时, 能够直接将函数体的代码放置于跟函数头处于同一行的位置。普遍来讲, 函数相较于表达式具备更强的适应性, 表达式仅仅能够创建简单的函数对象其仅适用于函数体为单行的状况, 不过表达式仍然存在如下的两个用途:以下代码, 凭借表达式, 对内置的map()函数予以调用的情况进行的示范过程展示出来:# 传入计算平方的lambda表达式作为参数 x map(lambda x: x*x , range(8)) print([e for e in x]) # [0, 1, 4, 9, 16, 25, 36, 49] # 传入计算平方的lambda表达式作为参数 y map(lambda x: x*x if x % 2 0 else 0, range(8)) print([e for e in y]) # [0, 0, 4, 0, 16, 0, 36, 0]从上面代码可以看到, 按照要求, 内置的map()函数的首个参数得传入函数, 在此处, 传入的是函数的简化形式, 也就是表达式, 如此一来, 程序不但更简洁, 并且性能更佳。小结将编程过程中举足轻重的核心机制其一称为表达式, 语言不但支持面向过程类型的编程方式同时也对面向对象类型的持编程予以支持, 而作为面向过程编程所依赖的语法基础的表达式, 是务必需要引起关注重视的。其表达式不过是单行函数的简化形式了, 所以该表达式的作用更显简单。8.7函数装饰器及用法超级详细 8.7.1 引入具有内置特性的种类为三种的函数装饰器, 它们分别是、以及, 当中的、和均是属于的内置函数涵盖范围之内, 那么请问, 我们有没有可行的可能性去开展进行自行定义的函数装饰器呢? 其给出的答案是属于肯定性质的。当程序运用“函数”就像函数 A 这种去 另外一个函数好比函数 B的时候呀, 实际上达成了如下这两步呢:从上面所做的介绍当中, 能够了解到被“函数”进行修饰了的函数, 已经不再是之前原本的那个函数了, 而是被转变成为另外一个全新的事物这是由装饰器的还返回值来决定的。实际上, 所说的装饰器, 就是借助装饰器函数去对原函数的某些功能作出修改, 以此让原函数无需进行修改。为了让大家厘清函数装饰器的作用下面看一个非常简单的示例def funA(fn): print(A) fn() # 执行传入的fn参数 return fkit 下面装饰效果相当于funA(funB) funB 将会替换装饰成 funA() 语句的返回值 由于funA()函数返回 fkit因此 funB 就是 fkit funA def funB(): print(B) print(funB) # fkit位于上方的程序, 运用funA 去修饰 funB, 这代表着该程序必须达成两步相关操作:然而, 要是单纯地领会函数装饰器所具备的作用, 依据上面那套用程序能够以等效的方式转变成为下面这样的程序:def funA(fn): print(A) fn() # 执行传入的fn参数 return fkit def funB(): print(B) funB funA(funB) print(funB) # fkit留意一下, 在此程序里边, funB等于funA去调用funB的这个情况, 它等同于上面那个程序当中, 这个funA所产生的那作用句号。运行上面 2 段程序可以看到相同的输出结果Fkit凭借这个例证, 断定读者关于函数装饰器的执行关联已然存有一番相对清晰的认知了, 然而读者或许会萌生出另外一个疑惑, 此函数装饰器致使被装饰的函数转变为字符串, 那么函数装饰器具备何种用途呢?千万不要忘掉, 被加以修饰的那个函数, 始终都会转变成为 符号所参照引用的函数的返回结果, 所以呢, 被修饰的函数究竟会变成何种模样, 全然是由 符号所引用的函数的返回值来确定的, 也就是说, 要是 符号所引用的函数的返回值属于函数的话, 那么被修饰的函数在替换之后依旧是函数。8.7.2带参数的函数装饰器要是原函数funB()之中存在参数得传递给函数装饰器, 那该怎么去开展实现呢?有一种办法较为简洁, 那便是能够于与之对应的函数装饰器funA(), 给它追加所需的参数, 就如同下面这般:def foo(fn): # 定义一个嵌套函数 def bar(a): fn(a * (a - 1)) print(* * 15) return fn(a * (a - 1)) return bar 下面装饰效果相当于foo(my_test) my_test将会替换装饰成该语句的返回值 由于foo()函数返回bar函数因此my_test就是bar 同时my_test 的参数 a 对应 bar 函数的参数 a foo def my_test(a): print(my_test函数, a) # 打印my_test函数将看到实际上是bar函数 print(my_test) # 下面代码看上去是调用my_test()其实是调用bar()函数 my_test(10)上面的程序当中, 定义了一个装饰器函数, 这个函数是foo, 该函数执行完毕之后, 并非返回普通的值而是返回bar函数, 这乃是关键所在, 这就意味着, 被该foo修饰的函数, 最终会全部被替换成bar()函数。上面的程序, 运用了foo去修饰()函数这么一种情况, 所以会出现程序按照同样的方式去执行foo(), 还会把 经由foo()函数的返回值进行替换, 对应的返回值是bar函数。于是乎, 再看上面程序里的第18行代码在进行打印 函数这个操作的时候, 实际上输出呈现出来的是bar函数, 这也就表明 已然被替换成为bar函数了。进一步来看, 接下来程序执行调用()函数这个动作, 实际上所调用的就是bar()函数。运行上面程序可以看到如下输出结果 函数 90 函数 90立足于这个基础之上, 另存在着一个问题, 倘若在程序里除此之外还存在着一个函数, 并且这个函数同样需要运用 funA 装饰器, 然而这个全新的函数带有两个参数, 在这种情形下又应当怎样去处理呢? 举例来说:foo def new_test(a,b):....面临这样的情形时, 最简便易行的解法途径乃是将*args 以及**当作 foo 函数用作装饰器时其内部函 数 bar()的参数, *args 与**意味着接纳任意个数以及类别不设定的参数, 所以函数装饰器能够被写作如下这般的样式:def foo(fn): # 定义一个嵌套函数 def bar(*args,**kwargs): fn(*args,**kwargs) return bar foo def my_test(a): print(my_test函数, a) foo def new_test(a,b): print(new_test函数,a, ,b) my_test(10) new_test(6, 5)运行结果为 函数 10 函数 6 58.7.3带自定义参数的函数装饰器事实上, 函数装饰器具备更高级别的灵活性, 方才提到, 装饰器虽能够接纳原函数所有类型以及数量的参数, 然而除此之外, 它还能够接纳自行定义的参数。比如说, 要是想要去定义一个参数, 这个参数用于标明装饰器从内部开始的函数已被执行的次数, 那么就能够把它写成如下这样的形式:def foo(num): def my_decorator(fn): def bar(*args,**kwargs): for i in range(num): fn(*args,**kwargs) return bar return my_decorator foo(3) def my_test(a): print(my_test函数, a) foo(5) def new_test(a,b): print(new_test函数,a, ,b) my_test(10) new_test(6, 5)运行结果为 函数 10 函数 10 函数 10 函数 6 5 函数 6 5 函数 6 5 函数 6 5 函数 6 58.7.4函数装饰器也可以嵌套就在上面所给出的示例当中, 呈现的全都是运用一个装饰器的情形, 然而事实上, 它同样是支持多个装饰器存在的, 就好比是这样:decorator1 decorator2 decorator3 def func(): ...上面程序的执行顺序是里到外所以它等效于下面这行代码decorator1( decorator2( decorator3(func) ) )待续······
返回列表