Python进阶之[非局部变量,闭包,装饰器]

阅读Tacotron2源码 之 Python进阶

  1. Non-Local Variable with Nested Function
  2. Closure in Python
  3. Decorator



1. Non-Local Variable with Nested Function

????在Python中,除了全局变量(Global Variable)和局部变量(Local Variable)之外,还有一种变量叫Non-Local Variable

????Non-Local Variable的存在来源于python允许Nested Function的存在。C/C++中,结构体(类)里面再定义结构体(类)可以做到,但是函数里面再定义函数则不被允许。Python可以实现嵌套函数的原因在于Everything in Python is Object,写嵌套函数的初心无非是外层函数想要利用内层函数的返回值。

????无关紧要,python三种变量都是相对的概念,全局变量好理解,就不写了,主要是Local VariableNon-Local Variable如何区别。直接看一组代码就能明白:

# Version 1
def outer():
    y = 40
    def inner():
        nonlocal y
        print("before modified :", y)
        y = 50
        print("after modified  :", y)
    inner()
    print("outer:", y)
outer()

>>>before modified : 40
>>>after modified  : 50
>>>outer: 50

????这是一个典型的Nested Funtion(嵌套函数),inner()函数嵌套在outer()函数的内部。对于outer()函数来说,y就是它的Local Variable;对于inner()函数来说,有了nonlocal关键字,y就是它的Non-Local Variable

????此时,你可能会认为Non-Local Variable是用nonlocal关键字定义的,其实不然,没有这个关键字,它也是Non-Local Variable。请看下面一组代码:

# Version 2
def outer():
    y = 40
    def inner():
        # nonlocal y
        print("before modified :", y)
        # y = 50
        # print("after modified  :", y)
    inner()
    print("outer:", y)
outer()
>>>before modified : 40
>>>outer: 40

????仔细观察,我只是注释了部分的代码,此时inner()函数访问(调用)了变量y,此时对于inner()函数来说,y仍然是它的Non-Local Variable

????那么现在一个明显的问题就是,既然Non-Local Variable不依赖于nonlocal关键字来定义,那这个关键字存在的意义是什么?继续看下面的代码:

# Version 3
def outer():
    y = 40
    def inner():
        # nonlocal y
        print("before modified :", y)
        y = 50
        print("after modified  :", y)
    inner()
    print("outer:", y)
outer()
>>>Error

????上面Version 3代码只是注释掉了Version 1中的关键字部分,然而运行却是报错的。然后你在看看Version 2和Version 3的区别,就会发现,Version 3试图在没有关键字nonlocal的声明前提下去修改Non-Local Variable,所以它报错了。

总结一下

????Non-Local Variable依赖于嵌套函数中存在,并且有两种定义方式——显式定义和隐式定义。显示定义要用nonlocal关键字声明,此时内部函数不仅可以访问还可以修改Non-Local Variable;隐式定义无须声明,但是此时内部函数只有访问而没有修改Non-Local Variable的权利。


2. Python Closure

????Closure(闭包),这个概念与上面的Non-Local Variable一样,它依赖于Nested Function而存在。一句话说什么是Closure

外部函数返回内部函数的嵌套函数,就叫闭包。

????观察上面代码,发现外部函数outer()只是调用了内部函数inner(),而并没有将其return出来,所以这个嵌套函数不是闭包。下面是一个闭包的例子:

def print_msg(msg):
    def printer():
        print(msg)
    return printer

another = print_msg("Hello")
another()
>>>"Hello"

????因为Everything in Python is Object,所以函数的返回值是另一个函数完全没问题。

????闭包的概念就是这么简洁,用法在下面:

del print_msg
another()
>>>Hello

print_msg("Hello")
>>>Traceback (most recent call last):
>>>...
>>>NameError: name 'print_msg' is not defined

????也很好懂,我们已经把外部函数print_msg()杀死了,但是内部函数printer()却活了下来,因为在杀死print_msg()之前,another = print_msg("Hello"),相当于another()继承了内部函数的地址,所以它仍然存在着。

总结一下:

什么时候需要用到Closure?

  1. 必须要写嵌套函数的时候
  2. 嵌套函数的内部函数需要访问非局部变量的时候
  3. 外部函数需要将内部函数作为返回值

其实我感觉说了跟没说一样。。。目前觉得闭包就是用在下面要说的装饰器中。


3. Decorator

????Decorator来源于现实需求,现有的代码功能有短缺,需要添加新功能,但是我想在不大刀改动原有代码的前提下,添加新功能并且新写完的代码具有向后兼容性。

????直接用例子说明:

# Version 1
# 阶乘函数
def count(number):
    mul = 1
    for item in range(1, number):
        mul *= item
    print(mul)

def cost(func, number):
    start = time.time()
    func(number)
    end = time.time()
    cost_time = end - start
    return cost_time

time = cost(count, 6)
print(time)
>>>6
>>>1s

????上面代码的需求是要计算阶乘函数耗费的时间,一个很直观的想法就是把阶乘函数作为参数传给cost()函数,这样运行cost()函数即可。这个做法可以但是并不完美,下面看看装饰器怎么做:

# Version 2
# 这是一个Closure
def cost(func):
    def _measure_time(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        elapse = time.time() - start
        print("takes {} seconds.".format(elapse))
    return _measure_time

@cost
def count(number):
    mul = 1
    for item in range(number):
        mul *= item
    print(mul)

count(4)
>>>6
>>>takes 1 seconds.

????Version 2就是一个装饰器,它没有改动原始的阶乘函数,只是新写了一个闭包cost,并且在阶乘函数头上添加了@cost,使用的时候,发现只运行count()函数,还输出了消耗的时间,这就叫代码的向后兼容性。

????再仔细看一下闭包的写法,用到了两种参数*args, **kwargs,这样,无论要装饰或者称为包起来的函数func()需要什么类型的参数,闭包都可以兼容。

????再提一句,Decorator可以叠加,如下:

def star(func):
    def inner(*args, **kwargs):
        print("*" * 30)
        func(*args, **kwargs)
        print("*" * 30)
    return inner

def percent(func):
    def inner(*args, **kwargs):
        print("%" * 30)
        func(*args, **kwargs)
        print("%" * 30)
    return inner

@star
@percent
def printer(msg):
    print(msg)
printer("Hello")

>>>
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************

????观察输出,又可以对闭包和装饰器有一个直观的理解。

总结一下:

????装饰器就是在代码中很少改动的前提条件下,添加新功能,并且新写完的代码具有向后兼容性。

原文地址:https://www.cnblogs.com/machine-lyc/p/11247462.html

时间: 2024-12-10 12:19:09

Python进阶之[非局部变量,闭包,装饰器]的相关文章

Python进阶【第九篇】装饰器

什么是装饰器 装饰器本身就是函数,并且为其他函数添加附加功能 装饰器的原则:1.不修改被装饰对象的源代码  2.不修改被装饰对象的调用方式装饰器=高阶函数+函数嵌套+闭包 # res=timmer(test)  #返回的是wrapper的地址 # res()  #执行的是wrapper() # test=timmer(test)  #返回的是wrapper的地址 # test()  #执行的是wrapper() #  @timmer  就相当于 test=timmer(test) # #搭一个高

【Python基础】高阶函数+函数嵌套+闭包 ==装饰器

高阶函数+函数嵌套+闭包 == 装饰器 一 什么是装饰器 二 装饰器需要遵循的原则 三 实现装饰器知识储备 四 高阶函数 五 函数嵌套 六 闭包 七 无参装饰器 八 装饰器应用示例 九 超时装饰器 参考: https://www.cnblogs.com/linhaifeng/articles/6140395.html https://www.cnblogs.com/haiyan123/p/8387769.html 原文地址:https://www.cnblogs.com/XJT2018/p/11

python中的函数式编程与装饰器

2.1 python中的函数式编程 函数式编码的特点 把计算视为函数而非指令 纯函数式编程,不需要变量,没有副作用,测试简单 支持高阶函数,代码简洁 python支持的函数式编程 不是纯函数式编码:允许有变量 支持高阶函数:函数也可以作为变量传入 支持闭包:有了闭包就能返回函数 有限度地支持匿名函数 2.2 python中高阶函数 函数名可以作为变量,如 高阶函数:只能接收函数作为参数的函数 变量可以是指向函数 函数的参数可以接收变量 一个函数可以接收另一个函数作为参数 能接收函数作为参数的函数

~~函数进阶(一):装饰器~~

进击のpython 函数进阶-装饰器 知道京东吧(不知道?那你知道淘宝,蘑菇街吧) 我们身为用户,在进入界面的时候 首先会提示我们登陆是吧 当我们登陆的时候,接下来的所有操作就不用再验证身份了 否则,一到收藏啊,关注啊,就需要我们重新登陆 那我们可不可以做一个这个呢?? 没有数据库,我们模拟一个数据库,懂我意思吧! DB = { "login": False, "user_name": "poddy", "password":

python_day04 函数嵌套 名称空间和作用域 闭包 装饰器 迭代器 生成器 列表解析 三元表达式 生成器表达式

本节课重要知识点内容如下: 函数嵌套 名称空间和作用域 闭包 装饰器 迭代器 生成器 列表解析 三元表达式 生成器表达式 1.函数嵌套 函数的嵌套调用:在调用一个函数的过程中,又调用了其他函数函数的嵌套定义:在一个函数的内部,又定义另外一个函数 def bar(): print('from nbar')def foo(): print('from foo') bar()foo()def max2(x,y): if x > y: return x else: return ydef max4(a,

python深入学习--decorator强大的装饰器

一.decorator基础. 最初接触python是在大学毕业进入某一游戏公司后,最开始觉得python不严谨,很不喜欢python这种"简单"的脚本,但是后来,越来越离不开python了,觉得这么灵活方便的语言,简直是程序员的福音,尤其是它的数据结构,他带来了一个"{}" 大括号就能搞定一切的时代.当然python还有很多优秀的特性.现在我们来讲python的特性之一--decorator(装饰器). 装饰器,顾名思义就是修饰函数或者类的,它把要装饰的函数作为参数

Python小程序练习二之装饰器小例子

Python小程序练习二之装饰器小例子 装饰器: 装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足: 1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 那么根据需求,同时满足了这两点原则,这才是我们的目的. 装饰器的原则组成: < 函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器 > 错误例子: 1.1Decorators.py 1 # The aut

【Python笔记】Python的几个高级语法概念浅析:lambda表达式 &amp;&amp; 闭包 &amp;&amp; 装饰器

本文主要记录自己对几个高级语法概念的理解:匿名函数.lambda表达式.闭包.装饰器. 这几个概念并非Python特有,但本文只限于用Python做说明. 1. 匿名函数 匿名函数(anonymous function)是指未与任何标识符绑定的函数,多用在functional programming languages领域,典型应用场合: 1) 作为参数传给高阶函数(higher-order function ),如python中的built-in函数filter/map/reduce都是典型的

python 函数名 、闭包 装饰器 day13

1,函数名的使用. 函数名是函数的名字,本质就是变量,特殊的变量.函数名()加括号就是执行此函数. 1,单独打印函数名就是此函数的内存地址. def func1(): print(555) print(func1) # <function func1 at 0x0000027B7CF1A048> 2,函数名的赋值 def func(): print(666) f = func print(f()) 3,函数名可以作为容器类数据的元素. def f1(): print(111) def f2()