# 一. 函数参数分为两大类
# 形参:在函数定义阶段括号定义的参数(变量名),称之为形式参数
#因为函数定义阶段只是用一个符号表示表示一个参数,并没有具体的值,所以称之为形式参数,形参
# 实参:在函数调用阶段括号内传入值(变量值),称之为实际参数
#在函数调用阶段,给了具体的数值,而不再是一个符号化的形式,所以这个叫做实际参数实参
# 形参与实参的关系:
# 在调用函数时实参的值会绑定给形参,这种绑定关系在函数调用结束立即解除
# def func(x,y,z):####这里的xyz都是形参
# # x=1
# # y=2
# # z=3
# print(x)
#
# func(1,2,3)####这里的123都是实参
# 二: 形参与实参的细分
# 1. 位置参数:
# 位置形参:指的是在函数定义阶段按照从左到右的位置顺序依次定义的形参
# 特点: 必须被传值,多一个不行,少一个也不行
# def func(x,y,z):
# print(x)
# print(y)
# print(z)
# func(1,2,3,4)####错误的调用,多给了一个位置实参
# func(1,2,)#####错误调用,少给了一个位置实参
# func(1,2,3)#####正确调用,这个参数不能多也不能少,具体的个数根据函数定义的时候的语法来
# 位置实参: 指的是在函数调用阶段按照从左到右的顺序依次传入的值
# 特点: 与形参一一对应
# func(1,2,3)
# func(2,3,1)
# 2.关键字参数(单指关键字实参): 在函数调用阶段,按照key=value形式为指定的形参赋值
# 特点: 可以完全打乱传值的顺序,但仍然能指名道姓地为指定的形参赋值
# 注意:可以混用位置实参与关键字实参,但是
# 1. 不能为同一个形参重复赋值
# 2. 位置实参必须放在关键字实参的前面
# def func(x,y,z):
# print(x)
# print(y)
# print(z)
# func(1,2,3)
# func(z=3,x=1,y=2)
# func(1,x=3333,y=2,z=3)
# func(1,y=2,z=3)
# func(x=1,2,z=3)
# 3.默认参数(默认形参): 在函数定义阶段,就已经为形参赋值
# 特点: 在定义阶段就已经有值,意味着在调用阶段可以不用传值
# 注意: 可以混用位置形参与默认形参,但是
# 1. 位置形参必须放在默认形参前面
# 2. 默认形参的值只在函数定义阶段赋值一次,定义之后的任何改动都无法影响默认形参的值
# 3. 默认形参的值通常应该是不可变类型
# def func(x,z=1111,y):
# print(x)
# print(y)
# print(z)
# m=1000
# def func(x,y,z=m):
# print(x)
# print(y)
# print(z)
#
# m=2000
# func(1,2)
# def func(x,y,hobbies=None):
# if hobbies is None:
# hobbies=[]
# hobbies.append(y)
# print('%s 的爱好列表 %s' %(x,hobbies))
#
# func('egon','read',)
# func('alex','piao',)
# func('wxx','eat',)
# def func(x,y,z=1111):
# print(x)
# print(y)
# print(z)
#
# func(1,2,z=33333)
# def register(name,age,sex='male'):
# print(name)
# print(age)
# print(sex)
#
# register('大保健',18,)
# register('小保健',28,)
# register('中保健',38,)
# register('kevin',38,'female')
#4. 可变长参数: 指的是在调用函数时传入的值(实参)个数不固定,而实参无非两种,一种位置实参
# 另外一种就是关键字实参,针对这两种形式实参的个数不固定,对应着形参也应该有两种解决方案
# 来分别应对溢出位置实参(*)与关键字实参(**)
#*的用法
#一个* 如果用在定义函数阶段,用在形参里面,会把后面传递过来的多了的参数,自动存成元组的形式
# 如果用在函数的调用阶段,用在实参里面,会把后面需要传递的实参,列表,元组,字典等自动打散传递给前面的形参
#I: 在形参中出现*: *会将溢出的位置实参存成元组的形式,然后赋值紧跟其后的形参z
# def func(x,y,*z): #z=(3,4,5)
# print(x)
# print(y)
# print(z)
#
# func(1,2,3,4,5)
#II: 在实参中出现*: 但凡是在实参中出现*,都将*后的值打散成位置实参,然后再进行赋值
def func(x,y,z):
print(x)
print(y)
print(z)
#func(1,2,*[3,4,5,6,7])
#func(1,2,3,4,5,6,7)这种调用会报错,因为打散之后会有七个实参传递给前面的三个形参,多了四个
# func(*[3,4,5]) #func(3,4,5)#####这个调用刚刚好,因为一个列表打散之后变成三个实参刚好传递给前面的是哪个形参
#func(*{'a':1,'b':2,'c':3}) #func('a','b','c')###字典打散之后只会讲key传递给前面的实参
#**的用法
#这个是针对关键字参数
#I: 在形参中出现**: **会将溢出的关键实参存成字典的形式,然后赋值紧跟其后的形参z
# def func(x,y,**z): #z={'m':3,'a':1,'b':2}
# print(x)
# print(y)
# print(z)
#
# func(1,y=2,m=3,a=1,b=2)
#II: 在实参中出现**: 但凡是在实参中出现**,都将**后的值打算成关键字实参,然后再进行赋值
#一般是字典才能打散成关键字参数
# def func(x,y,z):
# print(x)
# print(y)
# print(z)
# func(1,2,**{'a':1,'b':2,'c':3}) #func(1,2,a=1,c=3,b=2)
# func(**{'y':1,'z':2,'x':3}) #func(z=2,x=3,y=1)
# 传给外层函数wrapper的参数格式如何原封不动地转嫁给wrapper内部调用的函数index???,解决方案如下
# def index(a,b,c):
# print('from index',a,b,c)
#
# def wrapper(*args,**kwargs): #args=(1,2,3)
kwargs={'x':1,'y':2,'z':3}
#前面的args 表示位置形参,后面传来的位置实参,不管多少,都会以元组的形式存放在这里面
#kwargs表示关键字形参吗,后面传来的关键字实参,不管多少个,都会以字典的形式存放在这里面
#特别的,args 和kwargs都只是一个代指的符号,没有特别的含义,只是我们习惯上用着两个字符串表示而已,
#用其他的字符串也可以,但是一般习惯用这个。稍微记一下就行
#index(*args,**kwargs)
# #index(*(1,2,3),**{'x':1,'y':2,'z':3})
# #index(1,2,3,x=1,y=2,z=3)
# # wrapper(1,2,3,x=1,y=2,z=3)
# wrapper(1,c=3,b=2)
# 5. 命名关键字形参: 在定义函数阶段,但凡是在*与**之间的参数称之为命名关键字形参
# 特点: 必须被传值, 并且在传值时必须按照key=value的形式传值
# def func(x,*,m=111,n,**kwargs):
# print(x)
# print(m)
# print(n)
# print(kwargs)
#
# # func(1,n=3,m=2)
# func(1,n=3)
# def func(x,y=2,*args,m,**kwargs):
# print(x)
# print(y)
# print(args)
# print(m)
# print(kwargs)
# func(1,200,3,4,5,m=111)