Python入门(七):函数

函数

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

函数定义

有时候并没有现成的函数满足我们的需求,这时候就需要自己定义函数:

  1. 函数以def关键词开头,后接函数名和圆括号
  2. 函数执行的代码以冒号起始,并且缩进
  3. return[表达式]结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回None
def functionname(parameters):
	"函数_文档字符串"
	function_suite
	return [expression]

其中,所指定的参数是一种占位符,且函数若不经过调用,则不会被执行。
当函数没有 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(函数名)查看函数类型。如下图所示:
在这里插入图片描述
: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. 函数返回

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

函数文档

  • 在函数定义中,为了便于理解,通常要给函数添加文档说明
  • 文档说明的添加方式为在函数内部以字符串的形式添加
  • 可以通过print(function.__doc__)help(function)命令查看函数文档
    在这里插入图片描述

函数参数

  • Python 的函数具有非常灵活多样的参数形态,即可以实现简单的调用,又可以传入非常复杂的参数。
  • 从简到繁的参数形态如下:
    1. 位置参数(positional argument)
    2. 默认参数(default argument)
    3. 可变参数(variable argument)
    4. 关键字参数(keyword argumen)
    5. 命名关键字参数(name keyword argument)
    6. 参数组合

位置参数

# arg1:位置参数,这些参数在调用函数(call function)时位置要固定
def functionname(arg1):
	"函数_文档字符串"
	function_suite
	return [expression]

默认参数

  • 函数的默认参数的作用是简化调用,只需传入必需参数即可调用函数。但是在需要的时候,又可以传入额外的参数来覆盖默认参数值
  • 由于函数的参数按从左到右的顺序匹配,所以默认参数只能定义在位置参数的后面,否则会报错
def functionname(arg1,arg2=v):
	"函数_文档字符串"
	function_suite
	return [expression]

:请定义一个 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 functionname(arg1,arg2=v,*args):
	"函数_文档字符串"
	function_suite
	return [expression]
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))

关键字参数

# **kw - 关键字参数,可以是从零个到任意个,自动组装成字典
def functionname(arg1,arg2=v,*args,**kw):
	"函数_文档字符串"
	function_suite
	return [expression]

可变参数关键字参数的异同总结如下

  • 可变参数允许传入零个到任意个参数,它们在函数调用时自动组装成一个元组
  • 关键字参数允许传入零个到任意个参数,它们在函数内部自动组装为一个字典
    在这里插入图片描述

命名关键字参数

# *,nkw - 命名关键字参数,用户想要输入的关键字参数,定义方式是在 nkw 前面加个分隔符 *,  
# 如果不加 * ,则定义的是位置参数
# 如果要限制关键字参数的名字,就可以用 命名关键字参数
# 使用命名关键字参数时,要特别注意不能缺少参数名
def functionname(arg1,arg2,*args,*,nkw,**kw):
	"函数_文档字符串"
	function_suite
	return [expression]

在这里插入图片描述

参数组合

  • 在 Python 中定义函数,可以用位置参数、默认参数、可变参数、命名关键字参数和关键字参数,这5种参数中的4个都可以一起使用,但要注意,参数的定义顺序必须是:
    • 位置参数、默认参数、可变参数和关键字参数
    • 位置参数、默认参数、命名关键字参数和关键字参数
  • 警告:虽然可以组合多达5种参数,但不要同时使用太多的组合,否则函数不易理解

参数传递方式

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

# 名称传递
<函数名>(<参数名> = <实际值>)
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 声明。
在这里插入图片描述

使用规则

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

闭包

  • 闭包是函数式编程的一个重要的语法结构,是一种特殊的内嵌函数
  • 如果在一个内部函数里对外层非全局作用域的变量进行引用,那么内部函数就被认为是闭包
  • 通过闭包可以访问外层非全局作用域的变量,这个作用域称为闭包作用域
  • 闭包的返回值通常是函数
    在这里插入图片描述
  • 如果要修改闭包作用域中的变量则需要nonlocal关键字
    在这里插入图片描述

函数式编程

  • 函数式编程是指代码中每一块都是不可变的,都由纯函数的形式组成
  • 纯函数,是指函数本身相互独立、互不影响,对于相同的输入,总会有相同的输出,没有任何副作用

下面我们通过例子看看函数式编程和非函数式编程的具体区别
在这里插入图片描述
在这里插入图片描述

模块化设计

  • 程序由一系列代码组成,如果代码是顺序但无组织的,不仅可读性差,而且维护和升级的难度大。

  • 解决这个问题最好的方法,就是如日本马拉松某名将所说,将路程(程序)分成若干个小路程(小程序),每个程序段完成一部分特定的功能。使用函数对程序合理划分为功能模块,并基于模块设计程序是一种常用方法,称为“模块化设计”。

  • 模块化设计指通过函数的封装功能将程序划分成主程序、子程序和子程间关系的表达。模块化设计是使用函数设计程序的思考方法,以功能块为基本单位,一般有两个基本要求:

    • 紧耦合:尽可能合理划分功能块,功能块内部耦合度高
    • 松耦合:模块间关系尽可能简单,功能块之间耦合度低
  • 耦合性指程序结构中各模块之间相互关联的程度,它取决于各模块间接口的复杂程度和调用方式。耦合性是影响软件复杂程度和设计质量的一个重要因素。

  • 紧耦合指模块或系统间关系紧密,存在较多和复杂的相互调用,松耦合相反。紧耦合的缺点在于更新一个模块可能导致其他模块变化,代码复用较困难。松耦合一般基于消息或协议实现,系统间交互简单。松耦合代表了模块化,从系统观点来看,松耦合是总体设计原则

  • 需要注意的是,使用函数只是模块化设计的必要非充分条件,根据计算需求合理划分即可。一般来说,完成特定功能或经常复用的语句组应用函数封装,并尽可能减少函数间参数和返回值的数量。

lambda函数

  • lambda 函数是一种匿名函数,使用 lambda 保留字定义,函数名即是返回结果,通常用于定义简单的,能在一行内表示的函数。
  • lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数
    在这里插入图片描述
    在这里插入图片描述
  • 匿名函数常常应用于函数式编程的高阶函数(high-order function)中,主要有两种形式:
    • 参数是函数(filter,map)
    • 返回值是函数(closure)

filter(function,iterable):过滤序列函数,过滤掉不符合条件的元素,返回一个迭代器对象
在这里插入图片描述
map(function,*iterables)根据提供的函数对指定序列做映射
在这里插入图片描述
除了 Python 这些内置函数,我们也可以自己定义高阶函数
在这里插入图片描述

函数递归

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

例2:将下列式子用 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:斐波那契数列
在这里插入图片描述

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)
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值