当前位置:

首页 > 编程开发 > 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
      本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
      作者最新文章
      编程开发 Python
      相关文章 更多
      python删除一个列表里的重复元素的方法与代码示例
      python删除一个列表里的重复元素的方法与代码示例

      学习如何使用Python高效删除列表中的重复元素。本文提供5种常见方法的代码示例,涵盖保持顺序与不保持顺序的场景,适合初学者和进阶开发者参考。

      Python写接口的步骤及完整代码示例
      Python写接口的步骤及完整代码示例

      详细讲解使用Python编写Web接口的核心步骤与完整代码示例,涵盖环境准备、路由定义、数据校验、服务启动及调试测试方法。

      codex怎么读取word文档及docx文件操作教程
      codex怎么读取word文档及docx文件操作教程

      介绍如何利用编程工具读取和解析Word文档(docx格式),涵盖基础文本提取代码示例、结构化数据处理逻辑,以及处理复杂排版时的技术局限与替代方案。

      怎么在Python中调用C语言代码提升性能
      怎么在Python中调用C语言代码提升性能

      详细讲解在Python中调用C语言代码提升性能的完整实践指南,包含原理分析、编译步骤、ctypes与CFFI具体实现、性能对比及常见陷阱。

      Python与C语言的性能差异及底层原理
      Python与C语言的性能差异及底层原理

      深入解析Python与C语言在执行效率上的数量级差异,从编译机制、变量内存布局、动态类型推断到GIL锁,系统阐明底层核心原理。

      Python和C语言哪个更适合编程入门
      Python和C语言哪个更适合编程入门

      客观对比Python与C语言在心智负担、底层机制与学习反馈上的核心差异,提供清晰有据的选择标准与切实的入门学习路径。

      selenium上传文件教程本地文件路径设置方法
      selenium上传文件教程本地文件路径设置方法

      深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

      大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
      大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

      针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

      langgraph python guide中文入门教程详解
      langgraph python guide中文入门教程详解

      本篇LangGraph Python中文入门教程详解,系统梳理StateGraph状态机定义、节点编写、边与条件路由、检查点持久化(MemorySaver)等核心机制,提供从环境安装到完整循环智能体构建的代码示例与实操指导。

      Windows 安装 Python:从官方入口到虚拟环境的可复现实践
      Windows 安装 Python:从官方入口到虚拟环境的可复现实践

      面向后端开发者,基于 Python 官方文档说明如何在 Windows 上安装 Python、创建 venv 虚拟环境及使用 pip 管理依赖。包含可复制命令、验证方法及工具选择边界,避免常见配置误区。

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

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

      Windows
      Windows

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

      PDF教程
      PDF教程

      正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

      Mac软件 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      Aperty macOS版
      Aperty macOS版
      macOS

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      WINDOWS 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      PixTV 网页版
      PixTV 网页版
      Windows

      PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。