Python中的闭包与装饰器的用法详解

 更新时间:2023年07月29日 10:09:54   作者:夏末ya  
这篇文章主要介绍了Python中的闭包与装饰器的用法详解,装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象,需要的朋友可以参考下

闭包

Python函数是支持嵌套的。如果在一个内部函数中对外部函数作用域(非全局作用域)的变量进行引用,那么内部函数就会被称为闭包。

闭包需要满足如下3个条件:

1.存在于两个嵌套关系的函数中,并且闭包是内部函数;

2.内部函数引用了外部函数的变量(自由变量);

3.外部函数会把内部函数的函数名称返回。

示例:

在这里插入图片描述

装饰器

1、装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

2、装饰器是一个函数,它需要接收一个参数,该参数表示被修饰的函数。

例如,有如下一个装饰器函数:

def w1(func):
   print(‘正在装饰')
    def inner():
        print(‘正在验证权限')
        return inner()
  • 装饰器是个嵌套函数
  • 内部函数是一个闭包。
  • 外部函数接收的是被修饰的函数(func)

3、通过在函数定义的前面添加@符号和装饰器名,实现装饰器对函数的包装。

给f1函数加上装饰器,示例如下:

@w1
def f1():
    print('f1')

此时,程序会自动编译生成调用装饰器函数的代码,等价于: f1 = w1(f1)

多个装饰器

多个装饰器应用在一个函数上,调用顺序是从下至上。

@w1
@w2
def f1():
        print(‘---f1---')

执行顺序:先执行@w2,后执行@w1

带参数的装饰器

假设我们前文的装饰器需要完成的功能不仅仅是能在进入某个函数后打出log信息,而且还需指定log的级别,那么装饰器就会是这样的。

  def logging(level):
        def wrapper(func):
            def inner_wrapper(*args, **kwargs):
                print "[{level}]: enter function {func}()".format(
                    level=level,
                    func=func.__name__)
                return func(*args, **kwargs)
            return inner_wrapper
        return wrapper
    @logging(level='INFO') def say(something):
        print "say {}!".format(something)
    # 如果没有使用@语法,等同于
    # say = logging(level='INFO')(say)
    @logging(level='DEBUG') def do(something):
        print "do {}...".format(something)
    if __name__ == '__main__':
        say('hello')
        do("my work")
  • 当带参数的装饰器被打在某个函数上时,比如@logging(level=‘DEBUG’),它其实是一个函数,会马上被执行,只要这个它返回的结果是一个装饰器时,那就没问题。

基于类实现的装饰器

装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。

在Python中一般callable对象都是函数,但也有例外。只要某个对象重载了__call__()方法,那么这个对象就是callable的。

class Test():
    def __call__(self):
        print 'call me!'
t = Test()
t()  # call me

__call__这样前后都带下划线的方法在Python中被称为内置方法,有时候也被称为魔法方法。

重载这些魔法方法一般会改变对象的内部行为。上面这个例子就让一个类对象拥有了被调用的行为。

回到装饰器上的概念上来,装饰器要求接受一个callable对象,并返回一个callable对象(不太严谨,详见后文)。

那么用类来实现也是也可以的。我们可以让类的构造函数__init__()接受一个函数,然后重载__call__()并返回一个函数,也可以达到装饰器函数的效果。

class logging(object):
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kwargs):
        print "[DEBUG]: enter function {func}()".format(
            func=self.func.__name__)
        return self.func(*args, **kwargs)
@logging
def say(something):
    print "say {}!".format(something)

带参数的类装饰器

如果需要通过类形式实现带参数的装饰器,那么会比前面的例子稍微复杂一点。那么在构造函数里接受的就不是一个函数,而是传入的参数。通过类把这些参数保存起来。然后在重载__call__方法是就需要接受一个函数并返回一个函数。

class logging(object):
    def __init__(self, level='INFO'):
        self.level = level
    def __call__(self, func): # 接受函数
        def wrapper(*args, **kwargs):
            print "[{level}]: enter function {func}()".format(
                level=self.level,
                func=func.__name__)
            func(*args, **kwargs)
        return wrapper  #返回函数
@logging(level='INFO')
def say(something):
    print "say {}!".format(something)

内置的装饰器

内置的装饰器和普通的装饰器原理是一样的,只不过返回的不是函数,而是类对象。 在了解这个装饰器前,你需要知道在不使用装饰器怎么写一个属性。

def getx(self):
    return self._x
def setx(self, value):
    self._x = value
def delx(self):
   del self._x
# create a property
x = property(getx, setx, delx, "I am doc for x property")

以上就是一个Python属性的标准写法,其实和Java挺像的,但是太罗嗦。有了@语法糖,能达到一样的效果但看起来更简单。

@property
def x(self): ...
# 等同于
def x(self): ...
x = property(x)

带有返回值的装饰器

  def func(functionName):
        def func_in():  	  
        	return functionName()    
        return func_in 
     @func def test():
        return ‘itheima'

到此这篇关于Python中的闭包与装饰器的用法详解的文章就介绍到这了,更多相关Python装饰器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • PyTorch中的方法torch.randperm()示例介绍

    PyTorch中的方法torch.randperm()示例介绍

    在 PyTorch 中,torch.randperm(n) 函数用于生成一个从 0 到 n-1 的随机排列的整数序列,这篇文章主要介绍了PyTorch中的方法torch.randperm()介绍,需要的朋友可以参考下
    2024-05-05
  • Python中处理字符串之isalpha()方法的使用

    Python中处理字符串之isalpha()方法的使用

    这篇文章主要介绍了Python中处理字符串之isalpha()方法的使用,是Python入门学习中的基础知识,需要的朋友可以参考下
    2015-05-05
  • 详解pandas数据合并与重塑(pd.concat篇)

    详解pandas数据合并与重塑(pd.concat篇)

    这篇文章主要介绍了详解pandas数据合并与重塑(pd.concat篇),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • Python属性私有化详解

    Python属性私有化详解

    私有化是为了实现更好的封装形式。能隐藏属性,不被随意修改。以前置双下划线__属性/方法名定义,但是结尾不能是双下划线。如果不让某些属性在类外部被随意更改,可设置成私有属性。在类定义的内部可以访问
    2023-02-02
  • python数据结构之面向对象

    python数据结构之面向对象

    这篇文章主要介绍了python数据结构之面向对象,在python数据结构的上一章节我们讲述了python的输入输出控制异常,希望大家重点掌握输出和控制,这些都是比较简单的啦,多看看就好了,接下来我们要讲的是python面向对象,需要的朋友可以参考一下
    2021-12-12
  • PyQt与pycharm的结合使用教程

    PyQt与pycharm的结合使用教程

    这篇文章主要介绍了PyQt的使用与pycharm的结合,主要包括环境安装,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • 深入理解python中sort()与sorted()的区别

    深入理解python中sort()与sorted()的区别

    Python list内置sort()方法用来排序,也可以用python内置的全局sorted()方法来对可迭代的序列排序生成新的序列。这篇文章主要介绍了python中sort()与sorted()的区别,需要的朋友可以参考下
    2018-08-08
  • 关于python3安装pip及requests库的导入问题

    关于python3安装pip及requests库的导入问题

    小编最近快毕业了,闲着无事学习下python的内容在学习到requsets库的导入问题时遇到一些问题,通过查找相关资料问题顺利解决,今天小编把问题解决思路及注意事项分享给大家供大家参考学习
    2021-05-05
  • 使用grpc实现golang后端和python服务间通信

    使用grpc实现golang后端和python服务间通信

    gRPC是Google 开发的高性能、开源的远程过程调用(RPC)框架,本文主要为大家详细介绍了如何使用grpc实现golang后端和python服务间通信,感兴趣的可以了解下
    2024-03-03
  • Python+turtle绘制对称图形的示例代码

    Python+turtle绘制对称图形的示例代码

    这篇文章主要是带大家写一个利用Turtle库绘制一些有趣的对称图形,文中的示例代码讲解详细,对我们学习Python有一定帮助,感兴趣的可以了解一下
    2022-07-07

最新评论