text: (3)什么是函数(函数的定义、形参、实参、默认形参、可变长函数args \ kwargs，私有地址)
  什么是函数函数是指将一组语句的集合通过一个名字(函数名)封装起来，想要执行这个函数，只需调用其函数名即可
  2、使程序变的可扩展
  3、使程序变得易维护
  定义函数的语法
  主要的作用就是接受实参参数的传递，并且被语法调用 #形参只有在被调用时才内存单元，在调用解释时，即可释放所分配的内存单元。
  主要的作用就是讲参数传递给形参 #实参可以是常量、变量、表达式、函数等，无论实参是何种类型的量，在进行函数调用时，他们都必须有确定的值，以便把这些值传递给形参。
  定义函数的形参，并且将实参传递给形参再调用
  def calc(x，y): #def关键词，calc函数名，括号里的是形参
  位置形参:指的是在函数定义阶段按照从左到右的顺序依次定义的形参
  特点: 必须被传值,多一个不行,少一个也不行
  位置实参: 指的是在函数调用阶段按照从左到右的顺序依次传入的值
  特点: 与形参一一对应
  形参也可以是默认值
  默认参数(默认形参): 在函数定义阶段,就已经为形参赋值
  特点: 在定义阶段就已经有值,意味着在调用阶段可以不用传值
  注意: 可以混用位置形参与默认形参,但是
  1、位置形参必须放在默认形参前面
  2、默认形参的值只在函数定义阶段赋值一次,定义之后的任何改动都无法影响默认形参的值
  3、默认形参的值通常应该是不可变类型
  一般情况下，给函数传参数时要按顺序，不想按顺序就可以用关键字参数，只需指定参数名即可(指定了参数名的参数就叫关键参数)，关键字参数必须放在位置参数(以位置顺序确定对应关系的参数)之后
  关键字参数(单指关键字实参): 在函数调用阶段,按照key=value形式为指定的形参赋值
  特点: 可以完全打乱传值的顺序,但仍然能指名道姓地为指定的形参赋值
  注意:可以混用位置实参与关键字实参,但是
  1、不能为同一个形参重复赋值
  2、位置实参必须放在关键字实参的前面
  PS:位置参数就是形参没有指定任何值，所以函数调用时候是按照位置传递的，关键字参数就是指定形参的默认值，所以必须放在位置参数的后面，不然在函数传递时候按照位置传递，会出现错误
  例:def stu_register(name,age,course='Py',counter='CN'): #def是函数定义关键字，结尾必须用:结束，这是强制语法
  stu-register(‘Karl’,course='Py',age=22,counter='JP') #这里Karl没有用赋值去指定赋值给哪一个形参，所以必须对应形参的位置，而后面的实参都有赋值给指定的形参，所以可以不用按位置写
  指的是在调用函数时传入的值(实参)个数不固定,而实参无非两种,一种位置实参
  另外一种就是关键字实参,针对这两种形式实参的个数不固定,对应着形参也应该有两种解决方案来分别应对溢出位置实参(*)与关键字实参(**)
  若在函数的定义时不确定用户想传入多少个参数，就可以使用非固定参数(*args和*kwargs)
  *args会把多传入的参数变成一个元组形式
  def stu(name,age,*args)
  stu('Karl',22)
  输出的结果就是Karl 22() #输出结果后面有一个()，元组符号，这个就是args，因为没有传入任何值，所以就是一个空元组
  **kwargs 会把多传入的参数变成一个dict的形式(字典形式)
  def stu(name,age,*args,**kwargs)
  stu('Karl',22,'cn','Py',sex='male')
  输出结果就是Karl 22 ('cn', 'Py') {'sex': 'male'}
  PS:*args前面是一个*，**kwargs前面是两个*，args传入时候单个字符串即可，而kwargs传入的时候必须以键值(key=value)传入(即字典形式传入)
  PS:*args和**kwargs其实就是前面的*起作用，后面的名称只是约定俗成的名字(可以随便写)，这样所有开发人员看到这个就知道一个是传入元祖，一个是将值变成关键字传入
  PS:在实参中出现*: 但凡是在实参中出现*,都将*后的值打算成位置实参,然后再进行赋值
  PS: 在实参中出现**: 但凡是在实参中出现**,都将**后的值打算成关键字实参,然后再进行赋值
  PS: 在形参中出现**: **会将溢出的关键实参存成字典的形式,然后赋值紧跟其后的形参
  PS: 在形参中出现*: *会将溢出的位置实参存成元组的形式,然后赋值紧跟其后的形参
  例: 利用可变长参数在函数体内调用另一个函数的时候，将实参传递给被调用的函数
  def index(a,b,c): #先定义一个有形参的函数
  print('from index',a,b,c) #执行打印形参
  def wrapper(*args,**kwargs): #这里再定义一个函数带有可变长参数
  wrapper(1,c=3,b=2) #在调用的时候将wrpper实参传递给形参，然后在传递给调用的index的形参
  PS:这里由于wapper函数执行的就是调用index
  列:利用位置参数传递
  def index(a,b,c):
  print('from index',a,b,c) #这个函数执行的操作
  index(name,age,sex) #这里调用函数时候的形参必须是这个函数块的形参
  wrapper('karl','123','male') #这里按照位置实参传递
  PS:这是一个相互传递的过程，调用时候将位置实参传递给形参，然后再传递给调用的函数
  命名关键字形参:
  在定义函数阶段,但凡是在*与**之间的参数称之为命名关键字形参
  特点: 必须被传值, 并且在传值时必须按照key=value的形式传值
  def func(x,*args,m=111,n,**kwargs):
  PS:参数的顺序，先写位置，再写默认(*前面的才是默认参数)，然后写*args，再跟关键字参数(*后面的是带有默认值(无默认值)的关键字参数)，最后跟**kwargs
  返回值(return)
  函数外部代码想要获取函数的执行结果，就可以在函数里用return语句把结果返回
  def stu_register(name,age,course='Py',counter='CN'):
  if age >22: #这里输出一个判定条件，用return返回后面的结果
  s_reg = stu_register(‘Karl’,course='Py',age=22,counter='JP')
  print('年龄太大‘’)
  PS:函数在执行过程中只要遇到return语句，就会停止执行并返回结果，return语句就是函数运行结束的一个语句，如果未在函数中指定return，那么这个函数的返回值为None
  1、在函数中定义的变量就是局部变量，在程序的一开始定义的变量称为全局变量
  2、全局变量的作用域是整个程序，局部变量作用域就是定义该变量的函数
  3、当全局变量与局部变量同名时，在定义局部变量的函数内，局部变量起作用，在其他地方则是全局变量起作用
  print('beore change:',name)
  print('after change:',name)
  作用域(scope)，通常来说，一段 程序代码中所用到的名字并不总是有效/可用的，而限定这个民资的可用性的代码范围就是这个名字的作用域
  def change_name(): #这里没有定义形参
  global name #用global 函数声明name就是全局变量
  name ='Karl'
  change_name() #用为用了global全局函数声明变量name，所以这里也可以打印name
  name = 'Alex'
  name = 'Alex2'
  name = 'Alex3'
  print('第三层:',name)
  change_name2()
  print('第二层:',name)
  print('最外层:',name)
  PS:函数嵌套的调用也是需要按照层级来依次调用的，如果在第二层调用第三层则会报错，所以第二层只能调用第二层
  匿名函数(lambda)
  匿名函数就是不需要显式的指定函数名
  def calc(x,y)
  print(clac(2,5))
  calc = lambda x,y:x**y
  匿名函数的主要用法是和其他函数搭配使用
  res = map(lambda x:x**2,) #这里直接定义了一个变量的计算过程，用lambda函数定义X，冒号后面传递一个计算的方式，最后用列表定义x的值
  包:该内部函数需要访问一个名字,该名字属于外层函数作用域的(强调:不是全局作用域)
  为何要用闭包函数
  为了实现装饰器
  如何用闭包函数
  def outter(name): #定义函数
  x = ‘456’
  def inner(): #函数内嵌套一个函数
  print(‘成功调取这一层值’)
  return inner #返回函数，不能加括号，加了括号就是调取函数的功能，不加括号就是返回一个函数的内存空间地址
  f = outter() #outter内最后将inner的内存地址做返回结果，所以outter函数的只会返回inner的内存空间地址，这里outter就是inner的内存空间地址并且赋值给f
  f() #这里调用f就获得了inner局部变量的值
  PS:首先定义一个函数，然后在下层定义一个函数，最后返回下层函数的内存空间地址给全局函数，然后将全局函数的赋值给一个变量
  PS:由于全局函数outter下层有一个打印值，所以在打印f的时候也会打印出来
  只要满足以下任意一个条件，即是高阶函数
  1、接受一个或多个函数作为输入
  2、return返回另一个函数
  2、每次进入更深一层的递归时，问题规模相比上次递归都应有所减少
  3、递归效率不高，栈就会增加一层栈帧，每当函数返回，栈就会减一层栈。