python 参数
参考链接:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431752945034eb82ac80a3e64b9bb4929b16eeed1eb9000
首先要才分清楚,如果你想传入字典参数,那么正常写就好,因为字典也是python的数据类型,比如想用**kw来传入字典参数,这是什么鬼操作?(是因为kw就是关键字的意思吗?)
疑惑
1)使用等号来传参的不是关键字参数,而是默认参数
2)
def test1(a,b): print(a) print(b) if __name__ == "__main__": test1(1,a=3) #会报错:TypeError: test1() got multiple values for argument 'a'
3)关键字参数和命名关键字参数难道不可以先组装成一个字典,然后再用一个位置参数接受吗?
不能,虽然使用方式和取值方式都相同
#关键字参数的 def person(name, age, **kw): if 'city' in kw:#取值方式都一样 print(kw['city']) extra = {'city': 'Beijing', 'job': 'Engineer'} person('Jack', 24,**extra) #输出 Beijing #位置参数的 def person(name, age, kw): if 'city' in kw: print(kw['city']) extra = {'city': 'Beijing', 'job': 'Engineer'} person('Jack', 24,extra) #输出 Beijing
但是,有不同的地方
def person(name, age, **kw): print('name:', name, 'age:', age, 'other:', kw) extra = {'city': 'Beijing', 'job': 'Engineer'} person('Jack', 24)#关键字参数可以这样去传值,但位置参数不能这样,会报参数个数错误 #另外,传值的方式也有不同 def person(name, age, **kw): if 'city' in kw: print(kw['city']) extra = {'city': 'Beijing', 'job': 'Engineer'} person('Jack', 24,city='Beijing')#位置参数显然不能这样传值
3)命名关键字参数必须传入参数名,这也就注定了只有一种方式去传参,而不可以像关键字参数那样一次传入一个字典,那这和默认参数又有什么区别呢,除了
def person(name, age=2): print(name, age) #这两种方式都可以 person('Jack', age=24) person(‘jack',2) #而如果是命名关键字参数 def person(name, age, *, city, job): print(name, age, city, job) person('Jack', 24, city='Beijing', job='Engineer')#只有这一种方式去传值,和默认参数相比不是差不多吗?
位置参数
定义了位置参数,调用的时候就一定要传入对应的值
位置参数就是在定义函数的时候只有一个参数名字,最常见的
def fuc(a,b): print(a,b) #调用 func(2,0) #输出 2,0
默认参数:
默认参数就是定义函数的时候直接为参数赋值,为什么有默认参数呢?我们需要这个参数,但大多数的时候这个参数的值都是固定的,为了让调用的时候更加方便我们就可以定义一个默认参数。
拿下面这个输出函数平方的函数为例,x是数,n是幂次方
def power(x, n): s = 1 while n > 0: n = n - 1 s = s * x return s
假设我们平常90%的情况都在计算数的2次方,如果没有定义默认参数,调用的方式是这样的
power(3,2)#计算3的2次方
为了让调用更方便,我们就可以将n的默认值设置为2
def power(x, n=2): s = 1 while n > 0: n = n - 1 s = s * x return s
这样如果想要计算数的2次方就可以简化为
power(2) #输出 4
#如果想要计算别的方
power(2,3)#计算2的三次方
#power(2,n=3),不必这样写
#输出
8
注意
必选参数在前,默认参数在后,否则Python的解释器会报错(思考一下为什么默认参数不能放在必选参数前面);
拿上面的power()为例,假如
def power(n=2,num): ---snip--- 为了更好理解,我们先看一下上面的正确定义默认参数的方法调用时,如果使用默认参数,只传入了一个参数,不适用默认参数时,直接传入的两个值,python解释器就把第二个参数赋给默认参数了 那么假如python 允许了我们将默认参数定义在位置参数之前,当我们传入一个参数时,他怎么知道我们是给谁的呢(虽然有不严谨的地方,但原理大概就是这昂)
默认参数降低了函数调用的难度,而一旦需要更复杂的调用时,又可以传递更多的参数来实现。无论是简单调用还是复杂调用,函数只需要定义一个。
有多个默认参数时,调用的时候,既可以按顺序提供默认参数。也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需要把参数名写上。
默认参数必须指向不可变对象(数值类型、str、元组)
def add_end(L=[]):#可变类型 L.append('END') return L
正常调用(赋值)以及第一次(使用默认值)调用都不会出错
第二次以后使用默认值调用就会出错了
>>> add_end()#第一次 正确 ['END'] >>> add_end()#第二次 不正确 ['END', 'END'] >>> add_end()#第三次 不正确 ['END', 'END', 'END']
原因:Python函数在定义的时候,默认参数L
的值就被计算出来了,即[]
,因为默认参数L
也是一个变量,它指向对象[]
,每次调用该函数,如果改变了L
的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]
了。
可将上面的修改为None
def add_end(L=None):
为什么要设计str
、None
这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。
可变参数
可变参数的意思函数接受的参数的个数可以改变,可以是0个,也可以是2个,即能接受任意个数的参数,但是这些个数的参数的类型相同,或者说是他们在函数内部,都在同一个地方被使用
如果我们什么也不做,那么要想函数接受可变参数只有让传入的参数是一个列表或元组类型的,然后在函数内部再去遍历他。
#分别传入列表类型的和元组类型的作为参数传入 parameter1=[1,2,3] parameter2=[1,2,3,5] #元组类型 parameter3=(1,2,3) parameter4=(1,2,3,4) def test(a): sum=0 for s in a: sum=sum+s*s print(sum) test(parameter1) test(parameter2) test(parameter3) test(parameter4) #结果: (sort) λ python forTest.py
14
39
14
30
这样不方便的地方每次都必须把参数组装成一个元组或者列表的类型才能传进去,这样十分的不方便。
test([1,2,3,5])#把参数组装成列表 test((1,2,3,5))#组装成元组
使用可变参数之后,调用的方式可以简化为这样
test()#传入0个参数 test(1,2,3,5)
这需要把函数的参数改为可变参数,只需要加上一个*号,在定义函数的时候
def test(*a):#注意还是要有a的 #结果 (sort) λ python forTest.py 0 5
加上一个*好后,在函数内部,参数numbers
接收到的是一个tuple(可以理解为:函数内部把接受到的参数组装成一个元组或一个列表?)
如果已经有了一个类型为元组或者列表类型的参数,怎么传入一个参数为可变参数的函数中呢?
一种笨的:
test(parameter2[0],parameter2[1]) (sort) λ python forTest.py 5
另外一种:
test(*parameter2) (sort) λ python forTest.py 39
*nums
表示把nums
这个list的所有元素作为可变参数传进去。这种写法相当有用,而且很常见。
如果需要对传入的参数做不同的操作怎么办呢,比如一个参数是名字,一个参数是年龄,想要让他们两个随机组合的参数该怎么设定呢?
注意
一定要分清是参数的个数可变还是参数里面的长度可变:
比如在下面的函数测试方法中,我想要的是让方法输出每个函数的运行时间,就是待测试的函数个数有变化,而包含这些函数的是一个列表,那么,这个参数就不是可变的,可变的只是里面包含的函数个数。
错误的代码 #定义 def compared_all(collection,*args): result=[0 for i in range(len(args))] times=1 for i in range(times): for j in range(len(args)): 调用: algorithms=[bidirectional_bubble_sort] collection=creat_data(size=10) compared_all(collection,algorithms) #报错: Traceback (most recent call last): File "test_and_compared.py", line 91, in <module> compared_all(collection,algorithms) File "test_and_compared.py", line 63, in compared_all args[j](a1) TypeError: 'list' object is not callable #正确的代码: def compared_all(collection,algorithm_list): result=[0 for i in range(len(algorithm_list))] times=1 for i in range(times): for j in range(len(algorithm_list)):
关键字参数**kw
关键字参数有什么用?它可以扩展函数的功能。比如,在person
函数里,我们保证能接收到name
和age
这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。
关键字参数只是kw接受了一个字典而已,他跟你要传入的参数是个字典并没有关系
关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
1)在函数中使用
首先要在定义函数的时候这样写
def functionName(positionArg,**kw): s=kw['name_a']#获取关键字参数中键名为a的
使用函数的时传入参数:
#可以只传入必选参数
functionName(a)
#也可以传入任意个数的关键字参数,随便起名
test('1',a='23',b='234') #不能下面这样写,否则后提示有一个位置参数却给了两个 dics={'a':'23','b':'234'} test('1',dics)
和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去:
>>> extra = {'city': 'Beijing', 'job': 'Engineer'} >>> functionName(a,city=extra['city'])
更简便的写法
fuctionName(a,**extra)#注意这个**
**extra
表示把extra
这个dict的所有key-value用关键字参数传入到函数的**kw
参数,kw
将获得一个dict,注意kw
获得的dict是extra
的一份拷贝,对kw
的改动不会影响到函数外的extra
。
在函数中取值
def enroll(name,**kw): if 'sex' in kw.keys(): print(kw['sex']) print("name:%s,other:%s"%(name,kw)) if __name__=='__main__': enroll('y1',age=18) extra={'age':18,'sex':'男'}
命名关键字参数:
命名关键字参数必须传入参数名,这也就注定了只有一种方式去传参,而不可以像关键字参数那样一次传入一个字典
关键字参数可以允许在调用方法的时候传入无限个自定义名字的参数,如果要限制关键字参数的名字,就可以用命名关键字参数。例如,只接收city
和job
作为关键字参数。这种方式定义的函数如下:
def person(name, age, *, city, job):#可变参数是一个*号加参数名如*args,关键字参数是两个*号加参数名,定义和调用的时候都是一个*号或者两个*号加变量名 print(name, age, city, job)
和关键字参数**kw
不同,命名关键字参数需要一个特殊分隔符*
,*
后面的参数被视为命名关键字参数
调用方式如下:
>>> person('Jack', 24, city='Beijing', job='Engineer') Jack 24 Beijing Engineer
- 如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符
*
了:def person(name, age, *args, city, job): print(name, age, args, city, job)
- 命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错:
>>> person('Jack', 24, 'Beijing', 'Engineer') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: person() takes 2 positional arguments but 4 were given
#由于调用时缺少参数名city
和job
,Python解释器把这4个参数均视为位置参数,但person()
函数仅接受2个位置参数。 - 命名关键字参数可以有缺省值,从而简化调用:
def person(name, age, *, city='Beijing', job): print(name, age, city, job) 由于命名关键字参数city具有默认值,调用时,可不传入city参数: >>> person('Jack', 24, job='Engineer')#如果不传job,则会出错 Jack 24 Beijing Engineer
>>>person(1,2)
#错误
TypeError: person() missing 2 required keyword-only arguments: 'city' and 'job' - 使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个
*
作为特殊分隔符。如果缺少*
,Python解释器将无法识别位置参数和命名关键字参数:
def person(name, age, city, job): # 缺少 *,city和job被视为位置参数 pass #因为普通的位置参数也可以使用下面这样的方式传入参数值 def enroll(name): print("name:%s,"%(name)) #调用 enroll(name='y1') #输出 (sort) λ python forTest.py name:y1,
参数组合:
参数组合的顺序必须是:位置参数、默认参数、可变参数、命名关键字参数、关键字参数
比如定义一个函数,包含上述若干种参数:
def f1(a, b, c=0, *args, **kw): print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw) def f2(a, b, c=0, *, d, **kw): print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
在函数调用的时候,Python解释器自动按照参数位置和参数名把对应的参数传进去。
>>> f1(1, 2) a = 1 b = 2 c = 0 args = () kw = {} >>> f1(1, 2, c=3) a = 1 b = 2 c = 3 args = () kw = {} >>> f1(1, 2, 3, 'a', 'b') a = 1 b = 2 c = 3 args = ('a', 'b') kw = {} >>> f1(1, 2, 3, 'a', 'b', x=99) a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99} >>> f2(1, 2, d=99, ext=None) a = 1 b = 2 c = 0 d = 99 kw = {'ext': None} >>> func1(1,2,x=99) a = 1 b = 2 c = 0 args = () kw = {'x': 99}#按照传参数的形式,不同类型的参数有不同的传入方式 还有传入的参数会按顺序分给位置参数、默认参数、可变参数 >>> func1(1,2,a=99) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: func1() got multiple values for argument 'a' >>>
最神奇的是通过一个tuple和dict,你也可以调用上述函数:
>>> args = (1, 2, 3, 4) >>> kw = {'d': 99, 'x': '#'} >>> f1(*args, **kw) a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'} >>> args = (1, 2, 3) >>> kw = {'d': 88, 'x': '#'} >>> f2(*args, **kw) a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}
所以,对于任意函数,都可以通过类似func(*args, **kw)
的形式调用它,无论它的参数是如何定义的。