positional argument follows keyword argument的产生原因和解决办法

我的错误:

正在测试吴恩达识别猫的程序过程中出现:

但是我发现一个问题:

这样的话就可以运行,但是会报其它的错。

实参列表中,既有名字又有变量赋值的放在前面,将变量放在后面,这样的话,依旧报错,报错范围更大了。

实参列表中,将变量放在前面,既有名字又有变量赋值的放在后面,就不会报错。

我通过查阅资料:

资料1:

1.位置参数和关键字参数(根据实参的传递方式对参数进行的分类)

a.位置参数:实参位置和形参保持一致,按形参声明的先后顺序一一赋值

b.关键字参数:调用函数的时候以 形参=实参的方式来传参, 这个时候实参的顺序无所谓

1
2
3
4
5
6
7
8
9
10
def func1(a, b, c):
    print(a, b, c)


# 通过位置参数传参
func1(10, 20, 30)
# 通过关键字参数传参
func1(c=30, a=10, b=20)
# 通过位置参数和关键字参数结合传参(注意关键字参数必须在位置参数的后面)
func1(10, 20, c=30)

2.参数默认值

声明函数的时候,可以给参数赋默认值。
如果一个形参有默认值了,那么调用函数的时候这个参数就可以不用传参
如果有的形参有默认值,有的形参没有默认值,那么有默认值的形参要放在没有默认值的形参的后面

调用函数要保证每个参数都有值!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 参数c有默认值,调用函数的时候c可以不用传参
def func2(a, b, c=0):
    # a=100, b=200
    print(a, b, c)


func2(100, 200, 300)
func2(100, 200)
func2(a=100, c=200, b=150)
func2(b=110, a=220)


def func3(width, color='black', long=10):
    print('画一条长度是%d宽度是%s的%s线' % (long, width, color))


func3(1)
func3(1, 'red')
func3(2, long=20)

3.参数的类型说明 - 类型说明只有提示功能,没有约束功能

a.给参数设置默认值, 默认值的类型就是参数的类型
b.在形参后面加 :类型名,来说明参数类型。这种方式调用的时候必须传参
c.函数声明的时候在()后面加 ->类型名, 来说明函数返回值的类型

类型说明的好处:a.对实参的类型进行指导 b.实现函数功能的时候有类型相关的语法提示

1
2
3
4
5
6
7
8
9
def list_item(list1: list, index=0)->int:
    print(list1[index])


list_item([2, 34])


def func4(a: list, b):
   a.append(b)

4.不定长参数 - 函数的参数个数不确定

a. 声明函数的时候,在参数名前加,可以用来同时获取多个实参的值 -- 实质是将带的参数编程元祖,将多个实参的值作为元祖的元素

注意: a.如果函数中既有不定长参数又有普通参数,那么不定长参数必须放在普通参数的后边

b.两个的参数要放在一个的后边

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 写一个函数,计算多个数的和
def yt_sum(*nums):
    print(nums)


yt_sum()
yt_sum(1)
yt_sum(1, 2)
yt_sum(1, 2, 3)
yt_sum(1, 2, 3, 4, 5)


def func5(name, age, *scores):
    print(name, scores)


func5('夏明', 18, 209)
func5('小花', 10, 20, 30)

c.在参数名前加两个*, 会将这个参数变成一个字典来获取关键字参数的值,其他关键字作为key,实参作为value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def func6(**nums):
    print(nums)


func6()
func6(a=10)
func6(a=10, b=20)


def func7(*args, **kwargs):
    print(args, kwargs)


func7(10, 5, 89)
func7(a=90, b=9, c=90)
func7(90, 89, z=0, v=87)

练习:写一个函数,计算多个数的和(要求可以使用位置参数也可以使用关键字参数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def yt_sum(*args, **kwargs):
    sum1 = 0
    # 遍历元祖
    for num in args:
        sum1 += num

    # 遍历字典
    for key in kwargs:
        sum1 += kwargs[key]

    print(sum1)


yt_sum(1, 2)
yt_sum(1, 2, 3, a=4, b=5)

作者:匿隱
链接:https://www.jianshu.com/p/15016786d6ec
来源:简书

资料2:

参数:

两大类: 形参 和 实参

1、形参:在函数定义阶段括号内定义的参数,称为形式参数 本质就是变量的名

def foo (x,y):

print(x)

print(y)

2、实参:在函数调用阶段括号内定义的参数,称为实际参数 本质就是变量的值

foo(1,2)

print(x) # 这是函数体以外的内容 程序不会执行没有定义 x

详细的分类: #####################################################33

一、位置参数:在函数定义阶段,按照从左到右的顺序定义的形参,称为位置形参

特点:但凡是按照位置定义的形参。都必须被传值,多一个不行,少一个也不行

def foo (x,y):

print('x:',x)

print('y:',y)

foo(1,2)

输出

x: 1

y: 2

位置参数:在函数调用阶段,按照从左到右的顺序定义的形参,称为位置实参

特点:按照位置为对应的形参依次传值

二、关键字实参:在调用函数时,按照key=value的形式为指定的参数传值,称为关键字实参

特点:打破位置的限制,但仍能为指定的形参赋值

foo(y=1,x=2) # 这是指名道姓的给传参打破了 位置不能改变

注意:

可以混用位置实参与关键字实参,但位置实参必须放在关键字实参的前面

foo(y=2,1) SyntaxError: positional argument follows keyword argument

foo(1,y=2)

可以混用,但不能对一个形参重复赋值

foo(1,y=2,x=3) #报错

三:默认参数:在函数定义阶段,就已经为形参赋值,该形参称为默认形参

特点:在定义阶段就已经赋值,意味着在调用可以不用为其赋值

def foo(x,y=2):

print('x:',x)

print('y:',y)

foo(1) # y已经赋值了 可以只赋值给x

foo(1,3) # 也可以赋值 那么形参中的y=2 就消掉了

输出

x: 1

y: 2

x: 1

y: 3

注意:

1、位置形参必须放到默认形参的前面,否则报语法错误

def fff(x=1,y) # x=1 放后面

pass

2、默认参数的值只在定义阶段赋值一次即默认参数的值在函数定义阶段就已经固定死了

m=10

def foo(x=m,y=11)

print(x)

print(y)

m=1111111111 # 定义阶段已经固定死了

foo() #不需要传值

输出10 11

3默认的参数通常应该定义为不可变类型

总结:

实参的应用:取决于个人习惯

形参的应用:

1.位置形参:大多数情况下的调用值都不一样,就应该将参数定义位置形参

2.默认形参:大多数情况下的调用值都一样,就应该将参数定义默认形参

def register(name,age,sex='male'): #sex = 'male' 性别定义男性是默认形参

print(name)

print(age)

print(sex)

register('egon:',18)

register('小脑袋:',46,'female')

register('大脑袋:',78)

register('alse:',33)

输出

egon:

18

male

小脑袋:

46

female

大脑袋:

78

male

alse:

33

male

四 可变长参数: 指的是在调用函数时,传入的参数个数可以不固定

而调用函数时,传值的方式无非两种,一种位置实参,一种关键字实参

所以对应着,形参也必须有两种解决方案,来分别接受溢出的位置实参(*)与关键字实参(**)

1/形参中某个参数带*

形参中的*会将溢出的位置实参全部接受,然后存储元组的形式,然后把元组赋值给*后的变量名

def foo (x,y,*z):

print(x)

print(y)

print(z)

foo(1,2,3,4,5,6,7)

输出

1

2

(3, 4, 5, 6, 7)

比如求和的需求就需要可变长参数

def my_sum(*nums):

res=0

for num in nums : # 从元组里取出一个一个值

res+=num #就是将上一个num的值+1 等同于num+=1

return res

print(my_sum(1,2,3,4,5))

输出

15

2/实参中的参数也可以带*

实参中带*的,*会将该参数循环取出,打散成位置实参

以后但凡碰到实参中带*的,他就是位置实参,应该立刻打散成位置实参去看

def foo(x,y,z):

print(x,y,z)

foo(1,*[2,3]) # foo(1,2,3)

foo(1,*'he') # foo(1,'h','e')

foo(1,*(2,3,4)) # foo(1,2,3,4) # 报错 实参 多出一个值

形参 实参 都带*

def foo(x,y,z,*m): # m 是个变量名

print(x)

print(y)

print(z)

print(m)

foo(1,2,3,4,5,6,7,*[8,9,10,11]) # 流程是先从调用开始 foo=(1,2,3,4,5,6,7,8,9,10,11)

1赋给x 2赋给y3赋给z 其他都被*存成元组

注意:约定 形参中的变量名写法都是:*args

1/形参中某个参数带**

形参中的**会将溢出的关键字实参全部接受,然后存储字典的形式,然后把字典赋值给**后的变量名

def foo(x,y,**z): # x=1,y=2,z={'c'=5,'b'=4,'a'=3} 字典没有顺序

print(x)

print(y)

print(z)

foo(1,2,a=3,b=4,c=5)

2/实参中的参数也可以带**,该参数必须是字典

实参中带**的,**会将该参数循环取出,打散成位置关键字实参

以后但凡碰到实参中带**的,他就是关键字实参,应该立刻打散成位置关键字实参去看

def foo(x,y,z):

print(x)

print(y)

print(z)

# foo(1,2,**{'a':1,'b':2,'c':3}) #foo(1,2,c=3,b=2,a=1)

# 报错 1 给x 2 给y 后面就传不了了因为没有遵循形参一一对应的标准

foo(**{'z':3,'y':2,'x':10})

# 输出

10

2

3

注意:约定 形参中的变量名写法都是:**kwargs

def wrapper (*args,**kwargs):

print(args)

print(kwargs)

wrapper(1,2,3,4,5,6,x=2,c=4,b=5)

输出

(1, 2, 3, 4, 5, 6)

{'x': 2, 'c': 4, 'b': 5}

输出 两种形式 元组和 字典

def index(name,age,sex):

print('welecome %s:%s:%s to index page' %(name,age,sex))

print(name,age,sex)

def wrapper (*args,**kwargs): #def wrapper (**kwargs):

index(*args,**kwargs) #index(**kwargs)

wrapper(name='egon',age=18,sex='male')

输出

welecome egon:18:male to index page

egon 18 male

强调 但凡要调用一个函数把参数转给内部的一个函数 就用 * ** 模式

了解知识点:

五+++++++++++++++++++++++++++

命名关键字形参:

在函数定义阶段,后面的参数都是命名关键字参数。

特点:

在传值时,必须按照key=value传。并且key必须是命名关键字指定的参数名

def register(x,y,z,**kwargs):

if 'name'not in kwargs or 'age'not in kwargs:

print('用户名与年龄必须使用关键字形式的传值')

return

print(kwargs['name'])

print(kwargs['age'])

register(1,2,3,a='egon',b=18)

register(1,2,3,name='egon',age=18)

更简单

def register(x,y,z,*,name,age):

pass

register(1,2,3,age=18,name='egon')

和下面都一样

def register(x,y,z,*args,name,age):

print(name)

print(args)

print(age)

print(x)

print(y)

print(z)

register(1,2,3,4,5,6,7,age=18,name='egon')

输出

egon

(4, 5, 6, 7)

18

1

2

3

位置参数:

关键字参数:

资料3:

出现这个bug的原因在于参数位置不正确,关键字参数必须跟随在位置参数后面! 因为python函数在解析参数时, 是按照顺序来的, 位置参数是必须先满足, 才能考虑其他可变参数.

例如:

1
 
  1. for line in open('../prepare/train_data/train_data0.txt',encoding='utf-8','r').readlines():

  2. corpus.append(line)

这是错误的,正确的应该是

1
 
  1. for line in open('../prepare/train_data/train_data0.txt','r',encoding='utf-8').readlines():

  2. corpus.append(line)