函数是可重用的程序代码段,用于在程序中分离不同的任务,使用函数可以提高编程效率。
在Python程序开发过程中,将完成某一特定功能并经常使用的代码编写成函数,放在函数库(模块)中,在需要使用时直接调用,这就是程序中的函数。
合理的函数规划不但可以使程序容易编写、阅读、调试、修改与维护,还充分体现着程序开发人员解决问题思路的清晰性、严密性。
在Python语言中,通常包括内置函数、标准库函数、第三方库函数及用户自定义函数。
📢📢📢📢📢📢 精彩开始,come on 📢📢📢📢📢📢
🚩函数的理解与定义
👍函数的定义
函数是一段具有特定功能的、可重用的语句组,用函数名来表示并通过函数名进行完成功能调用
函数是一种功能的抽象,一般函数表达特定功能。两个作用:降低编程难度 和 代码复用
函数定义的基本形式如下:
def 函数名 (形式参数列表): # 形式参数可以有0到多个
函数体
return 表达式或数值 # 不是所有函数都必须该语句
函数定义的第一行称为函数签名,函数签名由关键字def开始,并指定函数名称以及函数的每个形式参数变量名称。
注意:
使用def关键字进行函数的定义,不用指定函数返回值的类型。定义函数时,可以声明函数的参数,形式参数的个数可以是多个,也可以没有,多个形式参数之间用逗号隔开。同样地,函数参数也不用指定参数类型。
函数体是复合语句,函数体语句需要采用缩进书写规则。
如果函数有返回值,返回值是通过函数体中的return语句获得的,return语句可以在在函数体内任何地方出现,表示函数调用执行到此结束;如果没有return语句,会自动返回空值(None),如果有return语句但return后面没有接表达式或者值得话也是返回None值。
【例子】求取包含10个斐波那契数
如何让代码按照用户指定的最终要求取得序列的长度?
或许还可以让程序更加抽象
函数定义如下:
给函数编写文档
👍函数嵌套定义
Python允许在函数内部定义函数,示例代码如下:
>>> def add(a,b):
... def getsum(x): #在函数内部定义的函数,将字符串转换为Unicode码求和
... s=0
... for n in x:
... s+=ord(n)
... return s
... return getsum(a)+getsum(b) #调用内部定义的函数getsum
...
>>> add('12','34') #调用函数
202
【注意】
内部函数只能在函数内部使用。
👍函数的调用
当调用一个函数时,程序的控制权会转移到被调用的函数中,执行被调用函数中的语句实现具体的功能。但执行完该被调函数,程序的控制权又回到调用函数中。
调用函数时,可以根据需要指定实际传入的参数值。函数的调用语法格式如下:
函数名 (实际参数列表)
程序调用一个函数需要执行以下四个步骤:
注意:
函数名是当前作用域中已经存在的函数,存在的函数可以是内置函数、标准库函数或用户自定义的函数。另外,调用函数应该位于定义函数之后,否则程序会报错。
实际参数列表必须与函数定义的形式参数列表一一对应。
函数调用是表达式。如果函数有返回值,可以在表达式中直接使用;如果函数没有返回值,则可以单独作为表达式语句使用。
在Python中,所有的语句都是解释执行的,不存在如C/C++中的编译过程。
Python中函数的调用必须出现在函数的定义之后。
在Python中,函数也是对象(function对象)。
def语句在执行时会创建一个函数对象。
函数名是一个变量,它引用def语句创建的函数对象。
可将函数名赋值给变量,使变量引用同一个函数。
>>> def add(a,b): #定义函数
... return a+b
...
>>> add #直接用函数名,可返回函数对象的内存地址
0x00D41078>
>>> add(10,20) #调用函数
30
>>> x=add #将函数名赋值给变量
>>> x(1,2) #通过变量调用函数
3
🚩参数传递与返回值
👍参数传递
💗函数的参数
在函数定义的参数表中的参数称为形式参数,简称形参。——在def语句中,位于函数名后面的变量通常称为形参
调用函数时,参数表中提供的参数称为实际参数,简称实参。——调用函数时提供的值称为实参
实参可以是常量、表达式或变量。
实参是常量或表达式时,直接将常量或表达式计算结果传递给形参。
在Python中,变量保存的是对象的引用,实参为变量时,参数传递会将实参对对象的引用赋值给形参。
位置参数 和 关键字参数
在结合使用位置参数和关键字参数时,必须先指定所有位置参数
def printParams(*params):
print(params)
def printParams2(**params):
print(params)
💗参数的多态性
多态是面向对象的一个特点,指不同对象执行同一个行为可能会得到不同的结果。
同一个函数,传递的实际参数类型不同时,可获得不同的结果,体现了多态性。
>>> def add(a,b):
... return a+b #两个参数执行加法运算
...
>>> add(1,2) #执行数字加法
3
>>> add('abc','def') #执行字符串连接
'abcdef'
>>> add((1,2),(3,4)) #执行元组合并
(1, 2, 3, 4)
>>> add([1,2],[3,4]) #执行列表合并
[1, 2, 3, 4]
💗参数赋值传递
调用函数时会按参数的先后顺序,依次将实参传递给形参。
【例如】调用add(1,2)时,1传递给a,2传递给b。
Python允许以形参赋值的方式,将实参传递给指定形参。
>>> def add(a,b):
... return a+b
...
>>> add(a='ab',b='cd') #通过赋值来传递参数
'abcd'
>>> add(b='ab',a='cd') #通过赋值来传递参数
'cdab'
采用参数赋值传递时,因为指明了形参名称,所以参数的先后顺序已无关紧要。
参数赋值传递的方式称为关键字传递。
💗参数传递与共享引用
>>> def f(x):
... x=100
...
>>> a=10
>>> f(a)
>>> a
10
从结果可以看出,将实参a传递给形参x后,在函数中重新赋值x,并不会影响到实参a。
这是因为Python中的赋值是建立变量到对象的引用。重新赋值时,意味着形参引用了新的对象。
💗传递可变对象的引用
🍊实参引用的是可变对象时,如列表、字典等,若在函数中修改形参,通过共享引用,实参也获得修改后的对象。
示例代码如下:
>>> def f(a):
... a[0]='abc' #修改列表第一个值
...
>>> x=[1,2]
>>> f(x) #调用函数,传递列表对象的引用
>>> x #变量x引用的列表对象在函数中被修改
['abc', 2]
🍊如果不希望函数中的修改影响函数外的数据,应注意避免传递可变对象的引用。
🍊如果要避免列表在函数中被修改,可使用列表的拷贝作为实参。
示例代码如下:
>>> def f(a):
... a[0]='abc' #修改列表第一个值
...
>>> x=[1,2]
>>> f(x[:]) #传递列表的拷贝
>>> x #结果显示原列表不变
[1, 2]
🍊还可以在函数内对列表进行拷贝,调用函数时实参仍使用变量。
示例代码如下:
>>> def f(a):
... a=a[:] #拷贝列表
... a[0]='abc' #修改列表的拷贝
...
>>> x=[1,2]
>>> f(x) #调用函数
>>> x #结果显示原列表不变
[1, 2]
💗有默认值的可选参数
在定义函数时,可以为参数设置默认值。
调用函数时如果未提供实参,则形参取默认值,示例代码如下:
>>> def add(a,b=-100): #参数b默认值为-100
... return a+b
...
>>> add(1,2) #传递指定参数
3
>>> add(1) #形参b取默认值
-99
【注意】
带默认值的参数为可选参数,在定义函数时,应放在参数表的末尾。
💗接受任意个数的参数
在定义函数时,如果在参数名前面使用星号“*”,表示形参是一个元组,可接受任意个数的参数。
调用函数时,可以不为带星号的形参提供数据。
示例代码如下:
>>> def add(a,*b):
... s=a
... for x in b: #用循环迭代元组b中的对象
... s+=x #累加
... return s #返回累加结果
...
>>> add(1) #不为带星号的形参提供数据,此时形参b为空元组
1
>>> add(1,2) #求两个数的和,此时形参b为元组(2,)
3
>>> add(1,2,3) #求3个数的和,此时形参b为元组(2,3)
6
>>> add(1,2,3,4,5) #求5个数的和,此时形参b为元组(2,3,4,5)
15
💗必须通过赋值传递的参数
Python允许使用必须通过赋值传递的参数。
🍊在定义函数时,带星号参数之后的参数必须通过赋值传递。
示例代码如下:
>>> def add(a,*b,c):
... s=a+c
... for x in b:
... s+=x
... return s
...
>>> add(1,2,3) #形参c未使用赋值传递,出错
Traceback (most recent call last):
File "", line 1, in
TypeError: add() missing 1 required keyword-only argument: 'c'
>>> add(1,2,c=3) #形参c使用赋值传递
6
>>> add(1,c=3) #带星号参数可以省略
4
🍊在定义函数时,也可单独使用星号,其后的参数必须通过赋值传递。
示例代码如下:
>>> def f(a,*,b,c): #参数b和c必需通过赋值传递
... return a+b+c
...
>>> f(1,b=2,c=3)
6
👍返回值
函数可以返回0个或多个结果
函数使用return语句带回返回值,该返回值由函数名带回,并结束函数的执行。不论return语句出现在函数的什么位置,一旦得到执行将直接结束函数的执行。
如果函数没有return语句或者执行了不返回任何值的return语句,Python将认为该函数以return None结束,即返回空值。
🚩作用域
变量起作用的范围称为变量的作用域,一个变量在函数外部定义和在函数内部定义,其作用域是不同的,不同作用域内同名变量之间互不影响。
变量声明的位置不同,其可以被访问的范围也不同。一个变量在函数外部定义和在函数内部定义,其作用域是不同的。
函数内部定义的变量一般为局部变量,而不属于任何函数的变量一般为全局变量。
一般而言,局部变量的引用比全局变量速度快,应优先考虑使用。
👍作用域分类
Python中变量的作用域可分为4种:内置作用域、文件作用域、函数嵌套作用域和本地作用域,如图所示:
本地作用域:没有内部函数时,函数体为本地作用域。函数内通过赋值创建的变量、函数参数都属于本地作用域。
函数嵌套作用域:包含内部函数时,函数体为函数嵌套作用域。
文件作用域:程序文件(也称模块文件)的内部为文件作用域。
内置作用域:Python运行时的环境为内置作用域,它包含了Python的各种预定义变量和函数。
内置作用域和文件作用域可称为全局作用域。
根据作用域的范围大小,作用域外部的变量和函数可以在作用域内使用;相反,作用域内的变量和函数不能在作用域外使用。
根据作用域范围,通常将变量名分为两种:全局变量和本地变量。
在内置作用域和文件作用域中定义的变量和函数都属于全局变量。
在函数嵌套作用域和本地作用域内定义的变量和函数都属于本地变量,本地变量也可称为局部变量。
👍全局变量
全局变量,它是在函数外部定义的,作用域是整个程序。全局变量可以直接在函数里使用,在函数内部定义的全局变量当函数结束以后仍然存在并且可以访问,但是如果要在函数内部改变全局变量值,必须使用global关键字进行声明。
👍局部变量
在函数内定义的变量只在该函数内起作用,称为局部变量,局部变量的作用域为函数体内,函数运行结束时,在该函数内部定义的局部变量会被自动删除。它们与函数外具有相同名的其他变量没有任何关系,即变量名称对于函数来说是局部的。
👍规则1: 局部变量和全局变量是不同变量
当函数执行完退出后,其内部变量将被释放。
函数func()内部使用了变量n,并且将变量参数b赋值给变量n
如果希望让func()函数将n当作全局变量,需要在变量n使用前显式声明该变量为全局变量,代码如下:
👍规则2: 局部变量为组合数据类型且未创建新变量,等同于全局变量
👍总结
基本数据类型,无论是否重名,局部变量与全局变量不同
可以通过global保留字在函数内部声明全局变量
组合数据类型,如果局部变量未真实创建,则是全局变量
除非真的有必要,否则应尽量避免使用全局变量,因为全局变量会增加不同函数之间的隐式耦合度,从而降低代码可读性,并使得代码测试和纠错变得很困难。
如果用特殊的关键字定义一个变量,也会改变其作用域。变量按其作用域的不同大致可以分为:全局变量、局部变量和类型成员变量。
🚩lambda函数
在Python中有2种函数,一种是用def定义;另一种是Lambda函数,也叫Lambda表达式,可以用来声明匿名函数,即没有函数名字的临时使用的小函数。
lambda函数也称表达式函数,用于定义匿名函数。可将lambda函数赋值给变量,通过变量调用函数。
🍊lambda函数定义的基本格式如下:
lambda参数表:表达式
lambda arg1 , arg2 , … :
其中
arg1、agr2……为函数的参数
为函数的语句,其结果为函数的返回值
Lambda表达式只可以包含一个表达式,不允许包含其他复杂的语句,但在表达式中可以调用其他函数,并支持默认值参数和关键字参数,该表达式的计算结果就是函数的返回值。
匿名函数(lambda)一般应用于函数式编程中,可以将Lambda表达式作为列表的元素,从而实现跳转表的功能,也就是函数的列表。
🍊Lambda表达式列表的定义方法为:
列表名=[(Lambda表达式1),(Lambda表达式2),……]
🍊调用列表中Lambda表达式的方法为:
列表名[索引](Lambda表达式的参数列表)
示例代码如下:
>>> add=lambda a,b:a+b #定义表达式函数,赋值给变量
>>> add(1,2) #函数调用格式不变
3
>>> add('ab','ad')
'abad'
lambda函数非常适合定义简单的函数。
与def不同,lambda的函数体只能是一个表达式 可在表达式中调用其他的函数,但不能使用其他的语句。
示例代码:
>>> add=lambda a,b:ord(a)+ord(b) #在lambda表达式中调用其他的函数
>>> add('1','2')
99
>>> map(lambda x: x ** 2, [1, 2, 3, 4, 5]) # 使用 lambda
[1, 4, 9, 16, 25]
# 提供了两个列表,对相同位置的列表数据进行相加
>>> map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
[3, 7, 11, 15, 19]
>>> print(''.join(map(lambda x: chr((ord(x) + 3 - ord('A')) % 26 + ord('A')) if 'A' <= x <= 'Z' else x, input("please input str1: "))))
please input str1: str
str
>>>
🚩函数递归
👍函数递归的理解
函数作为一种代码封装,可以被其他程序调用,当然,也可以被函数内部代码调用。这种函数定义中调用函数自身的方式称为递归。
递归在数学和计算机应用上非常强大,能够非常简洁的解决重要问题。
递归函数是指在函数体内调用函数本身。
数学上有个经典的递归例子叫阶乘,阶乘通常定义为:
n! = n (n-1)(n-2)......
这个关系给出了另一种方式表达阶乘的方式
>>> def fac(n): #定义函数
... if n==0: #递归调用的终止条件
... return 1
... else:
... return n*fac(n-1) #递归调用函数本身
...
>>> fac(5)
120
【注意】
递归函数必须在函数体中设置递归调用的终止条件。
如果没有设置递归调用终止条件,程序会在超过Python允许的最大递归调用深度后,产生RecursionError异常(递归调用错误)。
👍函数递归的调用过程
阶乘的例子揭示了递归的2个关键特征:
(1)基线条件:存在一个或多个基例,基例不需要再次递归,它是确定的表达式;
(2)递归条件:包含一个或多个调用,所有递归链要以一个或多个基例结尾。
👍函数递归实例解析
🍊字符串反转
将字符串s反转后输出
>>> s[::-1]
def rvs(s):
if s == "" :
return s
else :
return rvs(s[1:])+s[0]
🍊斐波那契数列
F(n) = F(n-1) + F(n-2)
def fibs(n):
if n == 0 or n == 1 :
return 1
else :
return fibs(n-1) + fibs(n-2)
🍊二分查找
1.如果序列索引最大值与最小值相等,判断是否为要查找的数值。
2.如果上下限不同,判断数值在上下限平均值的哪一侧,再做查找。
👍函数式编程
函数式编程(英语:functional programming)或称函数程序设计,又称泛函编程,是一种编程范型。
它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。
函数编程语言最重要的基础是λ演算,而且λ演算的函数可以接受函数当作输入(引数)和输出(传出值)。
函数式编程关心数据的映射,命令式编程关心解决问题的步骤。
函数式编程是一种编程模型,他将计算机运算看做是数学中函数的计算,并且避免了状态以及变量的概念。
Python提供了一些有助于进行函数式编程的函数:map、filter、reduce
🍊map函数
map() 函数语法:
map(function, iterable, ...)
🍊filter函数
filter() 函数语法:
filter(function, iterable)
[1, 3, 5, 7, 9]
🍊reduce函数
reduce () 函数语法:
reduce(function, iterable[, initializer])