day 10 - 1 函数进阶

函数进阶

命名空间和作用域

命名空间

命名空间 有三种
内置命名空间 —— python解释器
  就是python解释器一启动就可以使用的名字存储在内置命名空间中
  内置的名字在启动解释器的时候被加载进内存里
全局命名空间 —— 我们写的代码但不是函数中的代码
  是在程序从上到下被执行的过程中依次加载进内存的
  放置了我们设置的所有变量名和函数名
局部命名空间 —— 函数
  就是函数内部定义的名字
  当调用函数时 才会产生这个名称空间 随着函数执行的结束 这个命名空间随之消失

在局部:可以使用全局、内置命名空间中的名字
在全局:可以使用内置命名空间中的名字,但是不能用局部中使用
在内置:不能使用局部和全局的名字的

如下面的例子

#局部命名空间的名字 不可以在全局空间中被调用
def func():
a = 1
func()
print(a) #报错——NameError: name 'a' is not defined

命名空间的使用原则:

  1、在正常情况下,直接使用内置的名字
  2、当我们在全局定义了和内置命名空间中同名的名字时,会使用全局的名字
  3、在嵌套中,当自己的这一级中有所需的名字时,就不去找上一级找了
  4、在嵌套中,如果自己没有 就找上一级要 上一级没有再找上一级 如果内置的名字空间也没有 就报错
  5、多个函数应该拥有多个独立的局部名字空间,不互相共享

作用域

作用域 有两种
  全局作用域 —— 作用在全局 —— 内置和全局命名空间中的名字都属于全局作用域 —— globals()
  局部作用域 —— 作用在局部 —— 函数(局部命名空间中的名字属于局部作用域) —— locals()

我们接着来看下面  global 的例子

#global 声明过得变量,让在函数外部的 print() 可以进行打印
a = 1
def func():
global a #使用 global 进行声明
a = 2
func()
print(a) #输出结果为 2 b=1
def func():
#global b
b = 2
func()
print(b) #此时结果还是 1,因为在内部的任何操作 不影响外部的

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

接着我们对比着 global() 变量来看 locals() 变量

#可以看出两个打印的内容一个为局部一个为全局
a = 1
b = 2
def func():
x = 'aaa'
y = 'bbb'
print(locals()) #输出局部的变量,根据 locals 所在的位置
print(globals()) #永远打印全局的名字,可以看到打印出了好多内容 func()

你一定会觉得 globals 函数在很多时候很好用(我也是这么觉得着)
可以 globals 这个函数涉及到代码的安全性 不推荐使用
可以通过传参和接收返回值 来完成原本使用 global 完成的事情

#通过传参和接收返回值来时实现调用全局变量
a = 1
def func():
global a
a = 3 func()
print(a) #改为: a = 2
def func(a):
a += 1
return a print(func(a)) #此时返回 3

函数的嵌套与调用

#求两个数字的最大值
def max(a,b):
return a if a > b else b
print(max(15,98))

#求三个数的最大值
def the_max(a,b,c):
e = max(a,b) #把其中的两个值 丢给 max 去解决了
return max(e,c)
print(the_max(56,85,65))

不能直接调用函数内层的函数名

def outer():
def inner():
print('inner') #outer() #没有结果
#inner() #报错
#outer(inner()) #报错

可以使用 nonlocal 实现函数嵌套调用

# nonlocal
# 对全局无效
# 对局部 也只是对 最近的 一层 有影响
def outer():
a = 1
def inner():
b = 2
print(a) #内部函数可以使用外部函数的变量
print('inner')
def inner2():
print(a,b)
print('inner2')
nonlocal a #声明了上面第一次出现 a 局部变量,没找到 则报错(不常用)
#global #在此处使用不能达到要求,因为此时 a = 1 为局部变量
a += 1 #在这里操作是想修改上面 a = 1 的这个值(不可变数据类型的修改)
inner2() #函数必须是先定义后调用
print('局部变量 a:',a)
inner()
outer() #输出结果
'''
1
inner
1 2
inner2
局部变量 a: 2 #这里可以看到外部函数的变量在内部函数中被修改了
'''

函数的赋值

函数名可以用于:赋值、容器类型的元素、返回值、参数

#函数名的赋值与作为容器类型的元素使用
def func():
print(123) #func()    #函数名就是内存地址
func2 = func #函数名可以用于赋值
func2()    #所以结果为 123 l = [func,func2] #函数名可以作为容器类型的元素 如下面 for 循环
print(l) #可以看出赋值后的内存地址是一致的
for i in l:
i()
#函数名作为返回值与参数
def func():
print(123) def func2(f):
f()
return f #函数名可以作为函数的返回值
func2(func) #函数名可以作为函数的参数
func3 = func2(func) #用于接收返回值
func3()

闭包

定义:

必须符合:是嵌套函数,且内部函数调用外部函数的变量

def outer():
a = 1
def inner():
print(a) #到这里就是一个闭包了
print(inner.__closure__) #可以使用 __closure__ 来检测是否是闭包
#返回 (<cell at : int object at ,) 这些就表示是一个闭包了
inner()
outer()

闭包常用形式:接收返回值

在一个函数的内部去使用它外部的变量

#不过下面并不是闭包常用的形式
def outer():
a = 1
def inner():
print(a)
inner()
outer() #而下面的才是(采用接收返回值的形式)
def outer():
a = 1
def inner():
print(a)
return inner
inn = outer()
inn()
#在这里 a = 1 不会因为 outer() 函数的结束而消失,因为 inn() 后面可能会用到,所以才不会消失
#使用闭包的好处在于 我保护了 a = 1 这个变量,它既不是全局变量,我又可以在使用它时,去使用它
#延长了 a = 1 的声命周期 节省了创建和删除变量 a = 1 的时间

闭包的应用

##获取网站信息
#现在已经可以拿到网页了,但是我们一般都要把它封装成一个函数
from urllib.request import urlopen
ret = urlopen('https://fanyi.baidu.com/translate#en/zh/inter').read()print(ret)
print(ret) #我们使用闭包来封装它
from urllib.request import urlopen
def get_url():
url = 'https://fanyi.baidu.com/translate#en/zh/inter'
def inner():
ret = urlopen(url).read()
print(ret)
return inner
get_func = get_url()
get_func()
#这样的好处在于 就算在外部调用了 n 次 get_func() 而创建 n 次 url 这个变量
上一篇:Linux学习2-在阿里云服务器上部署禅道环境


下一篇:SPOJ1557 GSS2