一、函数作用域
1、作用域分四种情况:
- L:local,局部作用域,即函数中定义的变量
- E:enclosing,嵌套作用域,即包含此函数的上级函数的局部作用域,但不是全局的。
- G:global,全局作用域(全局变量),就是模块级别定义的变量,在函数中可以理解为函数外面的变量。
- B:built-in,系统固定模块里面的变量,比如int等。
搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。
x = str(100)
print('hello' + x)#hello100
# str = 90 #built-in作用域
# print(str)#str内置函数被破坏,绝对不要这样做
1、
g_counter = 33 #全局作用域
# o_count = 77
def outer():
o_count = 0#嵌套作用域
# g_counter = 334
def inner():
i_counter = 5 #局部作用域
o_count = 90
print(i_counter)# inner()
print('out' + str(o_count))#out90
print(o_count)#90
2、
g_counter = 33
# o_count = 77
def outer():
o_count = 0
# g_counter = 334
def inner():
i_counter = 5
o_count = 90
print('before inner')
print(o_count)#0
inner()
print('after inner')
print(o_count)#0
#局部作用域,超出函数变量销毁,所以返回值为最初变量值。
outer()
2、作用域产生
- 在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如if、try、for等)是不会引入新的作用域的,如下代码:
if 2 > 1:
x = 3
print(x)#1
这个是没有问题的,if并没有引入一个新的作用域,x仍处在当前作用域中,后面代码可以使用。
def fun():
x = 3
print(x)# NameError: name 'x2' is not defined
def、class、lambda是可以引入新作用域的。
3、变量的修改
x = 90
def f2():
print(x)
x = 88
print(x)
f2()
print(x)
# 错误的原因在于print(x)时,解释器会在局部作用域找,会找到x=5(函数已经加载到内存),但x使用在声明前了,所以报错:
# local variable 'x' referenced before assignment.如何证明找到了x=5呢?简单:注释掉x=5,x=6
# 报错为:name 'x' is not defined
#同理
x=6
def f2():
x+=1 #local variable 'x' referenced before assignment.
f2()
4、global关键字
- 当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了,当修改的变量是在全局作用域(global作用域)上的,就要使用global先声明一下,代码如下:
out_count = 3
def outer():
global count
out_count+=3
print(out_count)#6
outer()
5、 nonlocal关键字
- global关键字声明的变量必须在全局作用域上,不能嵌套作用域上,当要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量怎么办呢,这时就需要nonlocal关键字了
def outer():
out_count = 3
def inner():
nonlocal out_count
out_count+=3
inner()
print(out_count)#6
6、小结
(1)变量查找顺序:LEGB,作用域局部>外层作用域>当前模块中的全局>python内置作用域;
(2)只有模块、类、及函数才能引入新作用域;
(3)对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会使用外部作用域的变量;
(4)内部作用域要修改外部作用域变量的值时,全局变量要使用global关键字,嵌套作用域变量要使用nonlocal关键字。nonlocal是python3新增的关键字,有了这个 关键字,就能完美的实现闭包了。
二、递归函数
定义:在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
2.1、阶乘
#求n的阶乘
def jiecheng_new(n):
if n == 1:
return 1
return n * jiecheng_new(n - 1)
result = jiecheng_new(6)
print(result)#720
2.2、斐波那契数列
# 0 1 1 2 3 5 8 13 21 34
# 1 1 2 3 5 8 13 21 34
def fibo(n):
before = 0
after = 1
for i in range(n-1):
ret = before + after
before = after
after = ret
print(before,end='\t')
return ret
print(fibo(9))#1 1 2 3 5 8 13 21 34
def fibo(n):
if n<=1:
return n
return(fibo(n-1)+fibo(n-2))
print(fibo(8))#21
- 避免重复计算,加速
cache = {}
def fibo(n):
if n <= 1:
return n
if (n - 1) not in cache:
cache[n-1] = fibo(n-1)
if (n - 2) not in cache:
cache[n-2] = fibo(n-2)
return cache[n-1] + cache[n-2]
print(fibo(100))
- 递归函数的优点: 是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
2.3、递归特性:
(1)必须有一个明确的结束条件
(2)每次进入更深一层递归时,问题规模相比上次递归都应有所减少
(3)递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返 回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。) - 练习:水仙花,一个三位数,它的每个位上的数字的3次幂之和等于它本身
flowers = []
def flower():
# 569 // 10
for i in range(100,1000):
baiwei = i // 100
gewei = i % 10
shiwei = (i // 10) % 10
if gewei*gewei*gewei + shiwei*shiwei*shiwei + baiwei*baiwei*baiwei == i:
flowers.append(i)
flower()
print(flowers)#[153, 370, 371, 407]
三、将函数存储在模块中
3.1、导入整个模块
import fibo
#调用时
print(fibo.fibo)
3.2、导入特定的函数
from fibo import fibo
#调用时
print(fibo)
3.3、使用as给函数指定别名
import fibo as fb
from fibo import fibo as fb
3.4、导入模块中的所有函数
from fibo import *
四、函数文档字符串
函数文档字符串documentation string (docstring)是在函数开头,用来解释其接口的字符串。简而言之:帮助文档
- 查看方式
在交互模式下可以使用help查看函数,帮助文档,该界面会跳到帮助界面,需要输入q退出界面
使用doc属性查看,该方法的帮助文档文字直接显示在交互界面上。 代码如下:
def test(msg):
"""
函数名:test
功能:测试
参数:无
返回值:无
"""
print("函数输出成功"+msg)
test('hello')
print( help(test))
print(test.__doc__)