Python第三章__函数式编程、递归、闭包
欢迎加入Linux_Python学习群
群号:478616847
目录:
-
函数式编程
-
传参与返回值
-
递归
-
匿名函数
- 闭包
-
高阶函数
-
内置函数
在第三章,我们引入新的概念函数,在以往的代码编写中我们都是用的过程式编程,函数式编程的特点将过程式编程变成易于管理调用的小模块,
让重复的代码可以反复的调用,大大减少代码量,懒惰即美德
一、函数式编程
创建函数
一个函数式由关键字 def ,与函数名与括号冒号,和括号中的参数组成,当想要执行函数的时候只需要写上函数名加括号即可
格式: def function (parameter) 下面就创建一个函数
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 #创建函数 5 def print_str(): 6 print("Hello World") 7 8 #调用函数 9 print_str()
变量的作用域
我们看一个例子,在下面代码中我们先把变量 a=“haha” 然后在函数中把 a=“heihei” 最后执行这个函数,并输出变量a的结果
我们发现为什么a不等于 "heihei",而是我们之前赋值的 “haha” 这个现象我们马上揭秘
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 5 a = "haha" 6 7 #创建函数 8 def print_str(): 9 a = "heihei" 10 print("Hello World") 11 12 #调用函数 13 print_str() 14 print("我是变量a:",a)
全局变量与局部变量
很明显变量的作用域就是变量在哪一个范围内生效,如果出了这个范围则无法使用
全局变量:通常全局变量存活在脚本运行结束,并且所有的函数都可以访问它
局部变量:只能局部变量所在的函数内部调用,并且除非把局部变量声明成全局变量否则,其他函数均无法调用,并且局部变量
当所在的函数执行完成后就不存在了
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 5 a = "haha" 6 7 #创建函数 8 def print_str(): 9 a = "heihei" 10 print("我是局部变量a:",a) 11 12 13 #调用函数 14 print_str() 15 print("我是全局变量a:",a)
global
global就是可以把局部变量变成全局变量的,如果被声明的局部变量名与全局变量名一样的话,那么局部变量会覆盖全局变量,切
使用global声明变量需要在变量之前声明否则python会告诉你,你需要在 a 前面声明
报错提示:SyntaxWarning: name 'a' is assigned to before global declaration global a
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 a = "haha" 5 6 #创建函数 7 def print_str(): 8 global a 9 a = "heihei" 10 print("我是局部变量a:",a) 11 12 #调用函数 13 print_str() 14 print("我是全局变量a:",a)
二、传参与返回值
传参
函数用起来比较简单也容易理解,但是参数的变化就比较多了,在函数括号内就是参数,参数可以接收字符串,数字,也可以接收字典和列表
并且在调用的时候,我们还可以指定给哪个参数赋什么值
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 #创建函数 5 def print_str(Name,Age,Aender): 6 print(''' 7 Name:%s 8 Age:%s 9 Aender:%s 10 '''%(Name,Age,Aender)) 11 12 #用户输入 13 in_name = input("请输入你的名字:") 14 in_age = input("请输入你的年龄:") 15 in_aender = input("请输入你的性别:") 16 17 #固定位置传参 18 print_str(in_name,in_age,in_aender) 19 print("----------------------------------") 20 21 #不固定位置传参 22 print_str(in_name,Aender=in_aender,Age=in_age)
当我们想要传入列表或者字典时需要怎么办?
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 def print_str(lists): 5 if type(lists) == list: 6 print(''' 7 Name:%s 8 Age:%s 9 Aender:%s 10 '''%(lists[0],lists[1],lists[2])) 11 else: 12 print(''' 13 Name:%s 14 Age:%s 15 Aender:%s 16 '''%(lists["name"],lists["age"],lists["aenber"])) 17 18 #传入列表 19 userlist = ["Ben","22","Man"] 20 print_str(userlist) 21 22 print("----------------------------------") 23 #传入字典 24 userdict = {"name":"Ben","age":"022","aender":"Man"} 25 print_str(userlist)
默认参数
在函数中还可以设置默认参数,默认参数的意思是这个参数你可以传值也可以不传值,当不传值的时候这个参数就等于默认值
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 def print_str(lists,Country="China"): 5 if type(lists) == list: 6 print(''' 7 Name:%s 8 Age:%s 9 Aender:%s 10 country:%s 11 '''%(lists[0],lists[1],lists[2],Country)) 12 else: 13 print(''' 14 Name:%s 15 Age:%s 16 Aender:%s 17 country:%s 18 '''%(lists["name"],lists["age"],lists["aenber"],Country)) 19 20 #传入列表 21 userlist = ["Ben","22","Man"] 22 print_str(userlist) 23 24 print("----------------------------------") 25 #传入字典 26 userdict = {"name":"Ben","age":"022","aender":"Man"} 27 print_str(userlist,"America")
非固定参数
非固定参数的意义在于可以接收任意个值,在你的函数不确定用户想传入多少个值的时候使用,当然在调用有只有非固定参数的函数的时候我们可以不传参数。
非固定参数分一个*和两个*,*args会把传入的参数变成元祖,**kwargs把传入的参数变成字典,当然*ages可以是别的名称,但是在规范中最好使用*args,和**kwargs
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 def print_str(*args,**kwargs): 5 print("我是*",args) 6 print("我是**",kwargs) 7 8 #传入列表 9 userlist = ["Ben","22","Man"] 10 print_str(userlist,"America") 11 print("----------------------------------") 12 #传入字典 13 print_str(A = "1",B = "2")
既然形参可以带*和**,那么实参也是可以带*和**,那么*就是配合列表使用的,**就是配合字典的!
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 def print_str(*args,**kwargs): 5 print("我是*",args) 6 print("我是**",kwargs) 7 8 #传入列表 9 userlist = ["Ben","22","Man"] 10 print_str(*userlist) 11 12 print("----------------分隔符----------------") 13 14 #传入字典 15 userdict = {"name":"Ben","age":"022","gender":"Man"} 16 print_str(**userdict) 17 18 解参
实参带*就会把列表分解成 "Ben","22","Man" 一个一个单独的元素传入函数,而**会把字典分解成name="Ben",age="022",gender="Man"
这种键值对传入函数。
形参与实参
形参:
变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用
函数后则不能再使用该形参变量
实参:
可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此
应预先用赋值,输入等办法使参数获得确定值
注!当普通参数和默认参数和非固定参数结合使用的时候,要遵循一个顺序,普通参数在默认参数前面,默认参数在非固定参数前面
返回值
在正常使用函数的时候,函数是可以把函数内部处理的结果返回给函数调用者的,在没有返回值得函数中会保留None传给函数调用者,返回值可以返回序列等
在函数执行的时候遇到return函数会停止执行,并返回结果
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 #创建函数 5 def print_str(Age): 6 if int(Age) <= 30: 7 return "你才%s啊!真年轻"%(Age) 8 else: 9 return "你都%s啦!老家伙"%(Age) 10 11 in_age = input("请输入你的年龄:") 12 word = print_str(in_age) 13 print(word)
嵌套函数
在函数内部也可以写函数,这样就是外层函数套着内侧函数,这种形式称之为嵌套函数,同理因为作用域的关系嵌套函数只能内部调用
return unction_2(stra)+"我是第二层\n" 就等于先 c = unction_2(stra) 然后 return c+"我是第二层\n"
1 def unction(stra): 2 def unction_2(stra_2): 3 return stra_2+"我是第三层\n" 4 return unction_2(stra)+"我是第二层\n" 5 6 r_str = unction("") 7 print(r_str+"我是第一层")
三、递归
前面已经介绍了函数相关的知识,在函数中,函数可以调用其他的函数,并且函数还可以调用自身,利用这种特性我们可以完成一些特定的
操作,这种函数调用自身的形式就是递归
def recursion() :
return recursion()
在递归中不能像上面两行一样一直调用自身,这样一会程序就会崩溃,因为它永远的在调用就跟while死循环一样出不去,所以递归也需要进
判断给它出口
例子:阶乘
什么是阶乘,阶乘就是给一个自然数N,然后计算N的阶乘那么 N = 1x2x3x4....N ,这个就是阶乘,我们可以把它到过来看,
N = N x (n-1) x (n-2) x (n-3) ...... 1 一直乘到括号中的值等于1,既然知道了阶乘是什么,那么我们来写一个程序实现它
1 def factorial(n): 2 for i in range(1,n): 3 n *= i 4 return n 5 c = factorial(4) 6 print(c)
剖析:上面的例子首先把n=4传入进去,然后通过 for i in range(1,4)让i分别等于1,2,3,然后进行 n*=i,我们可以看出这个for循环是循环3次的
第一次(n = n*i) n = 4*1 ,此时n还是等于4
第二次(n = 4*i) n = 4*2 此时n = 8
第三次(n = 8*i) n = 8*3 此时n等于24
此时for循环了3次所以结束了,通过return把n的结果返回,所以最终结果算出 4的阶乘等于24
递归版本
下面看递归版本的阶乘
1 def factorial(n) : 2 if n == 1: 3 return 1 4 else: 5 return n * factorial(n-1) 6 c = factorial(4) 7 print(c)
剖析:
首先c = factorial(4)开始执行函数,然后进行第一次判断 n == 1,显然第一层n不等于1,然后碰到return n * factorial(n-1),碰到return本来是要返回的,但是 factorial(n-1)
有调用了factiorial这个函数,因此进入了第二层
第二层因为上一层传入的参数是n-1,所以第二层的n是等于3的,然后判断,这一层的n也不等于1,然后又进入第三层
第三层n等于3,然后判断这一层的n还不等于1,然后又进入第四层
到第四层的时候这时的 n就等于1,所以触发了 return 1 不再调用函数了,所以就开始返回
返回第三层 return n * factorial(n-1) , 此时factorial(n-1) 就等于第四层return上去的1,所以第三层返回时就等于return n * 1(return 2 * 1),并且第三层n是等于2的
返回第二层factorial(n-1)就等于第三层return上去的2,并且第二层n是等于3的,return 3 * 2
返回第一层factorial(n-1)就等于第二层return上去的6,并且第一层n是等于4的,return 4 * 6
到此为止递归执行完毕,c就等于 4 * 6 c=24
四、匿名函数
匿名函数也叫lambda函数,函数没有具体的名称。语法:function name= lambda args(多个参数用逗号隔开): Expression(表达式,表达式的结果就是返回值)
先来看一个最简单例子:
1 #普通函数 2 def func(arg1,arg2): 3 return arg1-arg2 4 5 #lambda函数 6 func_2 = lambda arg1,arg2: arg1-arg2 7 8 #传参执行 9 print(func(5,4)) 10 print(func_2(5,4))
有认识,这个匿名函数和普通函数没有什么区别么,其实匿名函数就像三元运算一样,并且能够用lambda函数有几大优势
1、在一些不会再别的地方调用的函数,我们可以使用匿名函数,并且这样简化了代码,看起来更加整洁。
2、lambda函数将会搭配一些内置函数来使用(下面会涉及到)
五、闭包
在上面的示例中知道了函数可以调用函数本身,这种形式称之为递归,那么还可以将函数作为参数返回,这种形式就称之为闭包
闭包最大的好处就是即用即调,闭包对于安装计算,隐藏状态,以及在函数对象和作用域中随意地切换是很有用的!
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 def func(rate): 4 count = [0] 5 def add_func(arg): 6 count[0] +=1 7 print("第%s次调用"%count[0]) 8 arg = arg - arg*rate 9 return arg 10 return add_func 11 12 closure = func(0.03) 13 print(closure(1000)) 14 print(closure(1100)) 15 print(closure(1200))
例子中做了一个减去手续费后返回余额的功能,首先执行了func函数,将利率封装了进去,然后,func函数把它内部的函数进行了进行了返回
要知道当函数不加括号的时候是不执行的!,所以此时closoure就是 add_func 函数的内存地址,当想要使用这个功能的时候,直接把closoure加括号
并传入值即可执行。并且可以看到的是在全局作用域中执行,随时可以切换到局部作用域。
六、高阶函数
函数可以用来当做返回值,可以用调用自己本身,高阶函数就是函数的参数把另一个函数作为参数,这种函数就称之为高阶函数。
1 def func_1(num): 2 return num+1 3 4 def func_2(num): 5 return num-1 6 7 def func_main(num,func): 8 # 可以简写成return func(num) 9 results = func(num) 10 return results 11 12 results = func_main(10,func_1) 13 print(results) 14 print(func_main(10,func_2))
编写高阶函数,就是让函数的参数能够接收别的函数。
七、内置函数
内置函数就是python中内置的一些方法,内置函数官方介绍请猛戳这里
内置函数使用方法示例,详细介绍请参考
1 # !/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 #返回数字的绝对值。 参数可以是整数或浮点数。 如果参数是复数,则返回其大小。 5 print(abs(-1.11)) 6 7 #传入一个可被循环的元素,如果这个元素中有一个为False则都为假 8 # 0 空值 False 都为假 9 print(all([1,2,3])) 10 11 #与all相反,只有一个为真,则为真; 12 print(any([0,2,False])) 13 14 #这个函数跟repr()函数一样,返回一个可打印的对象字符串方式表示。当遇到非ASCII码时 15 #就会输出\x,\u或\U等字符来表示。与Python 2版本里的repr()是等效的函数。 16 print(ascii("dsads"),ascii(66),ascii('b\23')) 17 18 #将十进制转换为二进制; 19 print(bin(10)) 20 21 #返回布尔值,即True或False之一,如果参数为false或省略,则返回False; 否则返回True。 22 print(bool(1)) 23 24 #根据传入的参数创建一个新的字节数组 25 #如果传入字符串必须给出编码 26 print(bytearray('你好','utf-8')) 27 #当source参数是一个可迭代对象,那么这个对象中的元素必须符合大于0 小于256 28 print(bytearray([256,1,2])) 29 30 #返回一个的“bytes”对象,返回bytes类型 31 bytes('中文','utf-8') 32 33 #检查对象是否可以被调用 34 def func(): 35 pass 36 print(callable(func)) 37 38 #返回整数所对应的Unicode字符,chr(97)返回字符串'a',而chr(8364)返回字符串'€'。 39 print(chr(126)) 40 41 #是用来指定一个类的方法为类方法,类方法可以不实例化直接调用 42 class A: 43 @classmethod 44 def B(cls,arg1,): 45 print(arg1) 46 A.B(1) 47 A().B(1) 48 49 #将源编译为代码或者AST对象。代码对象能够通过exec语句来执行或者eval()进行求值。 50 #源可以是正常字符串,字节字符串或AST对象。 51 expr = "5+5-1" 52 obj = compile(expr,"","eval") 53 print(eval(obj)) 54 55 #返回值为real + imag * j的复数或者转化一个字符串或数为复数。如果第一个参数为字符串,则不需要指定第二个参数。 56 print(complex(1, 2)) 57 print(complex(1)) 58 print(complex("1+2j")) 59 60 61 62 # 参数是一个对象和一个字符串。 该字符串必须是对象属性之一的名称。 63 class A: 64 def a1(self): 65 print("a1") 66 def a2(self): 67 print("a2") 68 69 obj = A 70 print(dir(obj)) 71 delattr(obj, "a1") 72 print(dir(obj)) 73 74 #dir 返回对象中的方法 75 strs="aaa" 76 print(dir(strs)) 77 78 #返回两个数值的商和余数 79 print(divmod(7,3)) 80 81 #用于遍历序列中的元素以及它们的下标 82 print(enumerate([1,2,3]))#返回的是可迭代的对象 83 for i,j in enumerate(('A','B','C')): 84 print(i,j) 85 86 #将字符串str当成有效的表达式来求值并返回计算结果。 87 print(eval("1+2+3")) 88 print(eval("False or True")) 89 90 #字符串str当成动态语句块执行并返回结果 91 exec('a=1+2+3') 92 print(a) 93 94 #使用指定方法(方法,函数),过滤可迭代对象的元素 95 def add(arg): 96 return arg > 3 97 98 for i in filter(add,[1,2,3,4,5]): 99 print(i) 100 101 #浮点型 102 print(float(11)) 103 104 #格式化显示 更多方法请参考官方说明 105 print('{:,.2f}'.format(111111)) 106 107 #根据传入的参数创建一个新的不可变集合 108 a = frozenset([1,2,3,4,5]) 109 print(a) 110 111 #获取对象的属性值 112 class A(): 113 def __init__(self,): 114 self.name = "123" 115 b = A() 116 print(getattr(b,'name')) 117 118 #返回当前作用域内的全局变量和其值组成的字典 119 print(globals()) 120 121 #检查对象是否含有属性 122 class A(): 123 def __init__(self,): 124 self.name = "123" 125 b = A() 126 print(hasattr(b,'name')) 127 128 #哈希值计算 129 #在当前环境中是唯一的 130 print(hash('Hello')) 131 132 #help帮助 133 def funcs(args): 134 """ 135 Function description 136 :param args: args = list 137 :return: 138 """ 139 pass 140 print(help(funcs)) 141 142 #转换16进制 143 print(hex(44)) 144 145 #显示对象的标识符 146 print(id("123")) 147 148 #input标准输入 149 s = input("user name:") 150 print(s) 151 152 #int返回整数 153 print(int(1.2)) 154 print(int("2")) 155 156 #判断对象是否是类或者类型元组中任意类元素的实例 157 print(isinstance("1",int)) 158 print(isinstance(1.1,(int,float))) 159 160 161 #判断类是否是另外一个类或者类型元组中任意类元素的子类 162 print(dir(str)) 163 print(issubclass(bytearray,str)) 164 print(issubclass(bool,int)) 165 166 #根据传入的参数创建一个新的可迭代对象 167 a = iter('12345') 168 print(next(a)) 169 print(next(a)) 170 171 #返回对象的长度len 172 a = [1,2,3,4] 173 174 #转换列表 175 print(list("abcd")) 176 177 #返回当前作用域内的局部变量和其值组成的字典 178 def A(): 179 print(locals()) 180 s = 1 181 print(locals()) 182 A() 183 184 #使用指定方法去作用传入的每个可迭代对象的元素,生成新的可迭代对象 185 def add(x): 186 return x+100 187 lists = [1,2,3,4] 188 for i in map(add,lists): 189 print(i) 190 191 #max:返回最大值 192 print(max(1,2,3)) 193 print(max([1,2,3,4])) 194 195 #在进行切片并赋值数据时,不需要重新copy原列表数据,可以直接映射原数据内存; 196 s = memoryview(b'abcd') 197 print(s[1]) 198 199 #返回最小值 200 print(min(1,2,3)) 201 print(min([2,3])) 202 203 #返回可迭代对象中的下一个元素值 204 a = iter('1234') 205 print(next(a)) 206 207 #创建一个新的object对象(新式类) 208 class B(object): 209 pass 210 211 #转化成8进制数字符串 212 print(oct(10)) 213 214 #open文件操作 215 file = open('test.txt',encoding="utf-8") 216 217 #ord:返回Unicode字符对应的整数 218 print(ord("A")) 219 220 #幂运算 221 222 print(pow(2,3)) 223 224 #标准输出 225 print() 226 227 #property:标示属性的装饰器 228 #类中使用具体方法请百度,或者等待后续更新 229 property 230 231 #range:根据传入的参数创建一个新的range对象 232 range(10) 233 range(1,10) 234 235 """repr()函数得到的字符串通常可以用来重新获得该对象,repr()的输入对python比较友好。 236 通常情况下obj==eval(repr(obj))这个等式是成立的。""" 237 obj='Python' 238 print(eval(repr(obj))) 239 240 241 #翻转序列 242 a = reversed([1,2,3,4,5]) 243 print(list(a)) 244 245 #round:对浮点数进行四舍五入求值 246 print(round(1.5)) 247 248 #set 转换成集合 249 print(set([1,2,3])) 250 251 #setattr:设置对象的属性值 252 class A(): 253 def __init__(self,age): 254 self.age = age 255 s = A(11) 256 print(s.age) 257 setattr(s,'age',22) 258 print(s.age) 259 260 #根据传入的参数创建一个新的切片对象 261 c1 = slice(3) 262 c2 = slice(2,4) 263 c3 = slice(0,5,2) 264 s = [1,2,3,4,5,6] 265 print(s[c1]) 266 print(s[c2]) 267 print(s[c3]) 268 269 270 #排序,返回一个新的列表默认按字符ascii码排序 271 a = [4,3,2,1,7] 272 print(sorted(a)) 273 274 #标示方法为静态方法的装饰器 275 class B(object): 276 def __init__(self,age): 277 self.age = age 278 279 @staticmethod 280 def hello(args): 281 print(args) 282 283 B.hello("Hello World") 284 285 #字符串类型 286 print(str(123)) 287 288 #求和 289 print(sum([1,2,3,4])) 290 291 #根据传入的参数创建一个新的子类和父类关系的代理对象 292 class A(object): 293 def __init__(self): 294 print("我是 A Clase") 295 296 class B(A): 297 def __init__(self): 298 print("我是 B Class") 299 super().__init__() 300 b = B() 301 302 #元祖 303 tuple([1,2,3,4]) 304 305 306 #type 返回对象的类型 307 print(type([1])) 308 print(type("1")) 309 310 #返回当前作用域内的局部变量和其值组成的字典,或者返回对象的属性列表 311 def func(): 312 print(vars()) 313 s = 1 314 print(vars()) 315 func() 316 317 #聚合传入的每个迭代器中相同位置的元素,返回一个新的元组类型迭代器 318 list1 = [1,2,3] 319 list2 = ["A","B","C","D"] 320 print(zip(list1,list2)) 321 for i in zip(list1,list2): 322 print(i) 323 324 #__import__:动态导入模块 325 __import__
作者:北京小远
出处:http://www.cnblogs.com/bj-xy/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。