Python函数
2021/6/9 周三
学习内容: 创建和调用函数、参数传递(值传递、引用传递)、参数类型(必需参数、关键字参数、默认参数、可变参数)、lambda匿名函数、递归
刚接触Python,主要是看 菜鸟教程、w3school 网站,以及电子工业出版社出版的Python编程入门指南上册的教材,并根据自己的情况对重点内容做的笔记。
如有错误,欢迎在评论区指出,非常感谢!
Python函数就相当于Java的方法,用户也可以自己创建函数。
一、创建和调用函数
1. 创建函数
Python创建函数的规则:
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
- 任何传入的参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串用于存放函数说明。
- 函数内容以冒号 : 起始,并且缩进。
- return [表达式] 结束函数并返回一个值给调用方,不带表达式的 return 相当于返回 None。如果没有返回值也可以省略return。
如:
def max(a,b):
"get bigger number"
if a>b:
return a
else:
return b
2. 调用函数
使用函数名称后跟括号就可以调用函数。
如,调用上面创建的函数,直接放在print()方法里输出函数的返回值:
print( max(5,10) )
>>> 输出:
>>> 10
二、参数传递
1. 形参和实参
与Java一样,Python的参数也有形参(形式参数)和实参(实际参数)。
比如上面定义函数时(a,b)中的a和b就是形参;
而调用函数时的(5,10)中的5和10则是实参,即传递进来实际参与到函数中运算的参数。
在Python编程入门指南的书上看到一个很好理解的比喻:就像剧本选角一样,剧本中的角色相当于形参,而实际演这个角色的演员就相当于实参。
在Python参数传递中,根据实参的类型不同,会分为将实参的值传递给形参,以及将实参的引用传递给形参两种。
2. 不可变对象-值传递
当实参为不可变对象(immutable),如Number、String、Tuple时,进行的是值传递,相当于只是传递了对象的值,而不是对象本身。
在函数中改变形参的值,实参的值不变。
def change(var):
print("2.改变前形参:"+str(var))
var=10
print("3.改变后形参:"+str(var))
a=1
print("1.改变前实参:"+str(a))
change(a)
print("4.改变后实参:"+str(a))
>>> 输出:
>>> 1.改变前实参:1
>>> 2.改变前形参:1
>>> 3.改变后形参:10
>>> 4.改变后实参:1
因为Number类型是不可变的,a 传递给var的就是值1
在函数里改变var的值后,虽然都是叫var,但这里其实是生成了一个新的值为10的对象var
而实参a并未受到影响,还是1。
3. 可变对象-引用传递
当实参为可变对象(mutable),如List、Dictionary、Set时,进行的是引用传递,相当于把真正的对象传递了过去。
在函数中改变形参的值时,实参的值也一同改变。
def change(var):
print("2.改变前形参:"+str(var))
var.append(4)
print("3.改变后形参:"+str(var))
mylist=[1,2,3]
print("1.改变前实参:"+str(mylist))
change(mylist)
print("4.改变后实参:"+str(mylist))
>>> 输出:
>>> 1.改变前实参:[1, 2, 3]
>>> 2.改变前形参:[1, 2, 3]
>>> 3.改变后形参:[1, 2, 3, 4]
>>> 4.改变后实参:[1, 2, 3, 4]
因为List类型是可变的,mylist传递给var的是对[1,2,3]这个List的引用,
在函数里向var添加一个元素改变了var的值,也就是[1,2,3]这个List被改变成了[1, 2, 3, 4]
所以在函数外的mylist再引用这个List,自然也是引用到了有新值的List。
另外这里还有一种情况,如果在函数内不用append,而是直接 var = [1,2,3,4],这其实也是重新给var赋值了,也是一个新的List对象,这时会与不可变对象情况一样。只有使用append()等方法,只改变List里的元素,List对象依然没变,才是引用传递。
三、参数类型
1. 必需参数
也称必备参数或位置参数,函数调用时,实参的数量和位置必须和形参参数列表是一样的。
比如定义一个mytest函数,需要传一个数字类型和一个字符串类型:
def mytest(a,b):
a+=1;
print(a)
print(b + "str")
mytest(1)
>>> 缺少参数,报错:
>>> TypeError: mytest() missing 1 required positional argument: 'b'
mytest(1,2)
>>> 参数类型不匹配,报错:
>>> TypeError: unsupported operand type(s) for +: 'int' and 'str'
mytest(1,"2")
>>> 符合参数列表的数量和类型,不报错,正确输出:
>>> 2
>>> 2str
mytest("2",1)
>>> 参数的位置不对(其实就是对应位置的类型不匹配),报错:
>>> TypeError: can only concatenate str (not "int") to str
总之实参和形参的顺序就是一一对应的,个数或类型对应不上就会报错。
但是还需注意一点的是,可能有类型相同但在函数中作用不同的参数,如果传入参数位置不对,因为是同类型所以不会报错,但放到函数中就会有问题了。
2. 关键字参数
关键字参数可以允许函数实参的顺序与声明时形参不一致,只是要在调用时使用关键字参数来确定传入的参数值匹配的是形参中的哪一个。
比如上面那个位置反过来的例子:
mytest(b="2",a=1)
>>> 输出:
>>> 2
>>> 2str
加上关键字参数,就不会报错了,因为Python解释器能够用参数名匹配参数值。
3. 默认参数
还可以在定义函数时给参数设置默认值,调用函数时如果没有传递参数,则会使用默认参数。
比如:
def mytest(a,b="b"):
a+=1;
print(a)
print(b + "str")
mytest(1)
这样即使调用时缺少参数,也没关系了,会以默认值来执行函数。
还需要注意一种情况,如果使用可变对象作为参数的默认值,多次调用并且都不指定实际参数,可能会导致有问题:
def mytest(a=[]):
a.append(1);
print(a)
mytest()
mytest()
>>> 输出:
>>> [1]
>>> [1, 1]
所以默认参数最好使用不可变对象;或者在形参上用None作为可变对象的值,函数里再去赋值:
def mytest(a=None):
if a==None:
a=[]
a=[1]
print(a)
mytest()
mytest()
>>> 输出:
>>> [1]
>>> [1]
4. 可变参数
如果不知道需要传给函数多少个参数,可以使用可变参数,也称不定长参数或任意参数,它能传入比声明时更多的参数。
主要有两种形式,一种是在函数定义的参数名称前添加 * 星号,这样,函数将接收一个参数元组,并可以相应地访问各项。
def mytest(a,*b):
print(a)
print(b)
mytest(1,2,3,4)
>>> 输出:
>>> 1
>>> (2, 3, 4)
如果需要使用一个已经存在的List作为可变参数,可以 在调用时的列表名称前加 * 星号,将List里的每个元素传入可变参数而不是将整个List作为参数传入:
def mytest(*b):
print(b)
mylist = [2,3,4]
mytest(mylist)
mytest(*mylist)
>>> 输出:
>>> ([2, 3, 4],)
>>> (2, 3, 4)
使用可变函数还可以不传入实际参数,这样就是一个空元组。
另一种是在函数定义的参数名称前添加两个星号 **,接收任意多个类似关键字参数引用显示赋值的实际参数,这样传来的参数将被放到一个字典中:
def mytest(**mydict):
for k,v in mydict.items():
print(k+" for "+v)
mytest(A="Apple", B="Ball", C="Car")
>>> 输出:
>>> A for Apple
>>> B for Ball
>>> C for Car
这跟用dict()构造函数创建字典一样,实参用=等号连接key和value,且key不能带引号。
同样也可以 给实参加**把已有的Dictionary传入函数:
mydict = {"A":"Apple","B":"Ball","C":"Car"}
mytest(**mydict)
如果写mytest(mydict)尝试直接将整个字典传入的话会报错。
四、匿名函数
匿名函数即不再使用def语句这样标准的形式定义一个函数。
python使用lambda表达式来创建匿名函数:
result = lambda [arg1 [,arg2,.....argn]] : expression
- lambda只是一个表达式,而不是一个代码块
- 可接受任意数量的参数
- 只返回一个值,需要定义一个result变量用于调用该表达式并接受返回值
- lambda函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数(关于全局变量可以看我之前的笔记 Python入门)
例如:
result = lambda a,b: (a+b)*(a-b)
print(result(10,5))
>>> 输出:
>>> 75
五、递归
Python 也接受函数递归,即的函数能够调用自身。
例如经典的求阶乘:
def mytest(n):
if n==1:
return 1
else:
return n*mytest(n-1)
print(mytest(3))
print(mytest(10))
>>> 输出:
>>> 6
>>> 3628800