目录
10.【Python编程】函数的定义、调用、参数传递及其应用
备注: 本教程主要使用Python3.6在jupyter notebook上编程实现。Python环境配置参考《【Python学习】Windows10开始你的Anaconda安装与Python环境管理》或者《【Python学习】纯终端命令开始你的Anaconda安装与Python环境管理》。
10.1 函数的定义与调用
数学上的定义
在数学上,函数被定义为:给定一个数集A
,假设其中的元素为x
,对A
中的元素x
施加对应法则f
,记作f(x)
,得到另一数集B
,假设B
中的元素为y
,则y
与x
之间的等量关系可以用y=f(x)
表示。
函数概念含有三个要素:定义域A、值域B和对应法则f。其中核心是对应法则f,它是函数关系的本质特征。
在我们初中、高中当中,出现的一元一次函数、一元二次函数、三角函数、指数函数、对数函数,便是如此。
编程上的定义
而在计算机科学里面,函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数能提高应用的模块性,和代码的重复利用率。
无论是数学上还是计算机科学上,函数都包含了输入(自变量)、处理代码(处理法则)、输出(因变量)。
在python当中,你可以定义一个由自己想要功能的函数,以下是简单的规则:
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
- 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号 : 起始,并且缩进。
- return [表达式] 结束函数,选择性地返回一个值给调用方,不带表达式的 return 相当于返回 None。
- 默认情况下,参数值和参数名称是按函数声明中定义的顺序匹配起来的。
它的格式为:
def 函数名(参数列表):
函数体
return 返回结果
例如:
- 使用python实现数学上的一元函数
# y = 2x+1
def f1(x):
y = 2*x + 1
return y
# 尝试使用该函数计算当x=5时的y值:
print("f1(5)=", f1(5)) # 调用f1函数
f1(5)= 11
- 使用python实现数学上的一元二次函数
# y=2x^2+3x-9
def f2(x):
y=2*x**2+3*x-9
return y
# 尝试使用该函数计算当x=5时的y值:
print("f2(5)=", f2(5)) # 调用f2函数
f2(5)= 56
- 使用python实现输入半径计算圆的面积、圆的周长
import math
# 计算圆的面积
def cal_area_of_circle(r):
s = math.pi*r*r
return s
# 计算圆的周长
def cal_circumference_of_circle(r):
c = 2*math.pi*r
return c
# 测试函数
r = float(input("请输入圆的半径(cm):"))
print("圆的半径是:", r)
print("圆的面积是:", cal_area_of_circle(r))
print("圆的周长是:", cal_circumference_of_circle(r))
请输入圆的半径(cm):5
圆的半径是: 5.0
圆的面积是: 78.53981633974483
圆的周长是: 31.41592653589793
10.2 参数及参数传递
在python当中,函数的参数,严格来说,是没有类型的,也就是,可以传给函数的参数任意类型的对象。通常可以约束要求传入的对象的类型,或者在函数内部判断对象的类型。
在 python 中,类型属于对象,变量是没有类型的。Python3 的六个标准数据类型中:
- 不可变类型(3 个):Number(数字)、String(字符串)、Tuple(元组)
- 可变类型(3 个):List(列表)、Dictionary(字典)、Set(集合)
解释:python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。
python 传不可变对象实例
- 不可变类型:类似于 C++ 的值传递,变量赋值 x=5 后再赋值 x=10,这里实际是新生成一个 int 值对象 10,再让 x 指向它,而 5 被丢弃,不是改变 x 的值,相当于新生成了 x。
在调用函数前后,形参和实参指向的是同一个对象(对象 id 相同),在函数内部修改形参后,形参指向的是不同的 id。
# 验证不可变类型变量的赋值
x = 5
id1 = id(x)
print(id1)
x = 10
id2 = id(x)
print(id2)
print(id1 == id2)
94347800181568
94347800181728
False
# python传不可变对象,在函数内修改,并不会影响函数外的对象值
def change(x):
# 函数内部的符号x表示形参
print("函数内部的形参x的id=", id(x)) # 形参和实参指向的是同一个对象
x=2022
print("函数内部重新赋值后的形参x的id=", id(x)) # 形参指向一个新的对象
print("-----------当x为数字类型时------------------")
x=2021
print("x的类型为:",type(x))
print("实参x的取值=", x)
print("传入函数的实参x的id=", id(x))
change(x)
print("实参x的取值=", x)
-----------当x为数字类型时------------------
x的类型为: <class 'int'>
实参x的取值= 2021
传入函数的实参x的id= 140642597090288
函数内部的形参x的id= 140642597090288
函数内部重新赋值后的形参x的id= 140642597089840
实参x的取值= 2021
python 传可变对象实例
- 可变类型:类似于 C++ 的引用传递,变量赋值 list1=[1,2,3,4] 后再赋值 list1[2]=5 则是将 list1 的第三个元素值更改,本身list1没有动,只是其内部的一部分值被修改了。
# 验证可变类型变量的赋值
list1=[1,2,3,4]
id3 = id(list1)
print(id3)
list1[2] = 5
id4 = id(list1)
print(id4)
print(id3 == id4)
140642597025800
140642597025800
True
# python传可变对象,在函数内修改,会影响函数外的对象值
def change(x):
# 函数内部的符号x表示形参
print("函数内部的形参x的id=", id(x)) # 形参和实参指向的是同一个对象
x.append(2022) # 传入函数的和在列表末尾添加新内容的对象用的是同一个引用。
print("函数内部修改列表后的形参x的id=", id(x)) # 形参指向一个新的对象
print("-----------当x为列表类型时------------------")
x=[2019,2020,2021]
print("x的类型为:",type(x))
print("实参x的取值=", x)
print("传入函数的实参x的id=", id(x))
change(x)
print("实参x的取值=", x)
-----------当x为列表类型时------------------
x的类型为: <class 'list'>
实参x的取值= [2019, 2020, 2021]
传入函数的实参x的id= 140642614321416
函数内部的形参x的id= 140642614321416
函数内部修改列表后的形参x的id= 140642614321416
实参x的取值= [2019, 2020, 2021, 2022]
参数类型
以下是调用函数时可使用的正式参数类型:
- 必需参数:必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。
- 关键字参数:函数调用使用关键字参数来确定传入的参数值。使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
- 默认参数:调用函数时,如果没有传递参数,则会使用默认参数。
- 不定长参数:你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,声明时不会命名。
- 加了星号
*
的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。
def functionname([formal_args,] *var_args_tuple ):
"函数_文档字符串"
function_suite
return [expression]
- 加了两个星号 ** 的参数会以字典的形式导入。
def functionname([formal_args,] **var_args_dict ):
"函数_文档字符串"
function_suite
return [expression]
- 声明函数时,参数中星号
*
可以单独出现,如果单独出现星号 * 后的参数必须用关键字传入。
# 下面的例子的r就是必需参数
def cal_area_of_circle(r):
s = math.pi*r*r
return s
# 下面的例子的a,b就是必需参数
def cal_area(a,b):
'''计算矩阵的面积
'''
s = a*b
return s
print(cal_area(1,3))
#print(cal_area(1))
'''
执行cal_area(1)
报错:TypeError: cal_area() missing 1 required positional argument: 'b'
'''
# 下面的形式为关键字参数,函数调用使用关键字参数来确定传入的参数值。
print(cal_area(a=1,b=3))
print(cal_area(b=3,a=3))
3
3
9
# 下面的例子的a,b指定了默认参数
def cal_area_v2(a=7,b=8):
'''计算矩阵的面积
'''
s = a*b
return s
# 实例中如果没有传入a、b参数,就使用默认参数
print(cal_area_v2())
print(cal_area_v2(3))
print(cal_area_v2(5))
print(cal_area_v2(b=3))
56
24
40
21
# 下面的例子为不定长参数
def printinfo(exp_name, *vartuple):
"打印任何传入的参数"
print("输出: ")
print(exp_name)
print(vartuple)
printinfo('exp1', [1,2,3,4], 50,"Hello world!")
输出:
exp1
([1, 2, 3, 4], 50, 'Hello world!')
# 下面的例子为不定长参数
def printinfo( arg1, **vardict ):
"打印任何传入的参数"
print("输出: ")
print(arg1)
print(vardict)
# 调用printinfo 函数
printinfo(1,a=2,b=3)
输出:
1
{'a': 2, 'b': 3}
def f(a,b,*,c):
return a*b*c
print(f(1,2,c=3)) # 正确调用方式
#print(f(1,2,3))
'''
print(f(1,2,3))
报错:TypeError: f() takes 2 positional arguments but 3 were given
'''
6
'\nprint(f(1,2,3))\n报错:TypeError: f() takes 2 positional arguments but 3 were given\n'
10.3 lambda表达式创建匿名函数
所谓匿名,也就是不再使用def
语句这样标准的形式定义一个函数。它的格式为:
lambda [arg1 [,arg2,.....argn]]:expression
说明:
lambda
只是一个表达式,函数体比 def 简单很多。lambda
的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。lambda
函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。- 虽然
lambda
函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
lambda
函数的应用场景:
- 将
lambda
函数赋值给一个变量,通过这个变量间接调用该lambda
函数。 - 将
lambda
函数赋值给其他函数,从而将其他函数用该lambda
函数替换。 - 将
lambda
函数作为参数传递给其他函数。部分Python内置函数接受函数作为参数,典型的此类内置函数有这些:filter
函数、sorted
函数、map
函数、reduce
函数。
# lambda能将函数描述简化为一行代码
def f(x):
return x**2
print(f(4))
g = lambda x:x**2
print(g(4))
16
16
# 将列表按照其绝对值大小升序排序
list1 = [9,-2,6,-5]
list2 = sorted(list1,key=lambda x:abs(x))
print(list2)
[-2, -5, 6, 9]
# 过滤掉列表中的偶数值
list3 = [1,2,3,4,5,6,7,8,9]
list4 = [i for i in filter(lambda x: x % 2 == 0, list3)]
print(list4)
[2, 4, 6, 8]
# 将列表中的元素加1
list5 = [1,2,3,4,5]
list6 = [i for i in map(lambda x: x+1, list5)]
print(list6)
[2, 3, 4, 5, 6]
# 将匿名函数保存在字典的value位置上
func_dict={'2':(lambda x: x * 2),
'3':(lambda x: x * 3),
'4':(lambda x: x * 4)
}
print(func_dict["2"](3)) # 计算3的2次方
print(func_dict["3"](3)) # 计算3的3次方
6
9