小白学习之路,基础三(函数)
一,函数的基本介绍
首先谈到函数,相信大家都不陌生,不管是其他语言都会用到,可能就是叫法不一样。就我知道的跟python中函数类似的,在C中只有function,在Java里面叫做method,在js中也是叫function。函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。
函数有下面三点好处:
1.能够减少重复代码的使用
2.让你的程序有更好的扩展性
3.可以让你的程序变得更加容易维护
下面我们就来讲一下怎么定义一个函数
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。
- 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
- return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
补充知识:return不返回值的时候有三种方法:不写return,只写一个return,或者return None。return当有多个返回值的时候用逗号分隔。接收的时候可以用一个变量接收(元组),也可以用多个变量接收。
二,函数的参数
函数可以传参,也可以不传参,都是可以的哦。
1 #定义一个需要传参的函数 2 def say(name): 3 print('hello',name) 4 say('zzq') #调用say这个函数,并且传入参数 5 6 #不需要传参的函数 7 def take(): 8 print('I Love You') 9 take() #调用函数,不传任何参数 10 11 #有返回值的函数 12 def add(x,y): 13 return x+y 14 a=add(4,5) #传入的参数必须是相同数据类型 15 print(a) #执行结果为9
值得注意的是在传入的参数还有三种:普通参数,不定长参数,自典型
1 #普通参数 2 def say1(name): 3 print(name) 4 say1('gmx') 5 6 #不定长参数 7 def say2(*args): 8 print(args) 9 say2(1,5,'sd') 10 11 #自典型 12 def say3(**kwargs): 13 print(kwargs) 14 say3(name='zzq',say='I Love You',to_name='gmx')#传入的参数必须是字典 15 16 #执行结果为 17 #gmx 18 #(1, 5, 'sd') 19 #{'name': 'zzq', 'say': 'I Love You', 'to_name': 'gmx'}
听到参数大家还经常听到形参跟实参这两个词,下面我跟大家大概讲一下两者的区别。
- 形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量
- 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值
三,全局变量跟局部变量
什么是全局变量跟局部变量呢,根据他们的名字可以知道,全局变量可以在全局使用,局部变量呢只能在一些地方使用的变量。
在函数里面定义的变量为局部变量,只能试用于函数。
在外面定义的叫全局变量,也可以在函数内部访问。
切记!如果全局变量有一个变量名字跟函数里面的相同,函数内部在调用时,用的是函数内部的变量
1 name='zzq' 2 def say(): 3 name='gmx' 4 print(name)#调用的是函数内部的name 5 say() 6 print(name)#调用的全局变量的name 7 8 #执行结果 9 #gmx 10 #zzq
在函数中的局部变量也能转化为全局变量,但是这种方法是不推荐使用的,也是不能使用的。
1 name='zzq' 2 def say(): 3 global name #把name设置为全局变量 4 name='gmx' 5 print(name) 6 say() 7 print(name) 8 #执行结果 9 #gmx 10 #gmx
四,匿名函数
python 使用 lambda 来创建匿名函数,一般一个函数用的次数比较少,为了方便就出现了匿名函数。
- lambda只是一个表达式,函数体比def简单很多。
- lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
- lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
- 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
匿名函数基本语法:
1 #计算数字的平方 2 calc=lambda n:n*n #定义一个匿名函数 3 print(calc(5)) 4 5 #当然还能这样用 6 l=map(lambda n:n*n,[1,2,3,4,5]) #map把后面可迭代对象去执行前面的函数 7 for i in l: 8 print(i)
在这里用到了map,那就简单的提一下把。因为要涉及到后面的迭代对象,所以就简单讲一下用法。
map函数的原型是map(function, iterable, …),它的返回结果是一个列表。
参数function传的是一个函数名,可以是python内置的,也可以是自定义的。
参数iterable传的是一个可以迭代的对象,例如列表,元组,字符串这样的。
解释起来有点不好理解,下面一些简单的代码例子,供大家参考吧。
1 a=(1,2,3,4,5) 2 b=[1,2,3,4,5] 3 c="zhangkang" 4 5 la=map(str,a) 6 lb=map(str,b) 7 lc=map(str,c) 8 9 print(la) 10 print(lb) 11 print(lc) 12 13 输出: 14 ['1', '2', '3', '4', '5'] 15 ['1', '2', '3', '4', '5'] 16 ['z', 'h', 'a', 'n', 'g', 'k', 'a', 'n', 'g']
五,嵌套函数
嵌套函数,听到名字难道是一个函数里面还可以放函数,卧槽,居然特么还有这种操作?当然,嵌套函数就是一个函数里面还嵌套另外一个函数,下面一个简单的例子,给大家参考理解吧。
1 name='zzq' 2 def say1(): 3 name='gmx' 4 def say2(): 5 name='other' 6 print('最里面函数',name) 7 say2() 8 print('外面一层函数',name) 9 say1() 10 print('最外面函数',name) 11 12 #执行结果 13 #最里面函数 other 14 #外面一层函数 gmx 15 #最外面函数 zzq
六,递归
我们刚刚了解了嵌套函数,知道在函数的内部可以继续调用函数,如果在函数的内部调用自己,那么这个函数就是递归。当然递归不能随便乱用,他也有自己的调用特性。
递归特性:
1. 必须有一个明确的结束条件
2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
1 #输入一个数,输出所有平方小于500的数 2 def calc(n): 3 if n>500: 4 return n 5 print(n) 6 return calc(n*n) #继续调用自己,形成递归 7 calc(2) 8 9 #执行结果 10 2 11 4 12 16 13 256
4.阶乘
在递归的运用中,有一个很经典的例子,那就是计算阶乘的结果。用其他方法也可以实现,但是估计比较麻烦,下面是用简单的递归实现阶乘计算。
1 def test(x):
2 if x==1:
3 return x
4 else:
5 return x*test(x-1)
5.汉诺塔问题
当然在递归里面很经典的还有一个问题就是汉诺塔问题,而且更能体现递归的好处。
如下图所示,从左到右有A、B、C三根柱子,其中A柱子上面有从小叠到大的n个圆盘,现要求将A柱子上的圆盘移到C柱子上去,期间只有一个原则:一次只能移到一个盘子且大盘子不能在小盘子上面,求移动的步骤和移动的次数
解决的思路其实就三步:1.先把n-1个盘子移动到B(借助C),2.最大的那个放在C,3.把n-1个盘子放到C(借助A)
x = 0 # 记录移动次数
def hannota(n, A, B, C):
'''
:param n: 总共移动盘子个数
:param A: A柱子
:param B: B柱子
:param C: C柱子
:return:
'''
if n > 0:
hannota(n - 1, A, C, B) # 把n-1从A进过C移动到B
global x
x += 1
print '%s-->%s' % (A, C) # 把最大的n从A移动到C
hannota(n - 1, B, A, C) # 把剩下的n-1从B通过A移动到C
hannota(2, "A", "B", "C")
print x
七,高阶函数
变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。通俗的讲就是函数的参数可以是函数,然后里面的参数可以给作为参数的函数当成传入的参数。emmm,好吧,讲得是有点乱。。那就分析分析代码吧
1 输入ascii的编码号,输出对应的值 2 def say(letter,f): 3 return f(letter) 4 ascii_name=say(65,chr)#f为一个函数 5 print(ascii_name) 6 #执行结果 7 #A
卧槽,不知不觉学了这么多函数了啊,你就会想这个学了有啥子用嘛,放心,这个为后面厉害的装逼的打基础呢,这章的嵌套函数跟高阶函数为后面的装饰器打基础。万丈高楼平地起,所以还是要把基础掌握好,怎么才能更好的掌握呢,当然方法只有一个,那就是多敲,多记,多思考。