python中的函数与js中函数功能一样封装共用逻辑,增加代码可复用性,但是复杂性远远高于JavaScript,在本篇文章中我将分六大方面来跟大家聊下函数:
- 定义函数
- 函数返回值
- 空函数
- 函数参数的类型检查
- 多值返回
- 函数参数
函数参数又分3个部分:
(1)默认值参数
(2)可变参数
(3)收集关键字参数
1、定义函数及返回值:
在Python中定义一个函数,要使用def语句(在js用function),依次写入函数名、括号、括号中的参数和冒号,函数返回值用return语句返回:
def fn_name (param):
print(param)
fn_name('lxc') # 函数调用
上边代码,如果没有返回值 则不需要用return。
下边代码,是有返回值,我们把返回值存到一个变量里,接着打印出来。
def fn_name (param):
return param
name = fn_name('lxc') # 函数调用
print(name)
注意点:
1、return语句的意思是终止语句,返回结果;
2、如果没有return语句,函数执行完毕,返回的是None(在js中,默认返回的是undefined);
3、执行return之后,后边语句将不会执行!!!
3、空函数
定义一个空函数一般是暂时没想好怎么写逻辑代码,占位用的:
def fn_name ():
pass
上边代码,代码块先用pass站位,程序也会运行,如果代码块为空,会报错!
pass还可以用在if条件语句中:
if True:
pass
4、参数类型检查 isinstance( )
在Python中,可用内置函数 isinstance( ),判断变量是否是某个数据类型的实例。
def name_fn (param):
if isinstance(param,(int)):
print("您输入的参数%s类型正确" % (param)) # 您输入的参数10类型正确
else:
raise TypeError('param is bad!')
name_fn(10)
python中的类型检查isinstance()类似于js中的instanceof(),只不过是写法不一样
var arr = [];
console.log(arr instanceof Array) // true
这里要说明下,python中,函数中传入一个错误的参数类型,如果不做类型校验也不会报错,在开发中添加参数类型校验如果传入错误类型,函数就可以抛出错误,会更加利于排错!关于错误和异常处理我在后边文章中将详细聊。
5、返回多值
函数也可以返回多个值,但是返回的类型是元祖tuple
def name_fn (param,param1,param2):
return param,param1,param2
print(name_fn(1,2,3)) # (1,2,3)
print(type(name_fn(1,2,3))) # <class 'tuple'>
注意点:
1、函数返回多个值,类型是tuple
2、返回一个值时,值是什么类型就是什么类型
补充:
下边我们来看下,多值返回一个元组,如何来用呢?如何取值呢?
def fn(x,y):
return x,y
result = fn(1,2)
print(result[0]) # 1
print(result[1]) # 2
上边代码,许多人会这样取值,时间久了,你会忘记result[0]到底是什么值;
def fn(x,y):
return x,y
result_one,result_two = fn(1,2)
print(result_one) # 1
print(result_two) # 2
上边代码,写法是不是更明了了,在python中叫做序列解包(类似于ES6中的结构赋值)。
下边我们来看下序列解包:
a,b,c = 1,2,3
print(a,b,c) # 1 2 3
也可以这样写:
param = 1,2,3
print(param) # (1,2,3)
print(type(param)) # <class 'tuple'>
a,b,c = param
print(a,b,c) # 1 2 3
上边代码,param = 1,2,3 会返回一个元组,在python中叫做序列封包
下边代码,如果右边变量数量与左侧不符,将会报错!!!
param = 1,2,3
a,b = param
print(a,b) #报错 ValueError: too many values to unpack (expected 2)
********6、函数参数********
下边我们主要说下默认值参数、动态参数
(1)默认值参数:
了解过ES6小伙伴都知道默认值参数,在python中默认值参数与 js中 的类似,当没传参数时,函数内将会使用默认值参数:
def default_param (param = 1):
print(param)
default_param() # 1
上边代码,调用函数时未传递参数,输出的是默认值参数。
(2)当默认值参数为可变类型时,应注意:
def default_param (list = []):
list.append(1)
print(list)
default_param() # [1]
default_param() # [1,1]
default_param() # [1,1,1]
上边代码,默认值参数是一个空列表list,第一次还是正常,当第二、三次在调用时,list已经不是空列表,这是因为列表list是可变的,每次调用函数,list都会被改变!!!
解决办法:把默认值换成不可改变的类型即可(如:数字int、字符串str、元组tuple),如果默认值非要定义一个空列表,可以这样写:
def default_param (list = None):
if list == None:
list = []
print(list)
else:
print(list)
default_param() # []
default_param() # []
上边代码,None是一个不可变对象,当调用函数时,如果未传参数,list将会是None空值类型,在函数内部,我们让列表list指向一个空列表,无论调用多少次,都是空列表[ ]。
***可变参数***:
先来回顾下ES6中处理可变参数(不定参数),是用扩展运算符(...variable),而python中处理不定参数与js有很大不同,使用 *号
def default_param (*params):
print(type(params)) # params 类型是一个元组 <class 'tuple'>
for i in params:
print(i,end='--') # [1, 2]--1--lxc--(1, 2)--
default_param([1,2],1,"lxc",(1,2))
上边代码,在调用函数时,我们传入了列表[1,2]、数字1、字符串'lxc'、元组(1,2) ,params类型是一个元组,上边我们讲过函数返回多个值时,也会返回一个元组;我们用for循环依次把参数输出出来!!!( 最后end意思是以什么方式连接参数,默认以\n换行来输出结果的, 可以类比js中的数组join方法,此方法默认是以逗号形式把数组转化为字符串)
补充:
可变参数可以放在形参中的任何位置,但是如果形参中有位置参数,则必须使用默认值参数,或者是实参中定义关键字参数,否则会报错:
def fn(*a,b):
print(a)
print(b)
fn(1,2,3,4) TypeError: fn() missing 1 required keyword-only argument: 'b'
#在是实参中定义关键字参数
def fn(*a,b):
print(a) #(1, 2, 2)
print(b) #3
fn(1,2,2,b=3)
#也可以在形参中定义默认值参数
def fn(*a,b=4):
print(a) #(1, 2, 2, 3)
print(b) #4
fn(1,2,2,3)
不在函数中使用可变参数:
*a,b = range(10)
print(a) #[0, 1, 2, 3, 4, 5, 6, 7, 8]
print(b) # 9
***收集关键字参数***:
1、收集关键字参数与可变参数类似,把余下的赋值语句(必须是赋值语句才行)统一放入一个字典当中,有两个 星号* ,而可变参数是把剩余的参数统一放入一个元组中。
用代码说下:
def default_param (name,age,**keyword):
print(name) # 'lxc'
print(age) # 20
print(keyword) #{'height':'170','say':'hehe'}
default_param('lxc',20,height="170",say="hehe")
上边代码,前两个参数name和age正常传入,接下来剩下的两个参数是赋值语句 height="170" 和 say="hehe",我的理解是,解释器会做两件事 :
(1)把余下的赋值语句统一放入一个字典中,等号两边分别对应key-valuie;
(2)把由赋值语句组成的字典,赋值给 有两个星号后边的参数(上边是keyword),所以我们输出keyword就是由赋值语句组成的字典。
可以结合上边可变参数,来理解收集关键字参数!!!
(3)形参中的默认值参数必须要放到普通参数的后边:
def fn (a,b = 1):
print(a) # 1
print(b) # 2
fn(1,2)
下边写法报错,因为没有把默认值参数放到最后:
def fn (a,b = 1,c):
pass
fn(1,2,3)
下边代码,前两个参数是必须传递的位置参数,其他参数如果不传会使用默认参数,当然也可以在实参中指定修改哪一个默认参数。。。
def fn (name,age,grade='高三四班',school='莱阳一中',):
print('%s,年龄:%d,%s,在读学校:%s' % (name,age,grade,school))
fn('吕星辰',20) # 吕星辰,年龄:20,高三四班,在读学校:莱阳一中
fn('鸡小西',21) # 鸡小西,年龄:21,高三四班,在读学校:莱阳一中
fn('牛二',18,school='烟台一中') # 牛二,年龄:18,高三四班,在读学校:烟台一中
***可变参数和关键字参数作为实参***
如果你明白了可变参数和收集关键字参数,那么接下你会有更深刻的理解,当他们作为实参来用的时候,也就是逆向参数,
逆向参数收集,需要在传入的列表 或元祖之前加上星号*,在字典参数之前添加两个星号。
来看一段代码:
dict = {'name':'lxc','age':20}
def default_param (name,age):
print(name) # 'lxc'
print(age) # 20
default_param(**dict)
上边参数,收集关键字参数作为实参来用,可以直接输出字典中key所对应的的value值(需注意:形参中的参数需要与字典中的key统一!!!)
dict = {'name':'lxc','age':20}
def default_param (**dict_alias):
print(dict_alias) # {'name':'lxc','age':20}
default_param(**dict)
上边代码,可对外边dict字典,实现深层拷贝,如果修改里边字典dict_alias中的数值,不会影响到外边dict!!!
再看下可变参数:
tuple = (1,2,3)
def defult_param (a,b,c):
print(a)
print(b)
print(c)
defult_param(*tuple)
上边参数,可变参数作为实参来用,形参只需要与元组中的参数数量一致即可!!!
补充:
1、为函数提供文档:
我们还可以为函数编写说明文档,把一段字符串放在函数声明之后,函数体之前,那么这段字符串将会被当做函数的一部分。
可以通过 help( )函数 查看说明文档,也可以用过函数的 _ _ doc _ _属性 访问函数的说明文档:
def fn():
'''
这个fn是一个演示函数,没有意义
:return:
'''
pass
fn()
help(fn)
#输出:
# Help on function fn in module __main__:
#
# fn()
# 这个fn是一个演示函数,没有意义
# :return:
#输出
print(fn.__doc__)
# 这个fn是一个演示函数,没有意义
# :return:
2、函数的参数传递机制:
在python中,函数参数传递机制都是 “值传递” 。所为值传递就是将实际参数值的副本(复制品)传入函数,而值本身不会收到影响。(可理解为js中的原始值)
def fn(a,b):
a,b = b,a
print(a) # 2
print(b) # 1
a = 1
b = 2
fn(a,b)
print(a) # 1
print(b) # 2
继续往下看,当值类型是引用值时的情况:
def fn(d):
d['a'],d['b'] = d['b'],d['a']
print(d) # {'a': 2, 'b': 1}
d = {'a':1,'b':2}
fn(d)
print(d) # {'a': 2, 'b': 1}
上边代码,在函数内部修改了字典中的值,结果外边字典里边也跟着改变了!
其实,d变量作为参数传入到fn函数,同样采用值传递方式,但是d变量只是一个引用变量(也就是一个指针),它保存了字典对象的地址,当把d变量赋值给fn参数后,也就是让fn参数也保存这个地址(指向这个地址),通俗点说,d变量和函数参数的d变量(副本)同时指向了同一个地址,系统复制的是d变量,并不是赋值字典本身。所以字典本身发生改变,两个变量也跟着改变了。(与js中的引用值一样)
还要着重强调下,上边代码中,外边d变量和fn函数里边的d变量是两个变量,只不过同时指向了一个地址而已,下边代码,我们把函数里边的d变量重新赋值,看下结果:
def fn(d):
d = None
print(d) # None
d = {'a':1,'b':2}
fn(d)
print(d) # {'a': 1, 'b': 2}
上边代码,函数内部变量重新赋值,结果外界d变量不受任何影响。。。再次证明主程序中的d变量和fn函数中的d变量是两个变量。