#函数的参数分为两大类:
#1 形参: 指的是在定义函数阶段括号内指定变量名,即形参本质就是"变量名"
#2 实参: 指的是在调用函数阶段括号内传入的值,即实参本质就是"值"
# 形参与实参的关系:在调用函数时,会将实参(值)赋值(绑定)给形参(变量名),
# 这种绑定关系在函数调用时临时生效,在调用结束后就失效了
# def foo(x,y): # x=1 y=2
# # x=1
# # y=2
# print(x,y)
#
# foo(1,2)
# 形参与实参的具体分类
# 一 位置参数
# 1.1 位置形参: 在定义函数阶段按照从左到右的顺序依次定义的形参,称之为位置形参
# 注意:但凡按照位置定义的形参,必须被传值,多一个不行,少一个也不行
# def foo(x,y):
# print(x,y)
# foo(1,2)
# foo(1,2,3)
# foo(1,)
# 1.2 位置实参: 在调用函数阶段按照从左到右的顺序依次传入的值,称之为位置实参
# 注意:但凡按照位置定义的实参,会与形参一一对应
# def foo(x,y):
# print(x,y)
#
# foo(2,1)
# 二 关键字参数
#关键字实参: 在调用函数阶段,按照key=value的形式指名道姓地为形参传值
#注意:
#1. 可以完全打乱顺序,但仍然能指名道姓为指定的形参传值
#2. 可以混合使用位置实参与关键字实参,但是必须注意:
# 2.1 位置实参必须放到关键字实参前面
# 2.2 不能对一个形参重复赋值
# def foo(name,age):
# print(name,age)
# foo('egon',18)
# foo(18,'egon')
# foo(age=18,name='egon')
# foo('egon',age=18)
# foo(name='egon',18)
# foo('egon',age=18,name='lxx')
# open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True):
# open('a.txt','w',encoding='utf-8')
# 三 默认参数
#默认参数:指的是在定义函数阶段,就已经为某个形参赋值了,改形参称之为有默认值的形参,简称默认形参
#注意:
#1. 在定义阶段就已经被赋值,意味着在调用阶段可以不用为其赋值
#2. 位置形参应该放到默认形参前面
#3. 默认参数的值在函数定义阶段就已经固定死了
#4. 默认参数的值通常应该是不可变类型
# def foo(x,y=2):
# print(x,y)
# foo(1)
# foo(1,3)
# foo(y=3,x=1)
# def foo(y=2,x):
# print(x,y)
# m=10
# def foo(x,y=m):
# print(x,y)
#
# m=20
# foo(1)
# def register(name,hobby,l=[]):
# l.append(hobby)
# print('%s 的爱好为 %s' %(name,l))
#
# register('alex','piao')
# register('lxx','喝腰子汤')
# register('yxx','不洗澡')
# register('egon','read')
# def register(name, hobby, l=None):
# if l is None:
# l=[]
# l.append(hobby)
# print('%s 的爱好为 %s' % (name, l))
#
# register('alex', 'piao',[])
# register('lxx', '喝腰子汤',[])
# register('yxx', '不洗澡',[])
# register('egon', 'read',[])
# 位置形参vs默认形参
# 对于大多情况下传的值都不相同的,应该定义成位置形参
# 对于大多情况下传的值都相同的,应该定义成默认形参
# def register(name,age,sex='男'):
# print(name,age,sex)
#
# register('李铁蛋',18,)
# register('李银蛋',28)
# register('张铜蛋',38)
# register('刘卤蛋',48)
# register('刘二丫',19,'女')
# 四 可变长度的参数
# 站在实参的角度,参数长度可变指的是在调用函数时,传入的实参值的个数不固定
# 而实参的定义方式无法两种:位置实参,关键字实参,对应着形参也必须有两种解决方案*与**,类分别应对溢出的位置实参与关键字实参
# 1. 在形参中带*:会将调用函数时溢出位置实参保存成元组的形式,然后赋值*后的变量名
# def foo(x,y,*z): #z=(3,4,5,6)
# print(x,y,z)
#
# foo(1,2,3,4,5,6)
# 2. 在实参中带*: 但凡在实参中带*星的,在传值前都先将其打散成位置实参,再进行赋值
# def foo(x,y,*z): #z=(3,4,5,6)
# print(x,y,z)
# foo(1,*[2,3,4,5,6]) # foo(1,2,3,4,5,6)
# def foo(x,y,z):
# print(x,y,z)
# foo(1,*(2,3,4,5,6)) #foo(1,2,3,4,5,6)
# foo(*(1,2,3)) #foo(1,2,3)
# foo(*'hello') #foo()
# foo(*'abc') #foo('a','b','c')
# 3. 在形参中带**:会将调用函数时溢出关键字实参保存成字典的形式,然后赋值**后的变量名
# def foo(x,y,**z): #z={'z':3,'a':1,'b':2}
# print(x,y,z)
#
# foo(1,y=2,a=1,b=2,c=3)
# 4. 在实参中带**: 但凡在实参中带**星的,在传值前都先将其打散成关键字实参,再进行赋值
# def foo(x,y,**z): #z={'a':100,'b':200}
# print(x,y,z)
#
# foo(1,**{'a':100,'b':200,'y':111}) #foo(1,b=200,a=100,y=111)
# def foo(x,y,z):
# print(x,y,z)
#
# foo(**{'y':111,'x':222,'z':333}) #foo(z=333,x=222,y=111)
#5. 规范: 在形参中带*与**的,*后的变量名应该为args,**后跟的变量名应该时kwargs
# def foo(*args,**kwargs): #args=(1,2,3,4,5) kwargs={'a':1,'b':2,'c':3}
# print(args)
# print(kwargs)
# foo(1,2,3,4,5,a=1,b=2,c=3)
# def bar(x,y,z):
# print(x,y,z)
#
# def wrapper(*args,**kwargs): #args=(1,2,3,4,5,6) kwargs={'a':1,'b':2,'c':3}
# bar(*args,**kwargs)
# #bar(*(1,2,3,4,5,6),**{'a':1,'b':2,'c':3}) #bar(1,2,3,4,5,6,a=1,b=2,c=3)
# wrapper(1,2,3,4,5,6,a=1,b=2,c=3)
# !!!!!!!!!!!!!!!当我们想要将传给一个函数的参数格式原方不动地转嫁给其内部的一个函数,应该使用下面这种形式
# def bar(x,y,z):
# print(x,y,z)
#
# def wrapper(*args,**kwargs): #args=(1,2) kwargs={'z':3}
# bar(*args,**kwargs)
# #bar(*(1,2),**{'z':3}) #bar(1,2,z=3)
# wrapper(1,2,z=3) # 虽然调用的是wrapper,但是要遵循的确是bar的参数标准
# 五 命名关键字参数: 放到*与**之间的参数称之为命名关键字参数
# 注意: 命名关键字参数必须按照key=value的形式传值
# def foo(x,y,*args,m,n,**kwargs): #args=(3,4,5,6,7,8)
# print(x,y) # 1,2
# print(args) #(3,4,5,6,7,8)
# print(m,n) #222,333
# print(kwargs)
#
# foo(1,2,3,4,5,6,7,8,n=333,m=222,a=1,b=2)
# def foo(*,x=1,y):
# print(x)
# print(y)
# foo(y=2222,x=1111)
# foo(y=2222)
# def foo(x,y=1,*args,m,**kwargs):
# print(x)
# print(y)
# print(args)
# print(m)
# print(kwargs)