小白学习之路,基础三(函数)

爱⌒轻易说出口 提交于 2020-03-30 03:44:55

 一,函数的基本介绍

首先谈到函数,相信大家都不陌生,不管是其他语言都会用到,可能就是叫法不一样。就我知道的跟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
View Code

值得注意的是在传入的参数还有三种:普通参数,不定长参数,自典型

 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'}
View Code

听到参数大家还经常听到形参跟实参这两个词,下面我跟大家大概讲一下两者的区别。

  • 形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量
  • 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值

三,全局变量跟局部变量

什么是全局变量跟局部变量呢,根据他们的名字可以知道,全局变量可以在全局使用,局部变量呢只能在一些地方使用的变量。

在函数里面定义的变量为局部变量,只能试用于函数。

在外面定义的叫全局变量,也可以在函数内部访问。

切记!如果全局变量有一个变量名字跟函数里面的相同,函数内部在调用时,用的是函数内部的变量

 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
View Code

在函数中的局部变量也能转化为全局变量,但是这种方法是不推荐使用的,也是不能使用的。

 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
View Code

四,匿名函数

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']
View Code

五,嵌套函数

嵌套函数,听到名字难道是一个函数里面还可以放函数,卧槽,居然特么还有这种操作?当然,嵌套函数就是一个函数里面还嵌套另外一个函数,下面一个简单的例子,给大家参考理解吧。

 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
View Code

六,递归

我们刚刚了解了嵌套函数,知道在函数的内部可以继续调用函数,如果在函数的内部调用自己,那么这个函数就是递归。当然递归不能随便乱用,他也有自己的调用特性。

递归特性:

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
View Code

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
View Code

卧槽,不知不觉学了这么多函数了啊,你就会想这个学了有啥子用嘛,放心,这个为后面厉害的装逼的打基础呢,这章的嵌套函数跟高阶函数为后面的装饰器打基础。万丈高楼平地起,所以还是要把基础掌握好,怎么才能更好的掌握呢,当然方法只有一个,那就是多敲,多记,多思考。

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!