当前位置:

首页 > 编程开发 > Python的命名空间、作用域和装饰器的使用方法详解

Python的命名空间、作用域和装饰器的使用方法详解

一、名称空间和作用域1、命名空间(Namespace)命名空间是从名称到对象的映射,大部分的命名空间都是通过Python字典来实现的。命名空间提供了在项目中避免名字冲突的一种方法。各个命名空间是独立的,没有任何关系的,所以一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。1、一般有三种命名空间:内置名称空间(built-innames):存放内置的名字,如len/eval/enumerate/bytes/max/min/sorted/map/filter....全局名称空间(global

    一、名称空间和作用域

    1、命名空间(Namespace)

    命名空间是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的。

    命名空间提供了在项目中避免名字冲突的一种方法。各个命名空间是独立的,没有任何关系的,所以一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。

    1、一般有三种命名空间:
    • 内置名称空间(built-in names):存放内置的名字,如len/eval/enumerate/bytes/max/min/sorted/map/filter....

    • 全局名称空间(global names):模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。

    • 局部名称空间(local names):函数内部的名字都是局部名称空间,不同函数内部的名字互不干涉。

    Python的命名空间、作用域和装饰器的使用方法详解

    2、命名空间查找顺序:

    如果找不到变量 runoob,它将放弃查找并引发一个 NameError 异常:

    NameError: name 'runoob' is not defined。
    • 查找顺序:假设我们要使用变量 runoob,则 Python 的查找顺序为:局部的命名空间去 -> 全局命名空间 -> 内置命名空间

    • 执行顺序:先内置(Python解释器启动的时候才会生成)-> 全局(文件执行的时候才会生成)-> 局部(函数调用的时候才会生成)

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

    命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。

    因此,我们无法从外部命名空间访问内部命名空间的对象。

    如下图所示,相同的对象名称可以存在于多个命名空间中。

    Python的命名空间、作用域和装饰器的使用方法详解

    2、作用域:

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

    全局名称空间和局部名称空间中可能会存在名字相同的变量,但是这两个变量互不影响。

    Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。

    变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。

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

    • L(Local):最内层,包含局部变量,比如一个函数/方法内部。

    • E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。

    • G(Global):当前脚本的最外层,比如当前模块的全局变量。

    • B(Built-in): 包含了内建的变量/关键字等。,最后被搜索

    对于变量作用域,变量的访问以: L –> E –> G –>B 的 规则查找。

    在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。

    Python的命名空间、作用域和装饰器的使用方法详解

    举例:

    x = 1
    
    def func():
        print(x)  #10
    
    x = 10
    func()

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

    在Python3.0中,可以使用以下的代码来查看到底预定义了哪些变量:

    import builtins
    print(dir(builtins))

    Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问,

    如下代码:实例中 msg 变量定义在 if 语句块中,但外部还是可以访问的。如果将 msg 定义在函数中,则它就是局部变量,外部不能访问。

    if True:
        msg = 'I am from Runoob'
    print(msg)
    # 'I am from Runoob'

    3、全局变量和局部变量

    定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。

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

    # 作用域注意点
    x = 1
    
    def f1():  # 定义阶段x=1
        print(x)  #1
    
    def f2():
        x = 2  #此x为f2函数的局部变量,f1无法直接访问
        f1()
    
    f2()

    4、函数对象+作用域应用

    def f1():
        def inner():
            print('from inner')
        return inner
    
    f = f1()  # from inner   。把局部定义的函数inner()放在全局之中
    
    def bar():
        f()
    
    bar()

    5、global关键字修改全局作用域中的变量

    函数内可以访问全局变量,但不能直接更新(修改)其值,可以加上 global 引用以更新变量值 :

    x = 1
    
    def f1():
        x = 2
    
        def f2():
            global x  # 修改全局
            x = 3
    
        f2()
    
    f1()
    print(x)  # 3

    6、nonlocal关键字修改嵌套作用域中的变量。

    如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal 关键字了

    x = 1
    
    def f1():
        x = 2
    
        def f2():
            nonlocal x
            x = 3
    
        f2()
        print(x)  # 3
    
    f1()

    二、闭包函数

    闭包:闭是封闭(函数内部函数),包是包含(该内部函数对外部作用域而非全局作用域的变量的引用)。

    闭包指的是:函数内部函数对外部作用域而非全局作用域的引用。

    def outter(x):
        x = 1
    
        def inner():
            print(x)
    
        return inner #返回的是函数名(函数对象)
    
    
    f = outter(2)
    
    f()  # 1
    f()  # 1
    f()  # 1
    # 查看闭包的元素
    print(f.__closure__[0].cell_contents)  # 1

    闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。

    应用领域:

    延迟计算(原来我们是传参,现在我们是包起来)、爬虫领域。

    import requests
    
    
    def outter(url):
        def get():
            response = requests.get(url)
            print(f"done: {url}")
    
        return get
    
    
    baidu = outter('https://www.baidu.com')
    python = outter('https://www.python.org')
    
    baidu()
    baidu()
    
    python()
    python()

    三、函数装饰器

    装饰器指的是为被装饰器对象添加额外功能。因此定义装饰器就是定义一个函数,只不过该函数的功能是用来为其他函数添加额外的功能。装饰器的实现必须遵循两大原则:

    • 不修改被装饰对象的源代码

    • 不修改被装饰对象的调用方式

    装饰器其实就是在遵循以上两个原则的前提下为被装饰对象添加新功能。

    不改变函数体代码,并且不改变函数调用方式,它本质就是一个闭包函数。

    def f1(x):
        def f2():
            print(x)  # 10
        return f2
    
    f2 = f1()
    f2()  # f2()

    在不改变当前函数的情况下, 给其增加新的功能:

    def log(pr):  # 将被装饰函数传入
        def wrapper():
            print("**********")
            return pr()  # 执行被装饰的函数
    
        return wrapper  # 将装饰完之后的函数返回(返回的是函数名)
    
    
    @log
    def pr():
        print("我是小小洋")
    
    
    pr()
    
    # **********
    # 我是小小洋

    回调函数和返回函数的实例就是装饰器。

    四、无参装饰器

    举例:

    import time
    
    
    def index():
        print('welcome to index')
        time.sleep(1)
    
    
    def time_count(func):
        # func = 最原始的index
        def wrapper():
            start = time.time()
            func()
            end = time.time()
            print(f"{func} time is {start - end}")  #  time is -1.0038220882415771
    
        return wrapper
    
    
    index = time_count(index)  # index为被装饰函数index的内存地址,即index = wrapper
    index()  # wrapper()

    1、被装饰函数有返回值:

    如果原始的被装饰函数index()有返回值的时候,wrapper()函数的返回值应该和index()的返回值相同,也就是说,我们需要同步原始的index()和wrapper()方法的返回值。

    import time
    
    
    def index():
        print('welcome to index')
        time.sleep(1)
        return 123
    
    
    def time_count(func):
        # func = 最原始的index
        def wrapper():
            start = time.time()
            res1 = func()
            end = time.time()
            print(f"{func} time is {start - end}")  #  time is -1.0050289630889893
            return res1
    
        return wrapper
    
    
    index = time_count(index)
    res = index()
    print(f"res: {res}")  #
    res: 123

    2、被装饰函数需要传参:

    如果原始的被装饰函数index()方法需要传参,那么我们之前的装饰器是无法实现该功能的,由于有wrapper()=index(),所以给wrapper()方法传参即可。

    import time
    
    
    def index():
        print('welcome to index')
        time.sleep(1)
        return 123
    
    
    def home(name):
        print(f"welcome {name} to home page")
        time.sleep(1)
        return name
    
    
    def time_count(func):
        def wrapper(*args, **kwargs):
            start = time.time()
            res = func(*args, **kwargs)
            end = time.time()
            print(f"{func} time is {start-end}") #  time is -1.0039079189300537
            return res
    
        return wrapper
    
    
    home = time_count(home)
    
    res = home('egon')
    print(f"res: {res}") #res: egon

    3、装饰器模板

    def deco(func):
        def wrapper(*args,**kwargs):
            res = func(*args,**kwargs)
            return res
        return wrapper

    4、装饰器语法糖:

    在被装饰函数正上方,并且是单独一行写上@装饰器名

    import time
    
    
    def time_count(func): #装饰器
        # func = 最原始的index
        def wrapper(*args, **kwargs):
            start = time.time()
            res = func(*args, **kwargs)
            end = time.time()
            print(f"{func} time is {start-end}") # time is -1.0005171298980713
            return res
    
        return wrapper
    
    
    @time_count  # home = time_count(home)
    def home(name):
        print(f"welcome {name} to home page") #welcome egon to home page
        time.sleep(1)
        return name
    
    
    res = home('egon')
    print(f"res: {res}") #res: egon

    五、带参数的装饰器

    注意无参装饰器只套两层。

    import time
    
    current_user = {'username': None}
    
    
    def login(func):
        # func = 最原始的index
        def wrapper(*args, **kwargs):
            if current_user['username']:
                res1 = func(*args, **kwargs)
                return res1
    
            user = input('username: ').strip()
            pwd = input('password: ').strip()
    
            if user == 'nick' and pwd == '123':
                print('login successful')
                current_user['username'] = user
                res1 = func(*args, **kwargs)
                return res1
            else:
                print('user or password error')
    
        return wrapper
    
    
    @login
    def index():
        print('welcome to index')
        time.sleep(1)
    
    
    res = index()
    
    #username: nick 
    #password: 123 
    #login successful 
    #welcome to index

    我们首先看看三层闭包怎么运用。

    def f1(y):
        def f2():
            x = 1
    
            def f3():
                print(f"x: {x}")  # x: 1
                print(f"y: {y}")  # x: 1
    
            return f3
        return f2
    
    
    f2 = f1(2)
    f3 = f2()
    f3()

    3、有参三层装饰器:

    在函数中嵌入装饰器

    import time
    
    current_user = {'username': None}
    
    
    def auth(engine='file'):
        def login(func):
            def wrapper(*args, **kwargs):
                if current_user['username']:
                    res = func(*args, **kwargs)
                    return res
    
                user = input('username: ').strip()
                pwd = input('password: ').strip()
    
                if engine == 'file':
                    print('base of file')
                    if user == 'nick' and pwd == '123':
                        print('login successful')
                        current_user['username'] = user
                        res = func(*args, **kwargs)
                        return res
                    else:
                        print('user or password error')
                elif engine == 'mysql':
                    print('base of mysql, please base of file')
            return wrapper
        return login
    
    
    @auth(engine='file')
    def index():
        print('welcome to index')
        time.sleep(1)
    
    
    res = index()

    username: nick 
    password: 123 
    base of file 
    login successful 
    welcome to index

    六、类装饰器

    没错,装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

    class Foo(object):
        def __init__(self, func):
            self._func = func
    
        def __call__(self):
            print ('class decorator runing')
            self._func()
            print ('class decorator ending')
    
    @Foo
    def bar():
        print ('bar')
    
    bar()
    
    functools.wraps

    使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring、__name__、参数列表,先看例子:

    # 装饰器
    def logged(func):
        def with_logging(*args, **kwargs):
            print func.__name__      # 输出 'with_logging'
            print func.__doc__       # 输出 None
            return func(*args, **kwargs)
        return with_logging
    
    # 函数
    @logged
    def f(x):
       """does some math"""
       return x + x * x
    
    logged(f)

    不难发现,函数 f 被with_logging取代了,当然它的docstring,__name__就是变成了with_logging函数的信息了。好在我们有functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器里面的 func 函数中,这使得装饰器里面的 func 函数也有和原函数 foo 一样的元信息了。

    from functools import wraps
    
    def logged(func):
        @wraps(func)
        def with_logging(*args, **kwargs):
            print func.__name__      # 输出 'f'
            print func.__doc__       # 输出 'does some math'
            return func(*args, **kwargs)
        return with_logging
    
    @logged
    def f(x):
       """does some math"""
       return x + x * x

    七、装饰器顺序

    一个函数还可以同时定义多个装饰器,比如:

    @a
    @b
    @c
    def f ():
        pass

    它的执行顺序是从里到外,最先调用最里层的装饰器,最后调用最外层的装饰器,它等效于

    f = a(b(c(f)))

    八、装饰器使用场景

    现在我们来看一下装饰器在哪些地方特别耀眼,以及使用它可以让一些事情管理起来变得更简单。

    授权(Authorization)

    装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。这里是一个例子来使用基于装饰器的授权:

    from functools import wraps
     
    def requires_auth(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            auth = request.authorization
            if not auth or not check_auth(auth.username, auth.password):
                authenticate()
            return f(*args, **kwargs)
        return decorated

    日志(Logging)

    日志是装饰器运用的另一个亮点。这是个例子:

    from functools import wraps
     
    def logit(func):
        @wraps(func)
        def with_logging(*args, **kwargs):
            print(func.__name__ + " was called")
            return func(*args, **kwargs)
        return with_logging
     
    @logit
    def addition_func(x):
       """Do some math."""
       return x + x
     
     
    result = addition_func(4)
    # Output: addition_func was called
    本文内容来源于互联网,如有侵权请联系删除。
    作者最新文章
    编程开发 Python
    相关文章 更多
    Python安装后怎么打开:使用IDLE或命令行启动解释器
    Python安装后怎么打开:使用IDLE或命令行启动解释器

    刚在Windows安装好Python却不知道如何启动?本文详细演示如何通过开始菜单找到并打开IDLE集成开发环境,以及如何在PowerShell或命令提示符中使用python和py命令启动交互式解释器、运行.py脚本文件。包含退出解释器的方法及常见启动问题排查,帮助初学者快速验证安装成功并开始编写代码。

    Windows系统Python安装教程:下载、勾选PATH及环境变量配置
    Windows系统Python安装教程:下载、勾选PATH及环境变量配置

    针对Windows初学者的Python安装实战指南。详细讲解如何从Python官网下载匹配架构的安装包,重点演示安装首屏勾选“Add python.exe to PATH”的关键操作,并提供使用python --version和py命令验证环境变量的具体步骤,帮助新手快速搭建开发环境并排查路径问题。

    麒麟OS如何查看Python进程的运行状态
    麒麟OS如何查看Python进程的运行状态

    要想确认麒麟OS中Python程序的运行状态以及资源占用情况,我们可以这样做:用ps -ef | grep python来筛选进程;通过top命令,按P键排序查看实时负载;使用pgrep -f "script.py"精准获取PID;借助lsof -p PID验证文件打开状态。另外,还可以结合syst

    Python在Debian上如何配置SSL证书
    Python在Debian上如何配置SSL证书

    在Debian系统上配置SSL证书通常涉及以下几个步骤:安装Web服务器:首先,你需要一个Web服务器,比如Apache或Nginx。这里以Apache为例。sudo apt updatesudo apt install apache2获取SSL证书:你可以从Let’s Encrypt免费获取SSL

    统信UOS怎么安装Python开发环境
    统信UOS怎么安装Python开发环境

    要想让Python项目在统信UOS上正常运行,得先安装python3、python3-pip、python3-venv、python3-dev以及build-essential等组件。具体操作就是执行sudo apt install命令来一步到位完成安装,同时别忘了配置清华镜像源来给pip加速哦。在

    纯Python方案实现中英文全文搜索
    纯Python方案实现中英文全文搜索

    在互联网上的各类网站中,无论大小,基本上都会有一个搜索框,用来给用户对内容进行搜索,小到站点搜索,大到搜索引擎搜索。从简单的来说,搜索功能确实很简单,一个简单的select语句就可以实现数据的搜索。而从复杂的来看,无论是搜索的精度还是搜索的效率,都是有很深的研究范围的。对于简单的搜索功能来说,一个s

    Mac如何取消通过Python脚本运行的关机程序
    Mac如何取消通过Python脚本运行的关机程序

    立即在终端输入sudo shutdown -c取消倒计时关机,成功后显示“Shutdown cancelled”;若存在pmset重复任务,需再执行sudo pmset repeat cancel清除。Mac因Python脚本执行了os.system("sudo shutdown -h +10")或

    Pythonasyncio异步并发与多固定出口IP调度实战
    Pythonasyncio异步并发与多固定出口IP调度实战

    之前写过一篇同步场景下用 Python 管理多个固定出口 IP 的实践(ExitPool + requests/httpx),覆盖了健康检查、故障转移和连接池复用。但在实际业务中,越来越多的场景用 asyncio 做高并发采集或批量接口调用——异步事件循环下多出口的管理方式和同步场景完全不同:单线程

    Python在静态出口IP产品中的实战:从地址漂移巡检到多IP故障切换
    Python在静态出口IP产品中的实战:从地址漂移巡检到多IP故障切换

    写在前面:为什么静态出口 IP 不是"买了就行"不少团队在引入静态出口 IP 产品时,第一反应往往是:“地址配上去,这事就算完了。”可真到了真实业务里,静态出口 IP 真正能体现价值的地方,往往不在分配这一步,而在分配之后怎么管:地址有没有漂移,质量是否达标,某一条线路突然不可用时怎么切换,连接层又

    using namespace 使用中遇到的问题怎么解决
    using namespace 使用中遇到的问题怎么解决

    命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    macOS软件
    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    Mac软件 更多
    灵活计算器
    灵活计算器
    macOS/iOS/Android

    灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

    赤友清理大师
    赤友清理大师
    macOS

    赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    极度公式
    极度公式
    Windows/macOS/Linux

    极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    WINDOWS 更多
    Windows 10
    Windows 10
    Windows

    Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

    极度公式
    极度公式
    Windows/macOS/Linux

    极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    密码键盘
    密码键盘
    Windows/macOS/iOS/Android

    密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。