一、定义

函数是将可以被反复使用的、用在实现单一或相关联功能的代码封装或组织在一起,形成一个独立的程序单位,并为该程序单位定义一个相应的名称,这样的程序单位称为函数,该名称为函数名。
为了更好的管理大规模且反复被重用的大段代码,Python引入了模块的概念,将大段程序或多个函数段封装成一个文件,可以反复使用,进一步提出了包的概念。

1.1 定义

定义语法
def <函数名> ([参数列表]):
<函数体>
def 关键字用来定义函数
函数名 由用户定义的任意有效的标识符
函数名 后面的圆括号必须有,其中参数列表中的参数由变量充当,可以有多个参数,参数之间用逗号分割。
函数定义的时候,括号后面的冒号(:)必不可少,函数体是至少一句语言的代码块。
在主程序中直接通过函数名嗲用函数,函数名后面的也必须有括号,调用函数的过程就是执行函数中语句的过程

  1. def my_len(): ##定义函数
  2. s = 'hello world'
  3. length = 0
  4. for i in s:
  5. length = length + 1
  6. print(length)
  7. my_len() ##调用函数

1.2 函数名的本质

函数名本质上就是函数的内存地址

  1. func() # 函数名就是内存地址
  2. func2 = func # 函数名可以赋值
  3. func2()
  4. l = [func,func2] # 函数名可以作为容器类型的元素
  5. print(l)
  6. for i in l:
  7. i()
  8. def func():
  9. print(123)
  10. def wahaha(f):
  11. f()
  12. return f #函数名可以作为函数的返回值
  13. ret = wahaha(func) # 函数名可以作为函数的参数
  14. ret()

二、函数的调用

格式:
<函数名>(<参数列表>)
函数名为前面def语句定义的函数,参数列表中的参数必须有确定的值,为实参,实参可以由多个参数组成,中间用逗号分割。
调用函数的方式:
1.直接调用函数名,将函数语句完整执行一遍,调用程序往往不需要函数的返回值
2.函数作为表达式的一部分出现,调用程序将使用函数的返回值,并参与表达式的数据计算
3.函数被嵌套在另一个函数中,函数的返回值被当作另外函数的实参使用。

  1. ##求阶乘的函数
  2. def jiechen(x):
  3. n = 1
  4. for i in range(1,1+x):
  5. n = n * i
  6. return n
  7. y = int(input("输入一个整数: "))
  8. if y < 0:
  9. print("error")
  10. else:
  11. print('y的阶乘:',jiechen(y))
  12. ##结果
  13. 输入一个整数: 5
  14. y的阶乘: 120

三、 参数

3.1 参数传递的方式

当Python中的主程序调用函数的时候,实参的值传递给形参,实际上是将实参所指向的对象的地址传递给了形参。
如果传递的对象是不可变对象(eg:数值、字符、元组等),在函数体中形参值的变化不会影响到实参。
如果传递的对象是可变对象(eg:列表、字典等),在函数中形参值得变化会影响到实参。

  1. ##传递不可变对象,形参的变化不会影响到实参
  2. def add(x):
  3. print("形参x的初始值为:",x)
  4. x+=1
  5. print("形参x的变化后的值为:",x)
  6. y = 4
  7. add(y)
  8. print('实参y的值为:',y)
  9. ##结果
  10. 形参x的初始值为: 4
  11. 形参x的变化后的值为: 5
  12. 实参y的值为: 4
  13. ##传递可变对象,形参得变化会影响到实参
  14. def change(x):
  15. print("形参x的初始值为:",x)
  16. x.append(3)
  17. print("形参x的变化后的值为:",x)
  18. y = [4]
  19. change(y)
  20. print('实参y的值为:',y)
  21. ##结果
  22. 形参x的初始值为: [4]
  23. 形参x的变化后的值为: [4, 3]
  24. 实参y的值为: [4, 3]
  25. def change(x):
  26. print("形参x的初始值为:",x)
  27. x.clear()
  28. print("形参x的变化后的值为:",x)
  29. y = {4:5}
  30. change(y)
  31. print('实参y的值为:',y)
  32. ##结果
  33. 形参x的初始值为: {4: 5}
  34. 形参x的变化后的值为: {}
  35. 实参y的值为: {}

3.2 形参和实参

3.2.1 形参

在函数定义的时候出现的参数,可以看作是一个占位符,它没有数据,只能等到函数被调用的时候接收传递进来的数据,被称为形式参数。
形参出现在函数的定义中,在整个函数体中都可以使用,离开该函数就不可使用

3.2.2 实参

函数被调用的时候给出的参数,包含实实在在的数据,会被函数内部的代码使用,被称为实际参数。

在调用函数的时候,实参的值必须是确定的,该值将以参数传递的形式传递给函数中的形参。

3.2.3 实参和形参的特点

1.形参变量只有在函数被调用的时候才会分配内存,调用结束的时候立刻释放内存,所以形参只有在函数内部有效,不能再函数外部使用
2.实参可以是常量、变量、表达式、函数等,无论实参是何种类型的数据,在进行函数调用的时候,都必须由确定的值,以便把这些值传递给形参,需要提前用赋值、输入等办法使实参获得确定值。
3.实参和形参在数量上、类型上、顺序上必须严格一致,否则会发生“类型不匹配”的错误。
4.函数调用中发生的数据传递时单向的,只能把实参的值传递给形参,不能把形参的值反向传递给实参。在函数调用的过程中,形参值发生改变的时候,实参的值不会发生改变。

3.3 位置参数和关键字参数

3.3.1 位置参数

Python中要求调用函数时参数的个数、位置和顺序要与函数定义中的形参一致,这种参数被称为位置参数。
位置参数也叫必须参数,要求用户必须以正确的顺序传入函数。
如果实参的个数和形参的个数不一致的时候,就会报错

  1. def posi_args(a,b):
  2. print("第一个参数:",a)
  3. print("第二个参数: ",b)
  4. posi_args(100,200)
  5. posi_args(200,100)
  6. ##结果
  7. 第一个参数: 100
  8. 第二个参数: 200
  9. 第一个参数: 200
  10. 第二个参数: 100
  11. def posi_args(a,b):
  12. print("第一个参数:",a)
  13. print("第二个参数: ",b)
  14. posi_args(100)
  15. posi_args(200,100)
  16. ##结果,报错
  17. posi_args(100)
  18. TypeError: posi_args() missing 1 required positional argument: 'b'

3.3.2 关键字参数

在调用函数的时候,可以明确指定参数值传递给哪个形参,这样的参数被称为关键字参数。
在设置函数的参数的时候,可以不按照它们的位置排列顺序,Python解释器可以用参数名进行匹配。
但是,实参和形参的数量还是要保持一致,否则就会报错。

  1. def posi_args(a,b):
  2. print("传递给a的值为:",a,"传递给b的值为: ",b)
  3. posi_args(a=100,b=200)
  4. posi_args(b=200,a=100)
  5. ##结果
  6. 传递给a的值为: 100 传递给b的值为: 200
  7. 传递给a的值为: 100 传递给b的值为: 200
  8. def posi_args(a,b):
  9. print("传递给a的值为:",a,"传递给b的值为: ",b)
  10. posi_args(a=100,b=200)
  11. posi_args(b=200)
  12. ##结果,实参和形参的数目不一致,会报错
  13. 传递给a的值为: 100 传递给b的值为: 200
  14. File "D:/pythontest/geshu.py", line 482, in <module>
  15. posi_args(b=200)
  16. TypeError: posi_args() missing 1 required positional argument: 'a'

用户可以将位置参数与关键字参数混合使用,但必须将位置参数放在关键字参数的前面。

  1. def posi_args(a,b):
  2. print("传递给a的值为:",a,"传递给b的值为: ",b)
  3. posi_args(100,b=200)
  4. ##结果
  5. 传递给a的值为: 100 传递给b的值为: 200
  6. ##位置参数放在关键字参数后面会报错
  7. def posi_args(a,b):
  8. print("传递给a的值为:",a,"传递给b的值为: ",b)
  9. posi_args(a=100,200)
  10. ##结果
  11. posi_args(a=100,200)
  12. ^
  13. SyntaxError: positional argument follows keyword argument

3.4 默认值参数

在有些特殊情况下,实参也可以少于形参,eg:设置了默认值参数。
Python允许在创建函数的时候为形参指定默认值。
调用函数的时候,如果没有传递参数,则会使用默认参数值,也可以通过显式赋值方式改变默认值。
指定了默认值的形参也被称为可选参数,没有指定默认值的形参被称为必选参数。
带有默认值的参数的函数定义:
def <函数名>(……<形参=默认值>……)
<函数体>
在定义函数的时候,必须保证位置参数在前,默认值参数在后。
默认值参数必须指向不可变对象,可变对象不能作为默认值参数的值,不可将列表作为参数的默认值。

  1. def power(x,n=2):
  2. s=1
  3. for i in range(1,n+1):
  4. s = s*x
  5. return s
  6. print(power(5)) ## 形参n为默认值5
  7. print(power(2,5)) ## 改变默认值n为5
  8. ##结果
  9. 25
  10. 32
  11. ##多个默认参数同时使用
  12. def add_three(x,y=3,z=5):
  13. s=x+y+z
  14. print(s)
  15. add_three(5)
  16. add_three(1,2,3)
  17. ##结果
  18. 13
  19. 6

3.5 可变参数

在Python中,可以定义可变参数,不事先指定参数的数量,调用函数的时候,可变参数可以接收任意多个参数。

3.5.1 单星号参数 *

通过带星号(*)的参数定义接收可变数量的参数,可变参数在函数调用的时候自动组装为一个元组。

  1. def sq_sum(*number):
  2. print(type(number))
  3. print("可变参数 number的值为:",number)
  4. sum = 0
  5. for i in number:
  6. sum = sum + i * i
  7. print("平方和为:",sum)
  8. return sum
  9. sq_sum(1,2,3)
  10. sq_sum(2)
  11. sq_sum()
  12. ##结果
  13. <class 'tuple'>
  14. 可变参数 number的值为: (1, 2, 3)
  15. 平方和为: 14
  16. <class 'tuple'>
  17. 可变参数 number的值为: (2,)
  18. 平方和为: 4
  19. <class 'tuple'>
  20. 可变参数 number的值为: ()
  21. 平方和为: 0

3.5.2 双星号参数 **

通过带双星号()的参数定义接收可变数量的参数,可变参数在函数调用的时候自动组装为一个字典。
如果一个函数定义中的最后一个形参有双星号(
)前缀,所有正常参数之外的其他关键字参数都将被放置在一个字典中传递该参数。

  1. def school(x,**y):
  2. print(type(y))
  3. print("y的值",y)
  4. print(type(x))
  5. print("x的值",x)
  6. for i in y:
  7. print(i+":"+str(y[i]))
  8. school("abc",a="lhuan",b="lars",c="mj")
  9. school("def",a="lll",b="daidai",c="ccy",d="gyon")
  10. ##结果
  11. <class 'dict'>
  12. y的值 {'a': 'lhuan', 'b': 'lars', 'c': 'mj'}
  13. <class 'str'>
  14. x的值 abc
  15. a:lhuan
  16. b:lars
  17. c:mj
  18. <class 'dict'>
  19. y的值 {'a': 'lll', 'b': 'daidai', 'c': 'ccy', 'd': 'gyon'}
  20. <class 'str'>
  21. x的值 def
  22. a:lll
  23. b:daidai
  24. c:ccy
  25. d:gyon

当参数本身就是一个序列(元组、列表、字典、集合)的时候

  1. def sq_sum(*number):
  2. print(type(number))
  3. print("可变参数 number的值为:",number)
  4. sum = 0
  5. for i in number:
  6. sum = sum + i * i
  7. print("平方和为:",sum)
  8. return sum
  9. nums=[1,2,3] ##参数本身就是列表
  10. sq_sum(nums)
  11. ##结果会报错
  12. File "D:/pythontest/geshu.py", line 532, in <module>
  13. sq_sum(nums)
  14. File "D:/pythontest/geshu.py", line 528, in sq_sum
  15. sum = sum + i * i
  16. TypeError: can't multiply sequence by non-int of type 'list'
  17. def sq_sum(*number):
  18. print(type(number))
  19. print("可变参数 number的值为:",number)
  20. sum = 0
  21. for i in number:
  22. sum = sum + i * i
  23. print("平方和为:",sum)
  24. return sum
  25. nums=[1,2,3]
  26. sq_sum(nums[0],nums[1],nums[2]) ##这个列表的值,在函数内部将这三个值组装成一个元组
  27. ##结果
  28. <class 'tuple'>
  29. 可变参数 number的值为: (1, 2, 3)
  30. 平方和为: 14

3.5.3 参数传递时的序列解包

对于序列类型的实参,可以在前面添加一个星号(*),将实参的序列进行解包,然后在调用函数的时候传递给可变参数。
如果不将实参进行序列解包而直接传递,实参和形参个数不相等,程序将会出错。

  1. ##序列解包后传递给可变参数
  2. def sq_sum(*number):
  3. print(type(number))
  4. print("可变参数 number的值为:",number)
  5. sum = 0
  6. for i in number:
  7. sum = sum + i * i
  8. print("平方和为:",sum)
  9. return sum
  10. nums=[1,2,3]
  11. sq_sum(*nums)
  12. ##结果
  13. <class 'tuple'>
  14. 可变参数 number的值为: (1, 2, 3)
  15. 平方和为: 14
  16. ##序列解包后传递给多个普通参数
  17. def add_three(x,y,z):
  18. s=x+y+z
  19. print(s)
  20. list=[1,2,3]
  21. add_three(*list)
  22. tup=(1,2,3)
  23. add_three(*tup)
  24. dic={1:'x',2:'y',3:'z'}
  25. add_three(*dic)
  26. set=(1,2,3)
  27. add_three(*set)
  28. ##结果
  29. 6
  30. 6
  31. 6
  32. 6

四、 函数的返回值

返回值是指函数返回给主调用程序的结果,通过函数中的关键字return实现,return后面的值就是函数的返回值。
不带参数值的return语句,返回None。
return <表达式列表>
return后面可以返回多个表达式的值,执行完return语句后函数结束。

  1. def cal(x,y):
  2. s = x+y
  3. a = x*y
  4. p = x**y
  5. return s,a,p
  6. print(cal(5,6))
  7. ##结果
  8. (11, 30, 15625)
  9. 函数内部没有输出语句,调用函数的时候必须使用print语句才能输出函数的返回值

五、 变量的作用域

变量的作用域指变量的作用范围,作用域不相同,即使变量名相同,变量之间也不相互影响。
根据变量作用域的不同,可以将变量分为全局变量(Global variales)和局部变量(Local variales)。
全局变量的作用域是整个程序,这种变量在整个程序范围内都可以被引用。
局部变量定义在函数体内部,作用域只是在函数体内,只能在函数内被引用,一旦程序执行离开函数,变量将失效,不可引用。

5.1 全局变量

1.在主程序中定义的变量是全局变量,在主程序中出现在赋值语句等等号左侧的变量是全局变量
2.用global声明的变量是全局变量,global语句可以声明多个变量为全局变量,变量中间用逗号(,)隔开
3.没有用global语句声明,但出现在函数内赋值号右侧且不是函数参数的变量是全局变量,或者说,如果在函数体内只是引用了某个变量的值而没有为其赋新值,则该变量是全局变量

5.2 局部变量

1.函数的行输参数是局部变量
2.在函数体内部赋值号左侧出现的变量是局部变量,作用域在该函数体内部,或者说只要子啊函数体内部有为变量赋值的操作,该变量即为局部变量。

  1. x = 100 ##全局变量
  2. def changex():
  3. x=200 ##局部变量
  4. return x
  5. print(x)
  6. print(changex())
  7. x = 100
  8. def changex():
  9. global x ##声明全局变量
  10. x=200
  11. return x
  12. print(x)
  13. print(changex())
  14. print(x)

5.3 global

  1. 声明一个全局变量。
  2. 在局部作用域想要对全局作用域的全局变量进行修改时,需要用到 global(限于字符串,数字)。

对可变数据类型(list,dict,set)可以直接引用不用通过global

globals() 函数返回一个全局变量的字典,包括所有导入的变量
locals() 返回字典类型的局部变量**

5.4 nonlocal

  1. 不能修改全局变量。
  2. 在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。
  3. nonlocal 只能用于局部变量 找上层中离当前函数最近一层的局部变量
  4. 声明了nonlocal的内部函数的变量修改会影响到 离当前函数最近一层的局部变量

    对全局无效
    对局部 也只是对 最近的 一层 有影响

  1. #例子1
  2. a = 1
  3. def outer(): #声明了global后只修改全局变量不会修改局部变量
  4. a = 1
  5. def inner():
  6. b =2
  7. print(a,'inner')
  8. def inner2():
  9. global a
  10. a += 1
  11. print('###',a)
  12. inner2()
  13. inner()
  14. outer()
  15. print('全局',a)
  16. 例子2
  17. a = 1
  18. def outer():
  19. a = 1
  20. def inner():
  21. a = 2
  22. def inner2():
  23. nonlocal a #声明了一个上面第一层局部变量
  24. a += 1 #不可变数据类型的修改
  25. inner2()
  26. print('##a## : ', a)
  27. inner()
  28. print('**a** : ',a)
  29. outer()
  30. print('全局 :',a)
  31. 例子3
  32. a = 0
  33. def outer():
  34. a = 1
  35. def inner():
  36. a = 2
  37. def inner2():
  38. nonlocal a
  39. print(a)
  40. inner2()
  41. inner()
  42. outer()
  43. print('全局',a)
  1. a=2
  2. def out():
  3. a=2
  4. def in1():
  5. a = 4
  6. def in2():
  7. nonlocal a
  8. a += 3
  9. in2()
  10. print("ccc",a)
  11. in1()
  12. print("111",a)
  13. out()
  14. print("ddd",a)
  1. ccc 7
  2. 111 2
  3. ddd 2

ps:对于不可变数据类型 在局部可是查看全局作用域中的变量
但是不能直接修改
如果想要修改,需要在程序的一开始添加global声明
如果在一个局部(函数)内声明了一个global变量,那么这个变量在局部的所有操作将对全局的变量有效

5.函数的嵌套和作用域链

函数的嵌套调用
内部函数可以使用外部函数的变量

  1. def max(a,b):
  2. return a if a>b else b
  3. def the_max(x,y,z): #函数的嵌套调用
  4. c = max(x,y)
  5. return max(c,z)
  6. print(the_max(1,2,3))
  7. def outer():
  8. def inner():
  9. print('in inner')
  10. inner() #不加这一句就无法使用
  11. outer()
  12. def outer():
  13. a = 1
  14. def inner():
  15. print(a)
  16. print('in inner')
  17. inner()#不加这一句就无法使用
  18. outer()

作用域链
在内部函数使用变量的时候,是从小局部到大局部到全局到内置的过程,一级一级往上找,找到最近的一个就使用

  1. def f():
  2. a = 1
  3. def f1():
  4. print(a)
  5. f1()
  6. f()
  7. def f():
  8. a = 1
  9. def f1():
  10. def f2():
  11. print(a)
  12. f2()
  13. f1()
  14. f()
  15. def f():
  16. a = 1
  17. def f1():
  18. a = 3
  19. def f2():
  20. print(a)
  21. f2()
  22. f1()
  23. f()

6.函名的本质

函数名本质上就是函数的内存地址

  1. func() # 函数名就是内存地址
  2. func2 = func # 函数名可以赋值
  3. func2()
  4. l = [func,func2] # 函数名可以作为容器类型的元素
  5. print(l)
  6. for i in l:
  7. i()
  8. def func():
  9. print(123)
  10. def wahaha(f):
  11. f()
  12. return f #函数名可以作为函数的返回值
  13. ret = wahaha(func) # 函数名可以作为函数的参数
  14. ret()

7.闭包

嵌套函数,内部函数调用外部函数的变量
优点:
方便外部调用
内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
闭包找到的是同一地址中父级函数中对应变量最终的值
内部函数总是可以访问其所在的外部函数中声明的变量和参数,即使在外部函数被返回(寿命终结)之后

特点:

1.让外部访问函数内部变量成为可能
2.局部变量会常驻在内存中
3.可以避免使用全局变量,防止全局变量被污染
4.会造成内存泄漏,有一块内存空间被长期占用,不被释放。每次外部函数执行的时候,外部函数的引用地址不同,都会重新创建一个新的地址。

判断闭包函数的方法

closure
**

  1. print(outer.__closure__)
  2. def func():
  3. name = 'aaron'
  4. def inner():
  5. print(name)
  6. print(inner.__closure__)
  7. return inner
  8. f = func()
  9. f()
  10. # 最后运行的结果里面有cell就是闭包
  11. name = 'aaron'
  12. def func():
  13. def inner():
  14. print(name)
  15. print(inner.__closure__)
  16. return inner
  17. f = func()
  18. f()

常用应用闭包的方式
虽然a不是全局变量 但是当inn想使用就可以一直使用,延长变量a的使用周期,也保护了内部变量

  1. def outer():
  2. a = 1
  3. def inner():
  4. print(a)
  5. return inner ## 一定要return
  6. inn = outer()
  7. inn()

闭包的应用

  1. import urllib #模块 闭包的应用from urllib.request import urlopen
  2. ret = urlopen('https://bing.ioliu.cn/').read()
  3. print(ret)
  4. def get_url():
  5. url = 'https://bing.ioliu.cn/'
  6. ret = urlopen(url).read()
  7. print(ret)
  8. get_url()
  9. def get_url():
  10. url = 'https://bing.ioliu.cn/'
  11. def get():
  12. ret = urlopen(url).read()
  13. print(ret)
  14. return get
  15. get_func = get_url()
  16. get_func()

  • 六、 函数内置属性和命名空间

    6.1 函数的内置属性

    函数有很多内置属性,使用dir(函数名称),就可以显示这些内置属性。 ```python def L_huan(): return 0 print(dir(L_huan()))

结果

[‘abs‘, ‘add‘, ‘and‘, ‘bool‘, ‘ceil‘, ‘class‘, ‘delattr‘, ‘dir‘, ‘divmod‘, ‘doc‘, ‘eq‘, ‘float‘, ‘floor‘, ‘floordiv‘, ‘format‘, ‘ge‘, ‘getattribute‘, ‘getnewargs‘, ‘gt‘, ‘hash‘, ‘index‘, ‘init‘, ‘init_subclass‘, ‘int‘, ‘invert‘, ‘le‘, ‘lshift‘, ‘lt‘, ‘mod‘, ‘mul‘, ‘ne‘, ‘neg‘, ‘new‘, ‘or‘, ‘pos‘, ‘pow‘, ‘radd‘, ‘rand‘, ‘rdivmod‘, ‘reduce‘, ‘reduce_ex‘, ‘repr‘, ‘rfloordiv‘, ‘rlshift‘, ‘rmod‘, ‘rmul‘, ‘ror‘, ‘round‘, ‘rpow‘, ‘rrshift‘, ‘rshift‘, ‘rsub‘, ‘rtruediv‘, ‘rxor‘, ‘setattr‘, ‘sizeof‘, ‘str‘, ‘sub‘, ‘subclasshook‘, ‘truediv‘, ‘trunc‘, ‘xor‘, ‘bit_length’, ‘conjugate’, ‘denominator’, ‘from_bytes’, ‘imag’, ‘numerator’, ‘real’, ‘to_bytes’]

查看函数的命名空间

def Lhuan(): return 0 print(Lhuan.__dict)

结果

{}

查看函数的文件字符串

def Lhuan(x,y): “return x+y” return x+y print(Lhuan.__doc)

结果

return x+y

查看该函数的名称

def Lhuan(x,y): “return x+y” return x+y print(Lhuan.__name)

结果

L_huan

  1. <a name="1oupP"></a>
  2. ### 6.2 函数的命名空间
  3. <a name="QYMvc"></a>
  4. #### 6.2.1 介绍
  5. Python使用动态命名空间。<br />每一个函数、模块与类创建的时候,都会定义其自己的命名空间。<br />代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间;<br />在函数的运行中开辟的临时的空间叫做局部命名空间。
  6. <a name="qPLqv"></a>
  7. #### 6.2.2 分类
  8. 命名空间一共分为三种:
  9. - 全局命名空间 (global namespace):写在函数外面的变量名,位于模块的最上层
  10. - 局部命名空间(local namespace):每一个函数都拥有自己的命名空间
  11. - 内置命名空间(bulit-in namespace):Python解释器提供的,启动之后就可以使用的名字,int、string、def、print等。
  12. <a name="7nHcs"></a>
  13. #### 6.2.3 取值顺序
  14. - 在局部调用:局部命名空间->全局命名空间->内置命名空间
  15. - 在全局调用:全局命名空间->内置命名空间
  16. 当用户在Python解释器中输入一个指令或语句的时候,Python会**先**搜索**局部命名空间**,**然后**搜索**全局命名空间**。
  17. <a name="jFUeT"></a>
  18. #### 6.2.4 加载顺序
  19. 内置命名空间(程序运行前加载)---->全局命名空间(程序运行中:从上到下加载)-------->局部命名空间(程序运行中:调用时才加载,函数调用结束后消失)
  20. <a name="sflmw"></a>
  21. ## 七、 函数的嵌套
  22. <a name="00MAO"></a>
  23. ### 7.1 函数嵌套的定义
  24. Python支持嵌套函数,即在函数定义的时候,函数体内部又包含另外一个函数的完整定义,并且可以多层嵌套。<br />被定义在其他函数体内部的函数被称为内部函数,内部函数所在的函数称为外部函数。
  25. ```python
  26. def First(): ##定义First()函数
  27. a=3
  28. def Score(): ##定义内部函数Score()函数
  29. b=4
  30. print(a+b)
  31. Score() ##在First中调用Score函数
  32. print(a)
  33. First() ##调用First函数
  34. ##结果
  35. 7
  36. 3

7.2 lambda函数

lamdba函数又被称为匿名函数,代表临时使用的简单小函数。
<函数名>=lambda<参数列表>:<表达式>
参数列表即lambda函数包含的形式参数列表
通过对表达式的计算,将函数的返回结果赋值给函数名
lambda相当于下面的
def <函数名>(参数列表):
retrun<表达式>

lambda函数中的表达式只能为单个表达式,不允许包含复杂语句,但在表达式中可以包含函数,并支持默认值参数和关键字参数,表达式的计算结果相当于普通函数的返回值。不能有return。

  1. f = lambda x,y,z:x+y+z
  2. print(f(10,20,30))
  3. ##结果
  4. 60
  5. f = lambda x,y=3,z=5:x+y+z
  6. print(f(10))
  7. ##结果
  8. 18
  9. f = lambda x,y=3,z=5:x+y+z
  10. print(f(10,z=55,y=88))
  11. ##结果
  12. 153

7.3 偏函数

Partial function,通过设置参数的默认值,可以降低函数调用的难度。

  1. from functools import partial
  2. int2= partial(int,base=2) ##base参数用于进制转换,省略的话,默认为10进制
  3. print(int2('110000'))
  4. print(int2('11001100'))
  5. ##结果
  6. 48
  7. 204

functools.partial的作用就是把一个函数的某些参数固定住(设置默认值),返回一个新函数。

八、 递归函数

8.1 介绍

在函数内部调用自身的调用方式称为递归(recursion)
应用递归函数需要满足的条件
1.可以通过递归调用缩小问题规模,但新问题必须雨原问题又相同的形式
2.必须存在一种使递归终止的条件,当达到该条件的时候结束递归

  1. ##计算阶乘
  2. def f(n):
  3. if n==1:
  4. return 1
  5. else:
  6. return n*f(n-1)
  7. n = int(input())
  8. print(f(n))
  9. ##结果
  10. 5
  11. 120

8.2 利用递归函数解决汉诺塔问题。

  1. def move(n,a,b,c):
  2. if n==1:
  3. print(a,'-->',b)
  4. return
  5. else:
  6. move(n-1,a,c,b) ##需要把(n-1)个圆盘移动到b
  7. move(1,a,b,c) ##将a的最后一个圆盘移动到c
  8. move(n-1,b,a,c) ##将b的(n-1)个圆盘移动到c
  9. move(4,'A','B','C')
  10. ##结果
  11. A --> C
  12. A --> B
  13. B --> A
  14. A --> C
  15. C --> B
  16. C --> A
  17. A --> C
  18. A --> B
  19. B --> A
  20. B --> C
  21. C --> B
  22. B --> A
  23. A --> C
  24. A --> B
  25. B --> A

8.3 防止栈溢出

使用递归函数的时候,需要防止栈溢出。
在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层帧。因为栈的大小不是无限的,所以递归调用的次数过多,会导致栈溢出。

  1. ##防止栈溢出
  2. def f(n):
  3. if n==1:
  4. return 1
  5. else:
  6. return n*f(n-1)
  7. print(f(1000))
  8. ##结果
  9. Traceback (most recent call last):
  10. File "D:/pythontest/geshu.py", line 611, in <module>
  11. print(f(1000))
  12. File "D:/pythontest/geshu.py", line 610, in f
  13. return n*f(n-1)
  14. File "D:/pythontest/geshu.py", line 610, in f
  15. return n*f(n-1)
  16. File "D:/pythontest/geshu.py", line 610, in f
  17. return n*f(n-1)
  18. [Previous line repeated 995 more times]
  19. File "D:/pythontest/geshu.py", line 607, in f
  20. if n==1:
  21. RecursionError: maximum recursion depth exceeded in comparison
  22. ##提示超过最大递归深度

使用尾递归优化。
尾递归是指在函数返回的是调用函数本身,并且return语句不能含有表达式。这样解释器或编译器就可以对尾递归进行优化,使递归无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。

  1. def f(n):
  2. return f1(n,1)
  3. def f1(num,product):
  4. if num == 1:
  5. return product
  6. return f1(num-1,num*product)
  7. print(f1(6,1000))
  8. ##结果
  9. 720000

九、 闭包

9.1 介绍

嵌套函数,内部函数调用外部函数的变量
优点:
方便外部调用
内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
闭包找到的是同一地址中父级函数中对应变量最终的值
内部函数总是可以访问其所在的外部函数中声明的变量和参数,即使在外部函数被返回(寿命终结)之后

9.2 特点:

1.让外部访问函数内部变量成为可能
2.局部变量会常驻在内存中
3.可以避免使用全局变量,防止全局变量被污染
4.会造成内存泄漏,有一块内存空间被长期占用,不被释放。每次外部函数执行的时候,外部函数的引用地址不同,都会重新创建一个新的地址。

9.3 判断闭包函数的方法

closure
**

  1. print(outer.__closure__)
  2. def func():
  3. name = 'aaron'
  4. def inner():
  5. print(name)
  6. print(inner.__closure__)
  7. return inner
  8. f = func()
  9. f()
  10. # 最后运行的结果里面有cell就是闭包
  11. name = 'aaron'
  12. def func():
  13. def inner():
  14. print(name)
  15. print(inner.__closure__)
  16. return inner
  17. f = func()
  18. f()

常用应用闭包的方式
虽然a不是全局变量 但是当inn想使用就可以一直使用,延长变量a的使用周期,也保护了内部变量

  1. def outer():
  2. a = 1
  3. def inner():
  4. print(a)
  5. return inner ## 一定要return
  6. inn = outer()
  7. inn()

9.4 闭包的应用

  1. import urllib #模块 闭包的应用from urllib.request import urlopen
  2. ret = urlopen('https://bing.ioliu.cn/').read()
  3. print(ret)
  4. def get_url():
  5. url = 'https://bing.ioliu.cn/'
  6. ret = urlopen(url).read()
  7. print(ret)
  8. get_url()
  9. def get_url():
  10. url = 'https://bing.ioliu.cn/'
  11. def get():
  12. ret = urlopen(url).read()
  13. print(ret)
  14. return get
  15. get_func = get_url()
  16. get_func()