【Python】Python之函数模块
大噶寒假快乐哇,哈哈哈,终于考完期末考试了!我终于可以再次捡起我的博客,继续向前探索Python的更多的内容了,所谓 “ 路漫漫其修远兮,吾将上下而求索 ” ,计算机的学习生涯是不断向前且永不停止的,所以为了学习到更多的前沿知识,成为一个具有竞争力的IT方面人才,对待计算机的学习一定要怀着一颗谦卑而又恭敬的内心!
废话不多说,我们这就进入今天的主题Python的函数学习。想来对于大家,函数 这个名词一定不很陌生,我们从小学就开始接触函数,直到进入大学第一次学习计算机编程语言的函数——C语言的函数,而下文我将给大家介绍Python语言简单的函数运用,帮助大家可以入门学习到Pyhton的函数。Python语言的函数虽然和C语言的函数有许多共通之处,但是也有C中未出现的东西,一会就会发现。这就进入主题——Python的函数。
Python之什么是函数
我们在小学就学习到了圆的面积计算公式为:S = πr² 。
当我们知道圆的半径 r 的值以后,就可以根据公式计算出面积。
假设我们需要计算3个不同大小的圆的面积:
r1 = 5
r2 = 8.88
r3 = 11.9
s1 = 3.14 * r1 * r1
s2 = 3.14 * r2 * r2
s3 = 3.14 * r3 * r3
print(s1)
print(s2)
print(s3)
当代码出现规律性重复的时候,你就需要当心了,每次写3.14 *x * x不仅很麻烦,而且如果要把3.14改成 3.1415926 的时候,就得全部替换。
有了函数,我们就不再每次写s = 3.14 * x * x,而是写成更有意义的函数调用 s = area_of_circle(x),而函数 area_of_circle 本身只需要写一次,就可以多次调用。
抽象是数学中非常常见的概念,举个例子:
计算数列的和,比如:1 + 2 + 3 + … + 1000,写起来十分不方便,于是数学家发明了求和符号 ∑ ,可以把1 + 2 + 3 + … + 1000记作:
1000
∑ n
n=1
这种抽象记法非常强大,因为我们看到 ∑ 就可以理解成求和,而不是还原成低级的加法运算。
而且,这种抽象记法是可扩展的,比如:
100
∑(n²+1)
n=1
还原成加法运算就变成了:
(1 x 1 + 1) + (2 x 2 + 1) + (3 x 3 + 1) + … + (100 x 100 + 1)
可见,借助抽象类型,我们才能不关心底层的具体计算过程,而直接在更高的层次上思考问题。
写计算机程序也是一样,函数就是最基本的一种代码抽象的方式。
而且 Python 不但能非常灵活地定义函数,而且本身内置了很多有用的函数,可以直接进行调用。
Python之调用函数
其实在Python的编译器中,就内置了很多有用的函数,我们可以直接进行调用。
要调用一个函数,需要知道函数的名称和参数,比如求绝对值的函数 abs ,而它接收一个参数。
为了清楚地了解Python编译器内置函数的名称和相关参数,我们可以直接从Python的官方网站查看文档(下为链接):
https://docs.python.org/3/library/index.html
也可以在交互式命令行通过 help(函数名) 查看所要查找相关的函数的帮助信息。
调用 abs 函数:
a = abs(1000)
b = abs(-219)
c = abs(12.14)
print(a)
print(b)
print(c)
测试结果:
调用函数的时候,如果传入的参数数量不对,就会报TypeError的错误,并且Python会明确地告诉你:abs()有且仅有1个参数,但你给了多个(如图标记处):
d = abs(123,567)
print(d)
如果传入的参数数量是对的,但参数类型不能被函数所接受,也会报TypeError的错误,并且给出错误信息(如下文例子,str是错误的参数类型):
d = abs('happy')
print(d)
而比较函数 operator.gt(x, y) 就需要两个参数,如果 x<=y,返回 False;如果 x>y,返回 True:
import operator
d = operator.gt(200,300)
e = operator.gt(400,399)
f = operator.gt(100,100)
print(d)
print(e)
print(f)
测试结果:
PS:上文用的是用来比较两数关系的 operator 函数,是Python 3 中新产生的函数,下面给出operator 的功能模块:
#Python 3 operator模块的功能如下:
import operator
operator.lt(a, b) #a < b
operator.le(a, b) #a <= b
operator.eq(a, b) #a == b
operator.ne(a, b) #a != b
operator.gt(a, b) #a > b
operator.ge(a, b) #a >= b
Python内置的常用函数还包括数据类型转换函数,比如 int()函数 可以把 其他数据类型 转换为 整数类型,str()函数 把 其他类型 转换成 str 类型 :
a = int('666')
b = int(3.14)
c = str(999)
d = str(1.23)
print(a)
print(b)
print(c)
print(d)
下面我们来编写一个完整的应用类代码:
sum()函数 接受一个list作为参数,并返回list所有元素之和。请计算 1 * 1 + 2 * 2 + 3 * 3 + … + 100 * 100。
解答:
这里我们需要用 while 循环构造出 list。( list 的相关内容在我以前的博文中有过提及)
参考代码如下:
L = []
x = 1
while x <= 100:
L.append(x * x) #在每一次的L中加入 x*x,完成求和目标
x = x + 1
print ( sum(L) )
Python之编写函数
在Python中,定义一个函数要使用 def 语句,依次写出函数名、括号、括号中的参数和冒号,然后在缩进块中编写函数体,函数的返回值用 return 语句返回。
我们可以自定义一个求相反数的 my_opposite 函数为例:
def my_opposite(x):
return (-x)
a = int(input()) #输入一个数值
print(my_opposite(a))
在此请注意:函数体内部的语句在执行时,一旦执行到 return 函数,函数就执行完毕,并将结果返回。因此,函数内部通过条件判断和循环可以实现非常复杂的逻辑。
如果没有 return 语句,函数执行完毕后也会返回结果,只是结果为 None 。
return None 可以简写成为 return 。
接下来我们来看一个简单的例题:
请定义一个 square_of_sum 函数,它接受一个 list ,返回 list 中每个元素平方的和。
解答:
for 循环可以取出 list 中每个元素(在本题中,我们定义 list 中的元素有十个:分别是自然数1至10。)。
参考代码如下:
def square_of_sum(L):
sum = 0
for x in L:
sum = sum + x * x
return sum
print (square_of_sum([1,2,3,4,5,6,7,8,9,10]))
测试结果:
Python函数之返回多值
Python函数 可以返回多个值吗?答案是肯定的。
我们举个例子,比如我们要求得一元二次方程 ax² + bx + c = 0 的两个解,那么我们编写一个函数的两个返回值就是方程式的两个解。
我们首先知道方程的求根公式为 x = (-b±√(b²-4ac)) / 2a 。
为了表示求根过程中的根号,我们可以使用 Python 的math包中的sqrt()函数,同时我们用import引用它,定义完函数以后为之赋值,我们就可以同时获得返回值:
import math
def quadratic_equation(a, b, c):
t = math.sqrt(b * b - 4 * a * c)
x = ( -b + t ) / (2 * a)
y = ( -b - t ) / (2 * a)
return x , y
m,n = quadratic_equation(2, 3, 0)
print(m,n)
测试结果如下:
但其实这只是一种假象,Python函数返回的仍然是单一值:
import math
def quadratic_equation(a, b, c):
t = math.sqrt(b * b - 4 * a * c)
x = ( -b + t ) / (2 * a)
y = ( -b - t ) / (2 * a)
return x , y
m = quadratic_equation(2, 3, 0)
print(m)
测试结果如下:
用 print 打印返回结果,原来返回值是一个 tuple 类型!
但是,在语法上,返回一个 tuple 可以省略括号,而多个变量可以同时接收一个 tuple,按位置赋给对应的值,所以Python的函数返回多值其实就是返回一个 tuple ,但写起来更方便。
Python之递归函数
我们知道其实在函数内部,也可以调用其他的函数。但是如果一个函数在内部调用函数自身,这个函数就是一个递归函数。
举个例子,我们来计算阶乘 n! = 1 * 2 * 3 * … * n,使用函数 fact(n)表示,可以看出:
fact(n) = n! = 1 * 2 * 3 * … * (n-1) * n = (n-1)! * n = fact(n-1) * n
所以,fact(n)可以表示为 n * fact(n-1),只有 n=1 时需要特殊处理。
于是,fact(n) 可以使用递归的方式表示出来,同时我们可以带入数据,检验编写函数的正确性:
def fact(n):
if n==1:
return 1
return n * fact(n - 1)
a = fact(5)
b = fact(10)
print(a)
print(b)
测试结果如下:
其实设想一下,当计算机在计算 fact(5) ,可以根据函数定义想象出函数计算过程如下:
fact(5) #此处代码块只是设想思路,并不是代码,无法运行
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120
递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
在这里我们拓展一些关于递归函数的知识(注意点):使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧;每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以递归调用的次数过多,会导致栈溢出。
下面我们来看一个非常经典的数学问题——汉诺塔问题。汉诺塔问题的具体来源和描述,我们可以参考以下网址:https://baike.baidu.com/item/%E6%B1%89%E8%AF%BA%E5%A1%94/3468295?fr=aladdin
我们假设对汉诺塔问题中的柱子分别编号为a, b, c,将所有圆盘从a移到c可以描述为:
1.如果a只有一个圆盘,可以直接移动到c;
2.如果a有N个圆盘,可以看成a有1个圆盘(底盘) + (N-1)个圆盘,首先需要把 (N-1) 个圆盘移动到 b,然后将 a的最后一个圆盘移动到c,再将b的(N-1)个圆盘移动到c。
请编写一个函数,给定输入 n, a, b, c,打印出移动的步骤:
move(n, a, b, c)
例如,输入 move(2, ‘A’, ‘B’, ‘C’),打印出:
A --> B
A --> C
B --> C
解答:
其实我们不难看出,汉诺塔的移动也可以看做是一个递归问题,同时函数 move(n, a, b, c) 的定义是将 n 个圆盘从 a 借助 b 移动到 c。
参考代码如下:
def move(n, a, b, c):
if n == 1:
print ( a, '-->', c )
return
move( n-1, a, c, b )
print ( a, '-->', c )
move( n-1, b, a, c )
x = move( 4, 'A', 'B', 'C' )
print(x)
测试结果如下:
Python之定义默认参数
在定义函数的时候,函数中还可以有默认的参数。
例如 Python 自带的 int() 函数其实就有两个参数,我们既可以传一个参数,又可以传两个参数(值得注意得是,当在 int() 函数中传入两个参数的时候,前者要将其表示为字符串):
a = int(100)
b = int('100',8)
print(a)
print(b)
测试结果如下:
int() 函数的第二个参数是转换进制,如果不传,默认是十进制 (base=10);如果传了,就用传入的参数。
由此可见,函数的默认参数的作用是简化调用,你只需要把必须的参数传进去即可完成操作。但是在需要的时候,又可以传入额外的参数来覆盖默认参数值。
我们假设来定义一个计算 x 的N次方的函数:
def power(x, n):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
a = power(3,3)
print(a)
测试结果:
假设我们计算平方的次数最多,我们就可以把 n 的默认值设定为 2 了,这样一来,计算平方就不需要传入两个参数了,而只需要传入以 2 为指数的 底数数值 就可以了:
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
a = power(3)
print(a)
测试结果如下:
这里值得注意的是,由于函数的参数按从左到右的顺序匹配,所以默认参数只能定义在必需参数的后面:
def name1(a, b=1, c=2):
pass # true
def name2(a=1, b,c):
pass #false
下面给出错误编写的测试结果:
Python之定义可变参数
如果我们想让一个函数能接受任意个参数,我们就可以定义一个可变参数的函数:
def fn(*args):
return args
a = fn()
b = fn('a')
c = fn('a',)
d = fn('a','b','c','d')
x = fn(1,2,3,4,5)
print(a)
print(b)
print(c)
print(d)
print(x)
测试结果:
可变参数的名字前面要加一个 * 号,在可变参数中,我们可以传入0个、1个或多个参数给可变参数。
可变参数也不是很神秘,Python解释器会把传入的一组参数组装成一个 tuple 传递给可变参数。因此在函数内部,直接把变量 args 看成一个 tuple 就好了。
这也就是为什么上文测试结果中,我们看到 b = fn(‘a’) 和 c = fn(‘a’,) 打印出来的结果是一样的原因,因为两者都是 tuple 类型。
事实上,我们定义 可变参数 的目的也是为了简化调用的。假设我们要计算任意个数的平均值,就可以定义一个如下的可变参数,这样设计可变参数函数,在调用的时候就可以简化输入( 可变参数 args 是一个tuple,当0个参数传入时,args是一个空tuple。 ):
def average(*args):
sum = 0.0
if len(args) == 0:
return sum
for x in args:
sum = sum + x
return sum / len(args)
print ( average() )
print ( average(3, 3, 3) )
print ( average(1, 2, 3) )
print ( average(1, 2, 3, 4, 5) )
最后的测试结果如下:
后记
到这里,本次的 Python 函数模块也就告一段落了,因为有段时间没有更新博文了,这次发博也是铆足了劲,想要好好地发一篇博文,所以这篇关于Python函数的文章也就比较长,还有一部分原因也是因为 函数这块知识比较多吧。
这段时间我会继续努力的,我们共同加油加油 !