一、 定义函数的三种类型
a. 有参函数:参数是函数代码用来接收外部传入值的
def max2(x,y): #x=100,=y101
    if  x > y:
         print(x)
     else:
         print(y)

    max2(100,101)b. 无参参数:当函数体的代码逻辑不需要函数的调用者掺入值的情况下,就无参
#无参参数相对用的不多
def interactive():
    name=input('username>>: ').strip()
    pwd=input('password>>: ').strip()
     print(name,pwd)

    interactive() #定义时无参,意味着调用时也无须传入参数c. 空函数:函数体为pass
def auth():
     """
    这是一个认证功能
    :return:
    """
    pass

def put():
     """
    上传功能
    :return:
    """
    pass

def get():
     """
    下在功能
    :return:
    """
    pass

def ls():
     """
    list contents
    :return:
    """
    passd. 练习
uname_of_db='egon'
pwd_of_db='123'

def interactive():
    uname_of_db=input('username: ').strip()
    pwd_of_db=input('password: ').strip()
     return uname_of_db, pwd_of_dbdef auth(uname,pwd):
     if uname==uname_of_db and pwd==pwd_of_db:
         print('登陆成功')
     else:
         print('登陆失败')

res=interactive()
x=res[0]
y=res[1]
auth(x,y)#函数的功能越细越好。比如,认证可分为输入和对比
二. 函数的调用
函数的使用必须遵循:先定义后调用的原则
注意:没事先定义函数而直接调用,就相当于引用一个不存在的变量名
定义阶段:在定义阶段只检测语法,不执行函数体代码
调用阶段:根据函数名找到函数的内存地址,然后执行函数体代码
运行以下代码,不报错,因为没有语法错误
def func():
   xxx运行以下代码,报错,因为有语法错误
def func():
  print('sadfsadf'运行以下代码,不报错,因为定义在调用之前,若将foo()前移,则报错
def bar():    #定义阶段
  print('from bar')
def foo():
  print('from foo')
  bar()

foo()      #调用阶段三 调用函数的三种形式
a. 语句形式:foo()
def func():
     print('from func')

func()b. 表达式形式:3*len('hello')
def max2(x,y):
     if x > y:
         return x
     else:
         return y

res=max2(10,3)
print(res)c. 当中另外一个函数的参数:range(len('hello'))
def max2(x,y):
     if x > y:
         return x
     else:
         return y

res=max2(max2(10,3),11)
print(res)四. 函数的返回值
什么时候应该有返回值:函数体代码运行完毕后需要有一个返回结果给调用者
返回值的三种形式
没有return,返回值为None
def func():
     pass

res=func()
print(res)return后跟一个值,返回该值本身
def func1():
     return 1

res=func1()
print(res)return可以逗号分隔,返回多个值,会返回一个元组给调用者
def func2():
     return 1,2,[1,2,3]   #等同于return (1,2,[1,2,3])

res=func2()
print(res)return的注意点:
return返回值的值,没有类型限制
return是函数结束的标志,函数内可以写多个return,但执行一次,函数就立刻结束,并把return后的值作为本次调用的返回值
def func3():
     print('first')
     return 1    #return 1 然后结束
     print('second')
     return 2
     print('third')
     return 3

res=func3()
print(res)五、形参与实参
1、形参与实参是什么?
   形参(形式参数):指的是在定义函数时,括号内定义的参数,形参其实就变量名
   实参(实际参数):指的是在调用函数时,括号内传入的值,实参其实就变量的值
    #x,y是形参
    def func(x,y): #x=10,y=11
        print(x)
        print(y)
    #10,11是实参
    func(10,11)
2、注意:
    实参值(变量的值)与形参(变量名)的绑定关系只在函数调用时才会生效/绑定
    在函数调用结束后就立刻解除绑定
六、位置参数
1. 位置参数(分两种: 位置形参,位置实参)
    位置即顺序,位置参参数指的就是按照从左到右的顺序依次定义的参数
2. 在定义函数时,按照位置定义的形参,称为位置形参
def foo(x,y,z):
print(x,y,z)
注意:位置形参的特性是:在调用函数时必须为其传值,而且多一个不行,少一个也不行
# foo(1,2) #TypeError: foo() missing 1 required positional argument: 'z'
# foo(1,2,3,4) #TypeError: foo() takes 3 positional arguments but 4 were given
3. 在调用函数时,按照位置定义的实参,称为位置实参
注意:位置实参会与形参一一对应
foo(1,3,2)
七、关键字参数
1、什么是关键字参数:在调用函数时,按照key=value的形式定义的实参,称为关键字参数
def foo(x,y,z): #x=1,y=2,z=3
print(x,y,z)
foo(x=1,y=2,z=3)
   注意:
相当于指名道姓地为形参传值,意味着即便是不按照顺序定义,仍然能为指定的参数传值
      foo(y=2,x=1,z=3)
在调用函数时,位置实参与关键字实参可以混合使用,但必须
遵循形参的规则
不能为同一个形参重复传值
位置实参必须放到关键字实参的前面
八、默认参数
1 默认参数:在定义阶段,已经为某个形参赋值,那么该形参就称为默认参数
'''
  注意:
定义阶段已经有值,意味着调用阶段可以不传值
位置形参必须在默认参数的前面
默认参数的值只在定义阶段赋值一次,也就是说默认参数的值再定义阶段就固定死了
  m=10
  def foo(x,y=m):
      print(x,y)
  m='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
  foo(1)      #1,10
  foo(1,11)   #1, 11
记住:默认参数的值应该设置为不可变类型(数字,字符串,元组)
  def register(name,hobby,l=[]): #name='wxx',hobby='play'
      l.append(hobby) #l=['play']
print(name,l) # wxx ['play']
错误示范, 因为列表是可变类型,值变id不变,致后面的输出受前面的影响
#函数的调用最好为独立的个体,不要影响到其他组成部分
   register('wxx','play')  # wxx ['play']
   register('alex','read')  # alex ['play'] ['read'] 
   register('egon','music') # egon ['play'] ['read'] ['music']
若一定用可变类型做默认参数,可参考以下修改方式
   register('wxx','play',[])  # wxx ['play']
   register('alex','read',[])  # alex ['read']
   register('egon','music',[]) # alex ['music']
若一定用可变类型做默认参数,可参考以下修改方式
   def register(name,hobby,l=None):
       if l is None:
           l=[]
       l.append(hobby) #l=['play']
       print(name,l) # wxx ['play']
    register('wxx','play')  # wxx ['play']
    register('alex','read') # alex ['read']
    register('egon','music') # alex ['music']
2. 应用:
对于经常需要变化的值,需要将对应的形参定义成位置形参
  对于大多数情况值都一样的情况,需要将对应的形参定义成默认形参
九、可变参数
1. 什么是可变长度参数: 可变长度指的参数的个数可以不固定,实参有按位置定义的实参和按关键字定义的实参,所以可变长的实参指的就是按照这两种形式定义的实参个数可以不固定,然而实参终究是要给形参传值的,所以形参必须有两种对应的解决方案来分别处理以上两种形式可变长度的实参
  1) 实参里包含*与**(实参中一旦见到*,先将其打散)
*会将溢出的位置实参全部接收,然后保存成元组的形式赋值给args
  def foo(x,y,z,*args): #args=(4,5,6,7,8)
      print(x,y,z)
      print(args)
  foo(1,2,3,4,5,6,7,8,)
**会将溢出的关键字实参全部接收,然后保存成字典的形式赋值给kwargs
  def foo(x,y,z,**kwargs): # kwargs={'c':3,'a':1,'b':2}
      print(x,y,z)
      print(kwargs)
  2) 实参里包含*与**
  a. 一旦碰到实参加*,就把该实参的值打散
  def foo(x,y,z,*args): #args=([4,5,6,7,8],)
      print(x,y,z)
print(args)
  foo(1,2,3,*[4,5,6,7,8]) #foo(1,2,3,4,5,6,7,8)
  foo(1,2,3,*(4,5,6,7,8)) #foo(1,2,3,4,5,6,7,8)
  foo(1,2,3,*'hello') #foo(1,2,3,'h','e','l','l','o')#
  def foo(x,y,z):
      print(x,y,z)
 foo(*[1,2,3]) #foo(1,2,3)
 foo(*[1,2,3,4]) #foo(1,2,3,4) #报错
 foo(*[1,2,]) #foo(1,2,) #报错
  b. 一旦碰到实参加**,就把该实参的值打散
  def foo(x,y,z,**kwargs):
      print(x,y,z)
      print(kwargs)
  foo(1,2,3,**{'a':1,'b':2}) #foo(1,2,3,b=2,a=1)
  def foo(x,y,z):
      print(x,y,z)
  foo(1,**{'z':3,'y':2}) #foo(1,z=3,y=2)
  foo(1,**{'z':3,'y':2,'x':111}) #foo(1,z=3,y=2,x=111)
2. 组合使用
def index(name,age,gender):
    print('welcome %s %s %s' %(name,age,gender))
def wrapper(*args,**kwargs): #args=(1,2,3),kwargs={'x':1,'y':2,'z':3}
    # print(args)
    # print(kwargs)
    index(*args,**kwargs) # index(*(1,2,3),**{'x':1,'y':2,'z':3}) # index(1,2,3,z=3,y=2,x=2)
# *args一定要在*kwargs前面
wrapper(1,2,3,x=1,y=2,z=3)
#虽然直接调用的是wrapper函数,但是主要卡的是wrapper中index函数的参数的类型。以上是经典的引用,请记住。