Python学习6.1_函数参数及参数传递
大多数编程语言都绕不开一个名词,那就是--函数(function)。而函数很重要的部分则是参数(arguments)的使用。Python的参数传递总体来说是根据位置,传递对应的参数。阐述如下:
1、位置参数及传递:
位置参数:顾名思义位置参数是根据形参的位置先后顺序传入的。
如下:定义一个测试函数,传入三个参数,然后依次打印出这三个参数:
def fun(first, second, third): print first, second, third if __name__ == '__main__': fun(21, 'second', [21, 'sum', 'T'])
在调用fun函数时,first, second, third根据各位置相对应的传递给了21, 'second', [21, 'sum', 'T'] 三个不同类型的数据。当然也可以将这些数据写成单独的变量,如下所示,然后将变量按位置顺序传入,这种方式和直接在实参(值)位置按顺序写入是一样的,只不过封装了一下。
def fun(first, second, third): print first, second, third if __name__ == '__main__': num1 = 21 num2 = 'second' num3 = [21, 'sum', 'T'] fun(num1, num2, num3)
输出的结果都是:21 second [21, 'sum', 'T']
位置参数的好处在于我们不需要知道形参长什么样;坏处是需要知道它们有几个,每一个表示什么意思,处在什么位置。位置参数相对而言,比较简单,只需按照位置一一对应传入即可。
2、关键字参数及传递
关键字参数:从意思理解指的是当调用一个函数的时候,可以用key = value的方式来指定给某个具体参数赋值。
由于是根据具体参数名赋值,那么就可以不遵守函数声明里的参数顺序,虽然不用管形参的排列顺序,但参数个数还是需要遵循的。
def fun(first, second, third): print first, second, third if __name__ == '__main__': fun(second=21, third='second', first=[21, 'sum', 'T'])
在调用函数fun时,根据形参的字符字段分别被赋值为21, 'second', [21, 'sum', 'T'],虽然三个值以及三个值的顺序都没发生改变,但由于形参关键字的原因已经使得三个值实际对应的顺序已然不同,从打印结果就可以看出
输出的结果:[21, 'sum', 'T'] 21 second
关键字参数的好处在于我们不需要知道形参的具体分布位置,同时明确了每个参数的含义;坏处是必须要知道形参都长啥样。
3、参数默认值
参数默认值:即函数在定义时就为某些形参赋予默认值。在该函数被调用时,若未给这些参数传入值,那么该函数就使用默认值进行计算;若这些参数有传入值,那么默认值就无效,函数使用传入值进行计算。
调用一:如下所示third和fourth在函数定义时分别被赋予默认值None和’today’,调用函数时,按位置参数顺序只传递了两个值。
def fun(first, second, third=None, fourth='today'): print first, second, third, fourth if __name__ == '__main__': fun(21, 'second')
输出结果:21 second None today 从结果看出,前两个参数是按位置参数顺序传递的值,后两个参数是默认值
调用二:如下所示third和fourth在函数定义时分别被赋予默认值None和’today’,但是在调用函数时,按位置参数顺序传递方式传递进三个值。
def fun(first, second, third=None, fourth='today'): print first, second, third, fourth if __name__ == '__main__': fun(21, 'second', [21, 'sum', 'T'])
输出结果:21 second [21, 'sum', 'T'] today 从结果看出,此次调用third参数值已经被改变为[21, 'sum', 'T'], 默认值无效。仅仅只是此次调用默认值无效,但函数内third设定的默认值依然存在,不会丢失。
参数默认值的好处在于我们可以根据参数在函数内的使用情况默认的赋予初始值,坏处是带有默认值的形参必须在不带默认值的形参后面,即默认值参数必须在位置参数后面。
4、收集参数(包裹参数)及传递
收集参数:将某些位置参数,或者关键字参数打包收集起来,然后使用。在定义函数时,我们有时候并不知道调用函数时需要传入多少个参数,也不知道会传入多少个参数。这时候,收集位置参数,或者收集关键字参数,来进行参数传递,会非常有用。
参数收集函数的定义分为两种,即形参前带“*”或者“**”:
def fun(*data):和def funtion(**bass):
下面定义了def fun(*data):和def funtion(**bass): 两个例子,将数据传入后,分别打印出该参数类型和收集后形成的数据。
def fun(*data): print type(data) print data def funtion(**bass): print type(bass) print bass if __name__ == '__main__': fun(21, 'second', [21, 'sum', 'T'], {'num': 100, 'sum': 'num123'}) funtion(feng=23, ling='xue', ying=[1, 2, 'yun'], han={'qiu': 'yu'})
从调用fun()和funtion()时传入的参数可以看出,fun()函数的实参,可以无序,也可以是任何类型,但必须是位置参数格式传入,不能是关键字参数格式传入;而funtion()函数的实参,也可以是无序,也可以是任何类型,但却必须是关键字参数格式传入,不能是位置参数格式。不信的话,可以试试传入其他格式。
输出结果:<type 'tuple'>
(21, 'second', [21, 'sum', 'T'], {'sum': 'num123', 'num': 100})
<type 'dict'>
{'ling': 'xue', 'feng': 23, 'ying': [1, 2, 'yun'], 'han': {'qiu': 'yu'}}
从输出结果得出fun()函数输出的类型是”组”,是元组,也就是说”*”将位置参数收集起来形成了一个包裹(元组);而funtion()函数输出的类型是字典,即”**”将关键字参数收集起来形成另一个包裹(字典)。并且就算两函数不传入任何值,其返回的结果也依然是元组和字典。
如下所示:两函数不变,调用时不传入任何值。
if __name__ == '__main__': fun() funtion()
输出结果:<type 'tuple'>
()
<type 'dict'>
{}
也就是说单星号”*”是将参数收集形成一个元组,不论参数数量多少,但限定传入格式是位置参数;双星号”**”是将数据收集成一个字典,同样不论数量多少,但限定传入格式是关键字参数类型。这两种形参的好处是不用限定传入参数的个数和传入的顺序。
5、解包裹参数
定义函数时,在形参名前面加上“*”或者“**”,可以将参数按位置收集起来,分别封包成元组和字典传递。
“*”和“**”,也可以在调用函数的时候使用,即在实参前使用,也就是所谓解包裹。
1、解包元组:定义一个fun()函数,设定四个形参,然后将这四个参数打印出来。调用该函数时传入一个元组,并在元组名前加“*”。形参个数在此是根据调用该函数时传入实参,将实参分解后的板块数设置的(其实这样做不妥,但此处仅为验证解包,故而直接匹配个数)。
def fun(a, b, c, d): print a, b, c, d if __name__ == '__main__': data = (21, 'second', [21, 'sum', 'T'], {'num': 100, 'sum': 'num123'}) fun(*data)
输出结果:21 second [21, 'sum', 'T'] {'sum': 'num123', 'num': 100} 从输出结果看出,已经将元组()解封成4个单独的数据。
2、解封字典比较特殊,从下面的形参设定就可以看出,形参和字典里面关键字是一一匹配的,这是特殊之处。
def func(ling, feng, ying, han): print ling, feng, ying, han if __name__ == '__main__': data_da = {'ling': 'xue', 'feng': 23, 'ying': [1, 2, 'yun'], 'han': {'qiu': 'yu'}} func(**data_da)
输出结果:xue 23 [1, 2, 'yun'] {'qiu': 'yu'} 输出结果可以看出,已经将字典解包了,将值都单独呈现出来。
如果定义函数时形参和字典关键字不能一一对应,运行时会报错,如下所示。
def func(xxxx, feng, ying, han): print xxxx, feng, ying, han if __name__ == '__main__': data_da = {'ling': 'xue', 'feng': 23, 'ying': [1, 2, 'yun'], 'han': {'qiu': 'yu'}} func(**data_da)
输出结果:下面报错意思是“定义了一个意外的关键字参数‘ling’”,也就是说字典里面有一个形参不存在的关键字’ling’,可以换其他的试试,一样会报错。
Traceback (most recent call last):
File "E:/python/0531/test02.py", line 12, in <module>
func(**data_da)
TypeError: func() got an unexpected keyword argument 'ling'
所以“*”和“**”不仅能按位置将参数收集起来,打包使用,也可以将包裹解开,按单个值使用。
6、混合
在函数的定义或调用时,参数的几种传递方式可以混合使用。但在过程中要注意前后顺序。基本原则是,先位置,次关键字,再次包裹位置,最后包裹关键字。
定义函数时,若形参类型是位置参数和关键字参数(赋予默认值),那么位置参数必须在关键字参数前面;若形参类型是位置参数和关键字参数以及包裹位置参数,那么形参顺序必须是位置参数,关键字参数,包裹位置参数;若形参类型是位置参数,关键字参数,包裹位置参数以及包裹关键字参数,那么形参顺序必须是位置参数,关键字参数,包裹位置参数,包裹关键字参数。这些形参类型顺序不能颠倒,也不能混乱(颠倒了,语法规则检验都过不了)。如下所示:
def func(data, num=100, *sum, **han): print type(data), type(num), type(sum), type(han) print data, num, sum, han if __name__ == '__main__': func('today', 21, [21, 'sum', 'T'], {'num': 100, 'sum': 'num123'}, feng=23, ying=[1, 2, 'yun'], han={'qiu': 'yu'})
输出结果:
<type 'str'> <type 'int'> <type 'tuple'> <type 'dict'>
today 21 ([21, 'sum'], {'num': 100}) {'feng': 23, 'han': {'qiu': 'yu'}}
注:注意”*”,”**”定义时和调用时的区分。包裹参数和解包裹参数不是相反操作,是两个相对独立的过程。
位置参数和按参数位置收集不同。位置参数,每个参数的位置固定的,个数也是固定的,调用时传入的值和形参的位置是一一对应的;而按参数位置收集,是指带有“*”的形参,它会将除位置参数对应的实参以外,其他参数按传入位置的先后收集成一个元组。
def func(data, num=100, *sum): func('today', 21, [21, 'sum', 'T'], {'num': 100, 'sum': 'num123'})
除'today', 21是位置参数的实参外,其余实参按参数位置被*sum收集起来,形成一个元组