day 10

函数进阶

命名空间和作用域

命名空间

命名空间 有三种
内置命名空间 —— 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 这个变量
原文地址:https://www.cnblogs.com/ysging/p/10055109.html