一、函数的定义与调用
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数能提高应用的模块性和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。
- 1.1、函数的声明
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。
- 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
- return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
格式如下:
def 函数名 (形式参数列表): 执行语句代码 return 返回的值
- 1.2、函数的调用自定义的函数与前面我们调用Python内置函数的方法相同,即在语句中直接使用函数名,并在函数名之后的圆括号中传入参数,多个参数之间以半角逗号隔开。在调用函数时,实际传递给函数的参数被称为实际参数,简称:实参。
注意:调用时,即使不需要传入实际参数,也要带空括号。例如,我们很熟悉的print()。例如:
def myFunc (x,y): return x+y a,b = 3.8,7.0 print('%0.2f+%0.2f=%0.2f' % (a,b, myFunc(a,b))) 运行结果为:3.80+7.00=10.80
- 其中:x,y分别为形参,a,b分别为实参,在函数中经过计算,以函数名将值返回主调程序。
二、参数的传递
- 2.1、参数按位置依次传递
参见的顺序传递参数,如上面的 1.2 。 - 2.2、参数赋值和参数默认值传递
- 在调用函数时,也可在调用函数名后的圆括号内用“形参变量名=参数值”的方式传入参数,这方式不必按照定义的函数时原有的参数顺序。将上面的函数调用改为
myFunc(y=b,x=a)
如下:
def myFunc (x,y): return x+y a,b = 3.8,7.0 print('%0.2f+%0.2f=%0.2f' % (a,b, myFunc(y=b,x=a))) 运行结果为:3.80+7.00=10.80
- 参数默认值传递:如果没有传参数就用默认的参数,如下:
def myFunc (x,y=4): return x+y a,b = 3.8,7.0 print('%0.2f+默认值=%0.2f' % (a,b, myFunc(x=a))) print('%0.2f+%0.2f=%0.2f' % (a,b, myFunc(y=b,x=a))) 运行结果为:3.80+默认值=7.80 3.80+7.00=10.80
- 2.3、元组类型变长参数传递
使用可变长参数可让Python 的函数处理比初始化声明时更多的参数。在函数声明时,若在某个参数名称前面加一个星号:*
,则表示该参数是一个元组类型可变长参数。在调用该函数的时,依次将必须赋值完毕后,将继续依次从调用时所提供的参数元组中接收元素值为可变长参数赋值。如果在函数调用时没有提供元组类型参数,相当于提供了一个空元组,即不必传递可变长参数。例如:
def printse_series(d,*dtup): print('必须参数:',d) if len(dtup) != 0: print('元组参数:',end = '') for i in dtup: print(i,end=' ') print(printse_series(10)) print(printse_series(10,20,30,40,50)) 运行结果为: 必须参数: 10 None 必须参数: 10 元组参数:20 30 40 50 None
- 2.4、字典类型变长参数传递
在函数声明时,若在其某个参数名称前加两个星号:**
,则表示该参数是一个字典类型可变长参数。在调用该函数时,以实参变量名等于字典值的方式传递参数,由函数自动按字典值接收,实参变量名以字符形式作为字典的键。因此字典是无序的,因此字典的键值对也不分先后顺序。如果在函数调用时没有提供字典类型参数,则相当于提供了一个空子典,即不必传递可变长参数。
def printse_series(d,*dtup,**ddic): print('必须参数:',d) if len(dtup) != 0: print('元组:',end = '') for i in dtup: print(i,end=' ') if len(ddic) != 0: print('\n字典:',ddic) for k in ddic: print('%s 对应 %s' % (k,ddic[k])) printse_series(1,2,3,4,5,6,x=10,y=20,z=30) 运行的结果: 必须参数: 1 元组:2 3 4 5 6 字典: {'x': 10, 'y': 20, 'z': 30} x 对应 10 y 对应 20 z 对应 30
- 2.5、高阶函数
Python是面向对象的程序,对象名可以指向函数。高阶函数就是能够接受将函数对象名称作为参数传入的函数。应该注意的是,这里对象名称的类型是函数而不是字符串。例如:
def add(x,y): return x+y def subtract(x,y): return x-y def myfunc(x,y,f): # 形参 f 的类型为函数对象 return f(x,y) a,b = 5,2 method=add #注意 f 的类型为函数对象 print('%s: 参数1为 %d,参数2为 %d,结果为%d' % (method,a,b,myfunc(a,b,method))) method=subtract #注意 f 的类型为函数对象 print('%s: 参数1为 %d,参数2为 %d,结果为%d' % (method,a,b,myfunc(a,b,method))) 运行的结果为: <function add at 0x10253f1e0>: 参数1为 5,参数2为 2,结果为7 <function subtract at 0x102601268>: 参数1为 5,参数2为 2,结果为3
- 2.6、函数中变量的作用域
- 变量的作用域:是指程序中能够对该变量进行读/写操作的范围。根据作用域不同,变量分为函数中定义的变量(Local,简称L)、嵌套中父级函数的局部作用域变量(Enclosing,简称E),模块级别定义的全局变量(Global,简称G)和内置模块中的变量(Built-in,简称B)。
- 程序执行对变量的搜索和读/写时,优先由近及远,即:函数中定义的变量>嵌套中父级函数的局部作用域变量>模块级别定义的全局变量>内置模块中的变量,也就是LEGB。
- Python允许出现同名变量。若具有相同命名标识的变量出现在不同的函数体中,则各自代表不同的对象,既不互相干扰,也不能互相访问;若具有相同命名标识的变量在同一个函数体中或具有函数嵌套关系,则不同作用域的变量也各自代表不同的对象,程序执行时按优先级进行访问。例如:变量作用于测试
x = 0 #global def outer(): x = 1 # enclosing def inner(): x = 2 # local print('local: x =',x) inner() print('enclosing: x =',x) outer() print('global: x = ',x) 运行结果为: local: x = 2 enclosing: x = 1 global: x = 0
三、匿名函数
匿名函数: 就是没有实际名称的函数。Python 使用lambda来创建匿名函数。在lambda表达式中封装简单的逻辑,其主体仅是一个表达式而不需要使用代码。其通式:<函数对象名>= lambda <形式参数列表>:<表达式>
,例如:
def add(x,y): return x+y 上面可以定义为匿名函数: add1= lambda x,y: x+y 上面的调用方式如下: a,b = 2.5,3.6 sum = add1(a,b) 或者直接调用 (lambda x,y:x+y)(2.5,3.6) 其结果都是 6.1
匿名函数也可以嵌套条件分支,完成较为复杂的逻辑,例如返回x,y中较大的值
maxValue = lambda x,y: x if x>=y else y print('maxValue =',maxValue(2,3)) 结果为: maxValue = 3
四、函数的递归
递归:是一种直接或者间接调用函数自身的算法,其实质是把问题分解成规模较小的同类子问题然后递归调用表示问题的解。能够设计成递归算法的问题必须满足两个条件:能找到反复执行的过程(调用自身)和能找到反复执行过程的条件(递归出口)。例如: 设 n 为大于等于1的正整数,用函数递归的方法求阶乘 n!
,本例求解 5!
,因此 (n-1)! 可表示为(n-1)(n-2)!,因此可以设计成一个计算阶乘的函数 recursive(n)
,可调用自己并返回 n*recursive(n-1)
,代码如下:
def recursive(n): if n==1: return 1 else: return n*recursive(n-1) a = 5 print('%d! = %d' % (a,recursive(a))) 结果为:5! = 120
五、函数的高级应用
- 生成器:是能够按照解析表达式逐次产生出数据项元素的函数。在Python语言中,利用生成器,可不必创建完整的数据集合,从而节省存储的空间。
- 生成器函数与普通的函数的差别主要在于:生成器函数体中用
yield
关键字生成数据项,而不是用print输出数据项。生成器函数的调用循环遍历时,可以用__next__()
方法获取yield
生成数据项。 - 生成器函数与普通函数的执行流程不同。普通的函数时顺序执行的,遇到 return 语句或 最后一条语句就返回。而生成器函数在每次调用
__next__()
方法的时候才执行,遇到yield
语句返回,再次执行时不是从头开始,而是从上次返回的yield
语句处继续执行。例如:用生成器产生连续偶数序列并输出三次测试的结果。
def generatior_even(): for i in range(1,11): print('第%d步'%i) yield i*2 g = generatior_even() print(g.__next__()) # 此处连续执行三次,结果可见,并非每次从头开始 print(g.__next__()) # 而是从上次执行yield 语句处继续执行 print(g.__next__())