Python入门(七):函数

函数

函数是一段具有特定功能的、可重用的语句组,通过函数名来表示和调用。
函数包括两部分:函数的定义和函数的使用
函数主要有两个作用:降低编程难度增加代码复用。函数是一种功能抽象,利用它可以将一个大问题拆分成若干个小问题,分而治之,为每个小问题编写程序,通过函数封装,使得大问题得以解决;函数可以在一个程序的多个位置使用,也可以用于多个程序,当代码需要修改时,只需在函数中修改一次就可使得所用调用位置的功能得以改变,这种代码复用降低了代码维护难度。

函数定义

有时候并没有现成的函数满足我们的需求,这时候就需要自己定义函数
定义函数需使用 def 语句,依次写出函数名、括号、括号中的参数和冒号:,然后在代码块中编写函数体,函数的返回值用 return 语句表示。

def <函数名>(<参数列表>):
	<函数体>
	return <返回值列表>

其中,所指定的参数是一种占位符,且函数若不经过调用,则不会被执行。
当函数没有 return 时,仅表示执行一段代码功能。
IPO:参数(Input) 函数体(Process) 结果(Output)
:定义一个 square_of_sum 函数,它接受一个list,返回list中每个元素平方的和。

def square_of_sum(L):
    sum = 0
    for x in L:
        sum = sum + x*x
    return sum

函数使用

函数使用需要了解该函数的名称及参数设置,内置函数可查阅相关文档,此处为官方文档 http://docs.python.org/2/library/functions.html#abs ,还可以输入命令help(函数名)查看帮助。此外,可通过type(函数名)查看函数类型。如下图所示:
Python入门(七):函数
:sum()函数接受一个list作为参数,并返回list所有元素之和。请计算 11 + 22 + 33 + … + 100100。

L = [ ]
x = 1
while x <= 100:
	L.append(x*x)
	x += 1
print(sum(L))

函数可以返回多个值,但实质上返回的是一个元组类型的数据集合
:请编写一个函数,返回一元二次方程 ax² + bx + c = 0 的两个解。

import math
def quadratic_equation(a, b, c):
    t = math.sqrt(b**2-4*a*c)
    return (-b+t)/(2*a),(-b-t)/(2*a)
print(quadratic_equation(2, 3, 0))返回

函数也可以有多个返回语句,用于不同情况下结果的输出。

def m(x):
    try:
        if x>0:
            return x+1
        else:
            return x-1
    except:
        return 0

另外,Python 语言最小函数可以不表达任何功能,如下:

def f():
	pass

其中,保留字 pass 表示不进行任何操作,仅起到占位的作用,因为函数体内部总要编写一段代码。对 f() 的调用不实现任何功能。
具体来说,函数使用共分为4个步骤:

  1. 函数定义
  2. 函数调用
  3. 函数执行
  4. 函数返回

需要注意的是,函数定义不一定放在调用之前,如下图所示:
Python入门(七):函数
编程中大量使用函数已经成为一种编程范式,叫做函数式编程。函数式编程的主要思想是把程序过程尽量写成一系列函数调用,这能够使代码编写更简洁、更易于理解,是中小规模软件项目中最常用的编程方式。

函数参数传递

函数的参数在定义时可以指定默认值,当函数被调用时,如果没有传入对应的参数值,则用默认值替代,函数定义时的具体语法形式如下:

def <函数名>(<非可选参数列表>,<可选参数> = <默认值>):
	<函数体>
	return <返回值列表>

定义默认参数

函数的默认参数的作用是简化调用,只需传入必需参数即可调用函数。但是在需要的时候,又可以传入额外的参数来覆盖默认参数值。
由于函数的参数按从左到右的顺序匹配,所以默认参数只能定义在必需参数的后面
:请定义一个 greet() 函数,它包含一个默认参数,如果没有传入,打印 ‘Hello, world.’,如果传入,打印 ‘Hello, xxx.’

def greet(name='world'):
    print ('hello,'+ name +'.')
greet()
greet('Bart')

:计算 n!//m 的值。

def fact(n,m=2):
    s = 1
    for i in range(1,n+1):
        s *= i
    return s//m
fact(5)    #60
fact(5,4)    #30

定义可变参数

如果想让一个函数能接受任意个参数,我们就可以定义一个可变参数:
Python解释器会把传入的一组参数组装成一个元组传递给可变参数,因此,在函数内部,直接把变量看成一个 tuple 就好了。

def fact(n,*b):
    s = 1
    for i in range(1,n+1):
        s *= i
    for item in b:
        s *= item
    return s
fact(3,9)    #3!×9
fact(3,9,2,1)    #3!×9×2×1

:请编写接受可变参数的 average() 函数。

def average(*y):
    sum = 0.0
    if len(y) == 0:
        return sum
    for x in y:
        sum = sum + x
    return sum/len(y)
print(average(1, 2, 2, 3, 4))

参数传递方式

在函数中,参数传递方式有两种:位置传递名称传递。函数调用时,默认采用按照位置顺序的方式传递给函数,即位置传递。若想使用名称传递,则指定参数的值即可,格式如下:

# 名称传递
<函数名>(<参数名> = <实际值>)
def fact(n,m):
    s = 1
    for i in range(1,n+1):
        s *= i
    return s//m
fact(9,3)  #位置传递
fact(m=3,n=9) #名称传递

变量作用域

根据程序中变量所在的位置和作用范围,变量分为局部变量和全局变量。

局部变量

局部变量指在函数内部定义的变量,仅在函数内部有效,当函数退出时变量不再存在。
Python入门(七):函数

全局变量

全局变量指在函数之外定义的变量,在程序执行全过程有效。全局变量在函数内部使用时,需要提前使用保留字 global 声明。
Python入门(七):函数

使用规则

Python入门(七):函数
Python入门(七):函数
Python入门(七):函数
Python入门(七):函数
Python入门(七):函数

模块化设计

程序由一系列代码组成,如果代码是顺序但无组织的,不仅可读性差,而且维护和升级的难度大。解决这个问题最好的方法,就是如日本马拉松某名将所说,将路程(程序)分成若干个小路程(小程序),每个程序段完成一部分特定的功能。使用函数对程序合理划分为功能模块,并基于模块设计程序是一种常用方法,称为“模块化设计”。
模块化设计指通过函数的封装功能将程序划分成主程序、子程序和子程间关系的表达。模块化设计是使用函数设计程序的思考方法,以功能块为基本单位,一般有两个基本要求:

  • 紧耦合:尽可能合理划分功能块,功能块内部耦合度高
  • 松耦合:模块间关系尽可能简单,功能块之间耦合度低

耦合性指程序结构中各模块之间相互关联的程度,它取决于各模块间接口的复杂程度和调用方式。耦合性是影响软件复杂程度和设计质量的一个重要因素。
紧耦合指模块或系统间关系紧密,存在较多和复杂的相互调用,松耦合相反。紧耦合的缺点在于更新一个模块可能导致其他模块变化,代码复用较困难。松耦合一般基于消息或协议实现,系统间交互简单。松耦合代表了模块化,从系统观点来看,松耦合是总体设计原则
需要注意的是,使用函数只是模块化设计的必要非充分条件,根据计算需求合理划分即可。一般来说,完成特定功能或经常复用的语句组应用函数封装,并尽可能减少函数间参数和返回值的数量。

lambda函数

lambda 函数是一种匿名函数,使用 lambda 保留字定义,函数名即是返回结果,通常用于定义简单的,能在一行内表示的函数。此外 lambda 函数主要用作一些特定函数或方法的参数,有一些固定使用方式。
Python入门(七):函数
Python入门(七):函数

函数递归

同数学中的定义相同,函数递归就是函数定义中调用函数本身的方式。
如果一个函数在内部调用自身,这个函数就是递归函数
在函数递归中,有两个关键特征:链条基例
同数学中的数学归纳法的原理相同,递归是数学归纳法思维的编程体现。
递归函数定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
例1:计算阶乘 n! = 1 * 2 * 3 * … * n

def fact(n):
	if n == 1:
		return 1
	else:
		return n*fact(n-1)

例2:将下列式子用 Python 程序表示。
Python入门(七):函数

def fact(n):
    if n==0:
        return 1
    else:
        return n*fact(n-1)

例3:字符串反转
除了我们在前文字符串操作中提到的一个直接命令[::-1]外,我们可以用函数来实现

def rvs(s):
    if s == "":
        return s
    else:
        return rvs(s[1:])+s[0]

例4:斐波那契数列
Python入门(七):函数

def f(n):
    if n==1 or n==2:
        return 1
    else:
        return f(n-1)+f(n-2)  

例5:汉诺塔问题

count = 0
def hanoi(n,src,dst,mid): #src:原始位置 dst:目标位置 mid:中间位置 
    global count
    if n == 1:
        print("{}:{}->{}".format(n,src,dst))
        count += 1
    else:
        hanoi(n-1,src,mid,dst)
        print("{}:{}->{}".format(n,src,dst))
        count += 1
        hanoi(n-1,mid,dst,src)
hanoi(3,"A","C","B")
print(count)