旋量群并并非 Python 独一无二的基本概念。很多其它C语言共享资源完全相同的基本概念。尽管很多新手可能将听闻过它,但她们并不确凿晓得它是甚么和怎样采用它。在责任编辑中,我重点项目如是说了有关 Python 旋量群的最基本技能,期望你能更快地认知那个基本概念。
一、与外机能
与此相反,旋量群存有于很多C语言中,下列表述源自腾讯新浪网:
旋量群是能加载其它表达式内部表达式的表达式。比如在javascript中,多于表达式内部的子表达式就能加载局部表达式,因此旋量群能认知成“表述在两个表达式内部的表达式“。在其本质上,旋量群是将表达式内部和表达式内部相连接的公路桥
那个表述对很多人而言太网络化了,因此它并不某一于 Python。为的是协助你认知那个基本概念,这儿有一个单纯的版:旋量群是在内部表达式中建立并采用内部表达式表达式的内部表达式。它由内部表达式做为其输入值回到。却是太控制技术了?我尊重。最合适透过两个真实世界的范例来认知。他们先看下面的标识符短片:
def multiplier_creator(n): def multiplier(number): return number * n returnmultiplier double_multiplier = multiplier_creator(2) triple_multiplier = multiplier_creator(3)在下面的标识符中,double_multiplier和triple_multiplier是两个旋量群,因为它们满足旋量群的表述,如下所述。
是在表达式内建立的multipler内部multiplier_creator表达式,由于它在表达式内部,因此称为内部表达式multiplier。内部表达式通常被称为嵌套表达式,因为它嵌套在另两个表达式中。建立的内部表达式是内部表达式的回到值。需要注意的是,表达式直接multiplier_creator回到multiplier表达式,而并非表达式的输入值multiplier。内部表达式multipler采用n表达式,它是内部表达式的参数multiplier_creator。内部表达式对内部表达式的访问也称为非局部表达式绑定。非局部表达式的绑定可能将是有关旋量群最令人困惑的部分。让他们在下一节中探讨它。
2.局部表达式和非局部表达式
当他们采用表达式时,他们晓得在表达式中,他们能自由采用任何传递的参数,这些参数称为局部表达式。其本质上,表达式形成了两个局部作用域,它限制了对其表达式的访问。
就下面的范例而言,multiplier_creator表达式表述了两个局部作用域,参数n是两个局部表达式。在类似的表达式中,内部表达式multiplier表述了另两个局部作用域,参数number是局部表达式。但值得注意的是,该multipler表达式还采用参数n。尽管n是multiplier_creator的作用域的局部表达式,但它并非multipler表达式的局部表达式。因此,在示例中,乘数表达式采用非局部表达式,也称为自由表达式。
除了局部表达式和非局部表达式的区别,你们中的一些人可能将还听闻过全局表达式,它们是在模块级别表述的表达式。
你可能将已经注意到,从内部表达式的角度来看,他们将访问内部表达式的局部表达式的过程称为非局部表达式绑定。用绑定来描述那个特性是很有意义的。绑定到底是甚么意思?让他们在下一节中学习它。
3. 非局部表达式绑定
非局部表达式的绑定在其它一些语言中也称为非局部表达式捕获,以描述旋量群的特性。你能单纯地将其基本概念化为内部表达式“拥有”采用的非局部表达式。让他们观察下列特征:
>>> del multiplier_creator >>> double_multiplier(5) 10 >>> triple_multiplier(5) 15在下面的标识符中,他们删除了内部表达式multiplier_creator,这样那个表达式就变得不可访问了。但请注意旋量群(即double_multiplier和triple_multiplier)采用multiplier_creator的参数n。因此,有些人可能将认为停用将停止工作。然而,她们仍在产生预期的结果。
根本原因是旋量群已经建立了它采用的非局部表达式的绑定。换句话说,它有两个绑定表达式的副本。为的是观察那个特性,考虑一些特殊的检查表达式,如下所示:
>>> double_multiplier.__code__.co_freevars (n,) >>> double_multiplier.__closure__[0].cell_contents 2 >>> triple_multiplier.__code__.co_freevars (n,) >>> triple_multiplier.__closure__[0].cell_contents 3__code__.co_freevars允许他们检查旋量群的非局部表达式绑定的名称,允许__closure__[0].cell_contents他们检查绑定的非局部表达式的值。你不必晓得这些机能的细节,它们只是底层实现。
重要的是要晓得,因为旋量群透过绑定拥有自己的非局部表达式副本,因此即使内部表达式已被删除,它们仍然能工作。
4. Nonlocal 关键字和 Unboundlocalerror 异常
人们在建立旋量群时,有时会遇到UnboundLocalError异常。让他们在下面的标识符短片中看到这一点:
>>> def running_total_multiplier_creator(n): … running_total = 0 … def multiplier(number): … product = number * n … running_total += product… return running_total … return multiplier … >>> running_doubler = running_total_multiplier_creator(2) >>> running_doubler(5) Traceback (most recent call last): File “<stdin>”, line 1, in<module> File“<stdin>”, line 5, in multiplier UnboundLocalError: local variable running_totalreferenced before assignment乍一看,该running_total_multiplier_creator表达式似乎能有效生成旋量群。但是,当他们采用生成的旋量群时,UnboundLocalError会引发异常。那个异常是甚么意思?如果你阅读 Traceback 消息,你会发现有问题的标识符是running_total += product. 为甚么会导致这样的错误?这是解释:
这行标识符其本质上解释为running_total = running_total + product.表达式查找顺序称为 LEGB 规则,遵循局部 -> 封闭 -> 全局 -> 内置的顺序。当你running_total = xxx在内部表达式中运行时,你是在内部表达式的局部范围内注册两个局部表达式。当Python继续运行标识符到赋值语句右边的时候,又遇到了那个running_total表达式,于是Python开始查找那个表达式,在局部作用域中找到它。然而,它注意到那个表达式还没有被赋值,因为它的赋值语句还没有完成它的执行,这是为甚么错误消息说:local variable ‘running_total’ referenced before assignment。当两个表达式在赋值之前被引用时,它被称为未绑定错误。如果他们退一步看看那个multiplier表达式,你可能将会意识到他们想要采用在表达式作用域内running_total定义的表达式,从表达式的角度来看running_total_multiplier_creator,它被称为封闭作用域。multiplier为的是让内部表达式明白它running_total并非两个局部表达式,他们必须采用nonlocal关键字来明确。这是正确的版:
>>> def running_total_multiplier_creator(n): … running_total = 0 … def multiplier(number): … nonlocal running_total … product = number * n … running_total += product … returnrunning_total… return multiplier … >>> running_doubler = running_total_multiplier_creator(2) >>> running_doubler(5) 10如你所见,他们只是单纯地声明它running_total是两个非局部表达式,这会指示 Python 在查找该running_total表达式时绕过局部作用域。
5. 为甚么要旋量群?
到目前为止,他们已经回顾了甚么是旋量群,但你可能将想晓得他们为甚么要麻烦拥有旋量群特性?旋量群最常见的应用之一是建立装饰表达式。尽管你可能将不晓得旋量群,但我敢打赌你可能将听闻过装饰器。在 Python 中,装饰器是在不影响装饰表达式算法的情况下修改其它表达式行为的表达式。如果你想更深入地介绍旋量群,请参阅我有关装饰器的文章。
在这儿,我将简要概述与他们刚刚学习的旋量群相关的装饰器。下列标识符向你展示了两个示例:
def simple_logger(func): def decorated(*args, **kwargs): print(f”Youre about to call {func}“) result = func(*args, **kwargs) print(f”You just called {func}“) return result return decorated @simple_logger def hello_world(): print(“Hello, World!”)这simple_logger是两个装饰器,采用它涉及两个@符号做为其名称的前缀和装饰表达式上方的位置。当你调用该hello_world表达式时,将发生下列情况:
>>> hello_world() Youre about to call <function hello_world at 0x101ce9790>Hello, World! You just called<function hello_world at 0x101ce9790>那个特性背后的原因是装饰表达式实际上是两个旋量群。装饰过程在幕后有下列两个步骤:
# 第一步 def hello_world(): print(“Hello, World!”) # 第二步 hello_world = simple_logger(hello_world)为的是证明这hello_world确实是两个旋量群,他们能像之前一样运行下列检查。
>>> hello_world.__code__.co_freevars (func,) >>> hello_world.__closure__[0].cell_contents <function hello_world at0x101ce9790>装饰表达式hello_world有两个非局部表达式func。绑定值是两个表达式。做为相关点,重要的是要晓得函数只是 Python 中的常规对象,因此能将表达式视为其它数据模型表达式(比如,列表和字典)。结论
在责任编辑中,他们回顾了旋量群的五个最重要的方面。下列是对这些要点的快速回顾:
内部机能和内部机能之间存有区别。旋量群是在内部表达式中建立的内部表达式。旋量群涉及非局部表达式的绑定。两个表达式有它的局部作用域。当你采用在表达式内建立的表达式时,你采用的是局部表达式。如果你采用在表达式内部建立的表达式,则你采用的是非局部表达式。该nonlocal关键字表示应将表达式视为非局部表达式。通常,LEGB 规则适用于表达式查找。但是,采用nonlocal关键字,Python 将被指示在查找表达式时绕过局部作用域。装饰是建立两个旋量群来替换其原始表达式声明的过程。每个装饰机能都是引擎盖下的两个停用。如果你发现我的任何文章对你有协助或者有用,麻烦点赞或者转发。 谢谢!