首页 > 编程语言 >Python 2-02 命名空间和作用域

Python 2-02 命名空间和作用域

时间:2023-05-22 11:04:16浏览次数:29  
标签:02 函数 作用域 global Python print def 变量


命名空间和作用域

一、命名空间

命名空间(Namespace)是从名称到对象的映射,一般用 Python 字典来实现。
为了解决项目中名字冲突的问题引入了命名空间的概念,命名空间可以嵌套。

1、命名空间分类:

内置名称(built-in names), Python 语言内置的名称,比如函数名 abs、char 和异常名称 Exception 等。
全局名称(global names),模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
局部名称(local names),函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)

2、命名空间查找顺序:

局部命名空间 -> 全局命名空间 -> 内置命名空间。如果找不到变量,它将放弃查找并引发一个 NameError 异常: NameError: name ‘x’ is not defined。

3、命名空间的生命周期:

命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。
因此,我们无法从外部命名空间访问内部命名空间的对象。

二、作用域

作用域就是一个程序可以直接访问命名空间的正文区域。

1、Python 的作用域

通常而言,变量的作用域从代码结构形式来看,有块级、函数、类、模块、包等由小到大的级别。但是在 Python 中,没有块级作用域,也就是类似 if/elif/else/ 语句块、for/while 语句块、try/except 语句块和 with 上下文管理器等等是不存在作用域概念的,他们等同于普通的语句。

if True:            # if 语句块中变量没有作用域
    x = 1   
print(x)

def func():         # 函数中变量有作用域
    a = 8  

print(a)
NameError: name 'a' is not defined

通常,函数内部代码可以访问外部变量,而外部代码通常无法访问内部变量。

Python 的作用域一共有 4 层,分别是:

L (Local) 局部作用域,最内层,包含局部变量,比如一个函数/方法内部。
E (Enclosing) 闭包函数外的函数中,包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。
G (Global) 全局作用域,当前脚本的最外层,比如当前模块的全局变量。
B (Built-in) 内建作用域,包含了内建的变量/关键字等。
x = int(2.9)  # 内建作用域,查找 int 函数
global_var = 0  # 全局作用域 global

def outer():
    out_var = 1  # 闭包函数外的函数中

    def inner():
        inner_var = 2  # 局部作用域 local

Python 以 L –> E –> G –> B 的规则查找变量,即:从内向外找。如果这样还找不到,那就提示变量不存在的错误。
通常,函数内部代码可以访问外部变量,而外部代码通常无法访问内部变量,访问权限决定于这个变量是在哪里赋值的。

g_count = 0  # 全局作用域
def outer():
    o_count = 1  # 闭包函数外的函数中
    def inner():
        i_count = 2  # 局部作用域

内置作用域是通过一个名为 builtin 的标准模块来实现的,但是这个变量名自身并没有放入内置作用域内,所以必须导入这个文件才能够使用它。

>>> import builtins
>>> dir(builtins)

2、全局变量和局部变量

函数内部定义的变量拥有一个局部作用域,被叫做局部变量,定义在函数外的拥有全局作用域的变量,被称为全局变量。(类、模块等同理)

局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。

所谓的局部变量是相对的。局部变量也有可能是更小范围内的变量的外部变量。

a = 1               # 全局变量  (公有)
def func():
    b = 2           # 局部变量  (私有)
    print(a,b)
    # 可访问全局变量 a 和局部变量 b,无法访问它内部的 c。
    # print(c) 

    def inner():
        c = 3       # 局部变量
        print(a)    # 可以访问全局变量 a
        print(b)    # b 对于 inner 函数来说,就是外部变量。
        print(c)

3、global 和 nonlocal 关键字

当内部作用域想修改外部作用域的变量时,需要 global 和 nonlocal 关键字来声明。

例 1:

total = 0       # total是一个全局变量
def plus(arg1, arg2):
    # global total    # 使用 global 关键字声明此处的 total 引用外部的 total
    total = arg1 + arg2 
    print("total = {},id = {}".format(total, id(total)))

    return total

plus(10, 20)
print("全局变量 total = {} id = {}".format(total, id(total)))

函数 plus 内部通过 total = arg1 + arg2 语句,新建了一个局部变量 total,它和外面的全局变量 total 是两码事。如果想要在函数内部修改外面的全局变量 total?使用 global 关键字声明!

global:指定当前变量使用外部的全局变量

例 2:

def outer():
    # global a
    # nonlocal a 
    a = 2
    print("函数 outer 调用之时闭包外部的变量 a 的内存地址:", a, id(a))

    def inner():
        # global a 
        # nonlocal a
        a = 3
        print("函数 inner 调用之后闭包内部变量 a 的内存地址:", a, id(a))

    inner()
    print("函数 inner 调用之后,闭包外部的变量 a 的内存地址:", a, id(a))

a = 1
print("global a 的内存地址:", a, id(a))

outer()
print("函数 outer 执行完毕,全局变量 a 的内存地址: ", a, id(a))

三个 a 各是各的 a,各自有不同的内存地址,是三个不同的变量。

使用 nonlocal 关键字!它可以修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量。将 global a 改成 nonlocal a,可以看到引用了 outer 函数的 a 变量。

例 3:不要上机测试,请说出下面代码的运行结果:

a = 10

def test():
    a += 1
    print(a)

test()

a += 1 相当于 a = a + 1,按照赋值运算符的规则是先计算右边的 a + 1。但是,Python的规则是,如果在函数内部要修改一个变量,那么这个变量需要是内部变量,除非你用 global 声明了它是外部变量。很明显,我们没有在函数内部定义变量 a,所以会弹出局部变量在未定义之前就引用的错误。

UnboundLocalError: local variable ‘a’ referenced before assignment

例 4(要注意其中的闭包,也就是函数内部封装了函数):

name = 'jack'
def outer():
    name='tom'
    def inner():
        name ='mary'
        print(name)

    inner()

outer()

因为 inner 函数本身有name变量,所以打印结果是 mary。

例 5:

name ='jack'

def f1():
    print(name)

def f2():
    name = 'eric'
    f1()

f2()

这题有点迷惑性,应该是 ‘eric’ 吧,因为 f2 函数调用的时候,在内部又调用了 f1 函数,f1 自己没有 name 变量,那么就往外找,发现 f2 定义了个 name,于是就打印这个 name。错了!!!结果是 ‘jack’!

Python 函数的作用域取决于其函数代码块在整体代码中的位置,而不是调用时的位置。调用 f1 的时候,会去 f1 函数的定义体查找,对于 f1 函数,它的外部是 name =‘jack’,而不是 name = ‘eric’。

例 6,f2 函数返回了 f1 函数:

name = 'jack'
def f2():
    name = 'eric'
    return f1

def f1():
    print(name)

ret = f2()  # 和 ret = f2 不同,前者返回 f2 函数结果,后者为 f2 函数的引用。
ret()

注意:

f2() # <function f1 at ****>
f2()() # jack

仔细回想前面的例子,其实这里有异曲同工之妙,所以结果还是 ‘jack’。

总结:

1、4个作用域从内往外找,内部一旦引用就不能再次声明或赋值。
2、各是各的作用域,除非声明 global 或闭包函数内声明 nonlocal。

# 全局变量 global
a = 12
b = [1,2]
print(a)

def f1():
    a=4 # local 和外部的 a 没有任何关系,也就是指向一个不同的对象,只是同名。
    c=3 # local
    print(a, c)

def f2():
    print(a) # 访问的是 global 只能访问不能修改
    # a = 12 # UnboundLocalError: local variable 'a' referenced before assignment

def f3():
    global a # 可操作全局变量 a
    print(a)
    a=10
    print(a)

def f4(a):
    # a 作为参数
    print(a)
    a=14
    print(a)

f1(),f2(),f3(),f4(a)
print(a)

# 函数内部 如果声明外部变量为全局变量,则可操作外部变量;否则只能访问不能改变地址。
# 同名变量也可以作为局部变量,但前题是之前没有被引用。
# 函数内部:变量地址一旦变化,就变成局部变量。以前的一切引用都是Undefined variable。

def func():
    # b += [3] 
    # UnboundLocalError   此处等价于 b = b + [3] 前者不改变地址,后者会改变,前题是先有 b,注意在函数外部是不等价的,外部 b += [3]不会改变地址。

    b.append(3) # 此处访问的是 global
    c = 3 # 局部变量 local
    print(a, b, c)

func()
print(a, b)
# print(c) 外部不能访问内部的变量


标签:02,函数,作用域,global,Python,print,def,变量
From: https://blog.51cto.com/u_1439909/6321631

相关文章

  • Python 2-01 函数
    一、函数定义def函数名(参数列表):函数体判断一个数是不是素数?#方法一:for循环判断素数num=int(input('请输入一个正整数:'))foriinrange(2,int(num**0.5)+1):ifnotnum%i:print(f'{num}不是素数')breakelse: print(f'{num}是素数')......
  • Python 2-06 闭包
    闭包Closures嵌套函数(nestedfunction),内函数引用了外函数的临时变量,并且外函数返回内函数的引用,这样就构成了一个闭包。defouter():x,y,z=10,'abc',[1,2]definner():print(x,y)returninnerf=outer()print(f.__closure__)#celltuple......
  • Python 1-24 练习五 综合练习
    1、无重复字符的最长子串给定一个字符串,请你找出其中不含有重复字符的最长子串的长度。#substr向右扩展一个元素,如果该元素已经在substr中,则需要将其及其前面的元素去掉。#可通过substr.index(c)定位元素或substr.split(c)[1]分割成子串#发现有重复字符时,可......
  • Python 2-05 高阶函数
    一、函数式编程函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。而函数式编程(请注意多了一个“式”字)——FunctionalProgrammi......
  • Python 3-11 异常处理
    异常处理一、错误和异常1、句法错误句法错误又称解析错误:>>>whileTrueprint('Helloworld')File"<stdin>",line1whileTrueprint('Helloworld')^SyntaxError:invalidsyntax解析器会复现出现句法错误的代码行,并用小“箭头”指向行里检测到的第一......
  • Python 05 Selenium 等待
    等待WebDriver通常可以说有一个阻塞API。因为它是一个指示浏览器做什么的进程外库,而且web平台本质上是异步的,所以WebDriver不跟踪DOM的实时活动状态。大多数由于使用Selenium和WebDriver而产生的间歇性问题都与浏览器和用户指令之间的竞争条件有关。例如,用户指示浏览......
  • Python 1-11 练习一
    Python1-11练习一一、已知字符串s=“aAsmr3idd4bgs7Dlsf9eAF”,要求如下1、请将s字符串的大写改为小写,小写改为大写。#使用字符串的内置方法a.swapcase():s='aAsmr3idd4bgs7Dlsf9eAF't=s.swapcase()print(t)2、请将s字符串的数字取出,并输出成一个新的字符串。s=......
  • Python 1-10 字符串操作
    Python1-10字符串操作1、字符串拼接>>>s='hello'*2>>>s='hello'+'world'>>>s='hello''world'>>>......
  • Python 02 Xpath
    XpathXpath(XMLPathLanguage)是在XML文档中选择节点的语言一、XPath路径表达式1、XPath节点在XPath中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML文档是被作为节点树来对待的,树的根被称为文档节点或者根节点。2、XPath节点关系父(Pa......
  • Python 1-09 字符串
    Python1-09字符串一、Python字符串在Python3中,字符串是由Unicode码点组成的不可变序列。x="Python"y=x+"Cat"xisy#False字符串是用单引号"或双引号""括起来的一串字符,使用三引号创建多行字符串。在Python中单字符也是用字符串表示。>>>var1='HelloWor......