day03.17

python函数的名称空间和作用域

名称空间:

储存名字与值的内存地址映射关系的空间。

名称空间的分类:

  • 内置名称空间:

python解释器自带的一些名称与值的对应关系;例如:print()、len()等等。是第一个被加载的名称空间。

其存活周期:伴随python解释器的启动而自动产生内置名称空间;随着python解释器的关闭而销毁。

len()
print()
open()

 

  • 全局名称空间:

在py文件中,除了内置的函数,文件执行过程中产生的名字都是在全局名称空间中。是第二个被加载的名称空间。

其存货周期:伴随着py文件的执行而产生全局名称空间,当py文件当中所有的代码执行完毕时,py文件运行结束时即刻销毁。

复制代码
name = 'jason'  # 变量名name存入全局名称空间
def index():  # 函数名index存入全局名称空间
    pass
if True:
    a = 111  # 变量名a存入全局名称空间
for i in range(10):  # 变量名i存入全局名称空间
    pass
while True:
    b = 222  # 变量名b存入全局名称空间
复制代码

 

  • 局部名称空间:

函数体代码运行产生的名字都会存放在局部名称空间中。函数的形参、函数内定义的名字等等都被存放在该空间当中。是最后一个被加载的名称空间。

其存货周期:伴随着函数的调用而产生局部名称空间,在函数体代码运行完毕时即刻销毁。

def index():
name = 'jason'  # name存入局部名称空间

 

名字的查找顺序

NOTE:

  1. 名称空间的加载顺序是:内置名称空间>>>全局名称空间>>>局部名称空间;
  2. 查找一个名字,必须从三个名称空间之一找到,(基于当前位置逐步向上层空间查找!!!);查找顺序为:局部名称空间>>>全局名称空间>>>内置名称空间。

在局部作用域查找名字时,起始位置是局部作用域,所以先查找局部名称空间,没有找到,再去全局作用域查找:先查找全局名称空间,没有找到,再查找内置名称空间,最后都没有找到就会抛出异常。

在全局作用域查找名字时,起始位置便是全局作用域,所以先查找全局名称空间,没有找到,再查找内置名称空间,最后都没有找到就会抛出异常

  1. 如果函数没有被调用,那么不要去管函数体代码有多复杂 先跳过!!!
  2. 局部名称空间之间如果不是嵌套关系,那么互不干涉!!!名称空间的嵌套关系是在函数定义阶段就固定死的,与调用位置无关!!!
len = '我是全局名称空间中的len'
def index():
    len = '我是局部名称空间中的len'
    print(len)
index()  # 调用函数 函数体就会执行 就会产生局部名称空间
print(len)
def index1():
    name = 'jason'
def index2():
    age = 18
    print(name)  # 报错
index1()
index2()

 

函数的作用域

函数的作用域就是指名称空间能够作用的范围。按照名字作用范围的不同可以将三个名称空间划分为两个区域:全局作用域和局部作用域。

全局作用域:位于全局名称空间、内置名称空间中的名字属于全局范围,该范围内的名字全局存活(除非被删除,否则在整个文件执行过程中存活),全局有效(在任意位置都可以使用)。

                     内置名称空间和全局名称空间作用范围在全局作用域;在程序任意阶段任意位置均可使用,全局有效。

局部作用域:位于局部名称空间的名字一般只能作用在局部范围。那么它的作用域就是局部作用域。

                      该范围内的名字临时存活(即在函数调用时临时生成,函数调用结束后就释放),局部有效且只能在函数内使用。

NOTE:

可以调用内建函数locals()和globals()来分别查看局部作用域和全局作用域的名字,查看的结果都是字典格式。在全局作用域查看到的locals()的结果等于globals()。

名字查找的关键字globals()和locals()关键字:

gloabl关键字:

表示全局范围,就是所谓的全局作用域,局部修改全局不可变类型。

在函数内,无论嵌套多少层,都可以查看到全局作用域的名字;如果想要在局部修改全局名称空间中的值且值为不可变类型时,需要提前加关键字global()申明。

x = 111
def index():
    # x = 222  # 并不是在修改全局的x 而是在局部名称空间中创建了一个新的x
    # 如果想要在局部名称空间中修改全局名称空间中的名字 那么需要使用关键字申明
    global x  # 申明 修改的是全局x而不是产生新的x
    x = 666
index()
print(x)

 


如果想要在局部修改全局的可变类型,不需要加关键字global()申明。

l1 = [111, 222, 333]
def index():
    l1.append(444)
index()
print(l1)    # [111, 222, 333, 444]

 

locals()关键字:

对于嵌套多层的函数,使用nonlocal关键字可以将名字声明为来自外部嵌套函数定义的作用域(非全局);

nonlocal x会从当前函数的外层函数开始一层层去查找名字x,若是一直到最外层函数都找不到,则会抛出异常。

局部名称空间嵌套的情况下,内层如果想要修改外层,
如果数据是不可变类型,则需要使用关键字nonlocal;
如果数据是可变类型,则不需要使用关键字nonlocal;

复制代码
def index():
    x = 111  # 在index的局部产生一个x=111
    l1 = [11, 22, 33]
    def f1():
        # x = 222  # 在f1的局部产生一个x=222
        # print(x)
        # 局部名称空间嵌套 内层修改外层
        # nonlocal x
        # x = 333
        l1.append(44)
    f1()
    print(x)    # 111
    print(l1)    # [11, 22, 33, 44]
index()
复制代码

 

函数名的多种用法:

  • 函数名可以当做变量名赋值

def index():
    print('from function index')
print(index)
res = index  # 让res也指向函数体代码
print(res)
res()  # index()

 

  • 函数名还可以当成函数的实参

def index():
    print('from index')
def func(a):
    print('from func')
    print(a)
    a()
func(index)    # 函数名当函数的实参

 

  • 函数名还可以当做函数的返回值

def func():
    print('from func')    # from func
    return index  # 将函数名当做返回值
def index():
    print('from index')    # <function index at 0x00000216B2FFEBF8>
res = func()  # res接收函数名
print(res)  # 指向的是index函数的内存地址    from index
res()  # index()

 

  • 函数名可以作为容器类型的元素

容器类型:内部可以存档多个元素的数据类型>>>:列表、元组、字典、集合等等

def index():
    print('from index')    # [11, 22, 33, 44, <function index at 0x000002B617861EA0>]
l1 = [11, 22, 33, 44, index]    
print(l1)    # from index
l1[-1]()  #  index()

 

函数的嵌套

python允许创建嵌套函数。也就是说我们可以在函数里面定义函数,而且现有的作用域和变量生存周期依旧不变。

def outer():
    name = "python"

    def inner():    # outer函数内部定义的函数
        print(name)    # python
    return inner()    # 返回该内部函数

outer()

 

def index():
    func()
    print('from index')
def func():
    index()
    print('from func')
func()

 

posted @   *sunflower*  阅读(64)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示