Python基础之(函数进阶)

一、命名空间和作用域

1.1、命名空间

先来看个例子:

a=123
def test(a):
    m=321
    print(a) #不报错

print(m) #报错了

上面为什么会报错呢?现在我们来分析一下python内部的原理是怎么样:

  我们首先回忆一下Python代码运行的时候遇到函数是怎么做的,从Python解释器开始执行之后,就在内存中开辟出一个空间,每当遇到一个变量的时候,就把变量名和值之间对应的关系记录下来,但是当遇到函数定义的时候,解释器只是象征性的将函数名读入内存,表示知道这个函数存在了,至于函数内部的变量和逻辑,解释器根本不关心。等执行到函数调用的时候,Python解释器会再开辟一块内存来储存这个函数里面的内容,这个时候,才关注函数里面有哪些变量,而函数中的变量会储存在新开辟出来的内存中,函数中的变量只能在函数内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。

我们给这个‘存放名字与值的关系’的空间起了一个名字-------命名空间。代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间;在函数的运行中开辟的临时的空间叫做局部命名空间。

  命名空间一共可以分为三种:全局命名空间、局部命名空间、内置命名空间

  (ps:内置命名空间中存放了python解释器为我们提供的名字:input,print,str,list,tuple...它们都是我们熟悉的,拿过来就可以用的方法)

三种命名空间之间的加载与取值顺序:

加载顺序:内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)

取值顺序:

  在局部调用:局部命名空间->全局命名空间->内置命名空间

  在全局调用:全局命名空间->内置命名空间

综上所述,在找寻变量时,从小范围,一层一层到大范围去找寻。由此可见例子中的a=123为全局变量,当在函数中调用的时候是没有问题的,而m=321为局部变量,在全局中进行打印的时候就会报错,找不到m值

1.2、作用域

作用域就是作用范围,按照生效范围可以分为全局作用域和局部作用域。

全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效

局部作用域:局部名称空间,只能在局部范围内生效

globals和locals方法(查看全局和局部命名空间)

ps:globals和locals都在全局的时候两者一样,当两者都处于局部的时候globals为全局,locals为局部

print(globals())
print(locals())   #全局

def func():
    a = 12
    b = 20
    print(locals())
    print(globals())  #局部

func()

global关键字

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

执行结果:

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

li=[1,2,3]
di={"a":1,"b":2}
def fun():
    li.append(4)
    di["c"]=3
fun()
print(li,di)

执行结果:

nonlocal 关键字 (python3中)

适用于嵌套函数中内部函数修改外部变量的值(上一层)

def fun():
    a = 2
    def fun2():
        nonlocal a
        a = a + 20
        print(a)
    fun2()
    print(a)
fun()

执行结果:

 

二、函数的嵌套和作用域链

2.1、函数的嵌套调用

def fun(a,b):
    if a>b:
        v=a
    else:
        v=b
    return v
def fun1(x,y,z):
    v=fun(x,y)
    v1=fun(v,z)
    return v1
print(fun1(2,-1,5))

#结果为:5

2.2、函数的嵌套定义

def fun():
    def fun1():
        print("in the fun1")
    fun1()      #此处对fun1进行调用,如不调用即是在fun中定义了一个fun1的函数
fun()

2.3、作用域链

函数的作用域链:小范围作用域可以使用大范围的变量,(只是查看,如果需要修改全局的使用global,局部上层使用nonlocal)但是反之不行,他是单向的。(针对不可变数据类型)

 

三、函数名本质

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

3.1、函数名可以被引用

def func():
    print('in func')

f = func
print(f)

3.2、函数名可以被当作容器类型的元素

def f1():
    print('f1')
def f2():
    print('f2')
d = {'f1':f1,'f2':f2}
d["f1"]()  #调用 

3.3、函数名可以当作函数的参数和返回值

def f1():
    print('f1')

def func1(argv):
    argv()
    return argv

f = func1(f1)
f()

一句话:函数是第一类对象,即函数可以当作数据传递 (不明白?那就把函数当变量用)

 

四、闭包函数

内部函数包含对外部作用域而非全局作用域变量的引用,该内部函数称为闭包函数

def fun():
    a=123
    def inner():
        print(a)

由于有了作用域的关系,我们就不能拿到函数内部的变量和函数了。如果我们就是想拿怎么办呢?返回呀!(这才是闭包函数的常用方法)

def fun():
    a=123
    def inner():
        print(a)
    return inner

f=fun()
f()  #调用返回的inner函数的内存地址

 闭包函数的例子:

from urllib.request import urlopen

def fun(url):
    def get():
        return urlopen(url).read()
    return get

sina=fun('http://www.sina.cn')
print(sina().decode()) #sina首页的源码

判断是否是闭包函数:__closure__

def fun():
    a=123
    def inner():
        print(a)

    print(inner.__closure__)

fun()
print(fun.__closure__)

 执行结果:

 

posted @ 2018-12-04 17:28  Crazyjump  阅读(273)  评论(0编辑  收藏  举报