广告
返回顶部
首页 > 资讯 > 后端开发 > Python >python总结之闭包和装饰器
  • 599
分享到

python总结之闭包和装饰器

2024-04-02 19:04:59 599人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

目录一、装饰器1.装饰器的简单介绍2.装饰器的解析过程二、闭包三、闭包中nonlocal语句的使用1.外部变量的引用和改写2.nolocal的使用及特点四、闭包与装饰器五、闭包的作用

一、装饰器

1. 装饰器的简单介绍

“装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数”,听起来有点绕,没关系,直接看示意图,其中 a 为与装饰器 @a 对应的函数, b 为装饰器修饰的函数,装饰器@a的作用是:

在这里插入图片描述

举个栗子:


def test(func):
    return func
@test
def afunc():
    print("hello")
afunc()

# hello

上面使用@test来表示装饰器,其等同于:afunc = test(afunc),因此装饰器本质上就是个语法糖,其作用为简化代码,以提高代码可读性。

2. 装饰器的解析过程

step1. python 解释器发现@test,就去调用与其对应的test函数

step2. test函数调用前要指定一个参数,传入的就是@test下面修饰的函数,也就是afunc()

step3. test() 函数执行,调用 afunc(),afunc() 打印“hello”

二、闭包

在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

闭包并不是Python中特有的概念,所有把函数做为一等公民的语言均有闭包的概念。不过像Java这样以class为一等公民的语言中也可以使用闭包,只是它得用类或接口来实现。

通过Python的语言介绍,一个闭包就是你调用了一个函数A,这个函数A返回了一个函数B给你。这个返回的函数B就叫做闭包。你在调用函数A的时候传递的参数就是自由变量。

举个栗子:


def func(name):
    def inner_func(age):
        print 'name:', name, 'age:', age
    return inner_func

bb = func('the5fire')
bb(26)  # >>> name: the5fire age: 26

这里面调用func的时候就产生了一个闭包——inner_func,并且该闭包持有自由变量——name,因此这也意味着,当函数func的生命周期结束之后,name这个变量依然存在,因为它被闭包引用了,所以不会被回收。

三、闭包中nonlocal语句的使用

1. 外部变量的引用和改写

在 python 的函数内,可以直接引用外部变量,但不能改写外部变量。

例如在下面的栗子,

counter中可以正常打印常量count,但无法改变count;对list可以执行append操作,正常修改。


def cnt(param):
    count = 0
    alist = []
    def counter():
        alist.append(1)
        # count += 1 # UnboundLocalError: local variable 'count' referenced before assignment
        print(param, str(count), alist)
    return counter

test = cnt("test")
test()
# test 0 [1]

2. nolocal的使用及特点

为了解决上述不可变变量的修改问题:

python 2 中可以在函数内使用 global 语句,但全局变量在任何语言中都不被提倡,因为它很难控制。python 3 中引入了 nonlocal 语句解决了这个问题。

Nonlocal 与 global 的区别在于:nonlocal 语句会去搜寻本地变量与全局变量之间的变量,其会优先寻找层级关系与闭包作用域最近的外部变量。


def cnt(param):
    count = 0
    def counter():
        nonlocal count
        count += 1
        print(param, str(count))
    return counter

test = cnt("test")
test()

# test 1

四、闭包与装饰器

上面已经简单演示了装饰器的功能,事实上,装饰器就是一种的闭包的应用,只不过其传递的(自由变量)是函数:

使用装饰器的写法:


def make1(fn):
    def wrapped():
        return "<a>" + fn() + "</a>"
    return wrapped

def make2(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

@make1
@make2
def hello():
    return "hello"

print(hello())
# <a><b>hello</b></a>

显式使用闭包的写法:


def make1(fn):
    def wrapped():
        return "<a>" + fn() + "</a>"
    return wrapped

def make2(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

def hello():
    return "hello"

hello = make2(hello)
hello = make1(hello)
print(hello())
# <a><b>hello</b></a>

多个装饰器装饰一个函数时,执行时的顺序是:最先装饰的装饰器,最后一个执行。它遵循了先进后出规则 类似于stack。

五、闭包的作用

闭包的最大特点是可以将父函数的变量与内部函数绑定,并返回绑定变量后的函数(也即闭包),此时即便生成闭包的环境(父函数)已经释放,闭包仍然存在。

这个过程很像类(父函数)生成实例(闭包),不同的是父函数只在调用时执行,执行完毕后其环境就会释放,而类则在文件执行时创建,一般程序执行完毕后作用域才释放,因此对一些需要重用的功能且不足以定义为类的行为,使用闭包会比使用类占用更少的资源,且更轻巧灵活。

假设我们仅仅想打印出各类动物的叫声,分别以类和闭包来实现:

在这里插入图片描述

样的,但显然类的实现相对繁琐,且这里只是想输出一下动物的叫声,定义一个 Animal 类未免小题大做,而且 voice 函数在执行完毕后,其作用域就已经释放,但 Animal 类及其实例 dog 的相应属性却一直贮存在内存中。

除此之外,闭包还有很多其他功能,比如用于封装等,另外,闭包有效的减少了函数参数的数目,这对并行计算非常有价值,比如可以让每台电脑负责一个函数,然后串起来,实现流水化的作业等。

六、几个小栗子

栗子1:


def outer(f):
    def inner(*arg, **kargs):
        inner.co += 1
        return f(*arg, **kargs)
    inner.co = 0
    return inner

@outer
def cu():
    pass

if __name__ == '__main__':
    cu()
    cu()
    cu()
    print(cu.co)
# 3

栗子2:

下述样例中,注意点:

  • 首先解析装饰器A,装饰器装饰了几个类就执行几次,输出两次“i赋值”
  • B和C为两个对象,属性值单独累加。

def A(func):
    def inner():
        inner.i += 1
        print("i加1,i={0}".fORMat(inner.i))
    inner.i = 0
    print("i赋值")
    return inner

@A
def B():
    pass

@A
def C():
    pass
B()
B()
B()
C()
C()
print(id(B), id(B.i))
print(id(C), id(C.i))

i赋值
i赋值
i加1,i=1
i加1,i=2
i加1,i=3
i加1,i=1
i加1,i=2
281473235252496 187650677653032
281473235252768 187650677653000

栗子3

  • 装饰器是在python解释器加载test函数的时候就完成的,即使不调用test函数,也会输出"bbb"和"aaa",输出顺序为,dec_b装饰了test,执行输出bbb,dec_a装饰了dec_b,执行输出aaa;
  • 执行test等同于执行dec_a(dec_b(test))
def dec_a(function):    print("aaa")    def inner_func():        print("before function")        function()    return inner_funcdef dec_b(function):    print("bbb")    def inner_func():        function()        print("after function")    return inner_func@dec_a@dec_bdef test():    print("test")test()
bbbaaabefore functiontestafter function

七、特殊的装饰器

property 装饰器

参考这篇文章:https://www.tianqiweiqi.com/python-property.html

property 是Python中很赞的概念,它使得面向对象编程更加简单。

在Python中,property()是一个内置函数,用于创建和返回一个property对象。Property对象有三个方法,getter(), setter()和delete(),用来在对象创建后设置fget,fset和fdel。

装饰器(decorator)可以给函数动态加上功能,对于类的方法,装饰器一样起作用。Python内置的@property装饰器就是负责把一个方法变成属性调用的。属性是对事物某种特性的抽象,面向对象编程中一个重要概念;区别于字段,它通常表示为字段的扩展,加以访问与设置保护机制。

1. 我们为什么需要用到property

博文中假设了一种场景,假设我们有一个存储并转化温度的需求,可以通过类实现:


class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature
    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

然后通过类实例进行温度的设定和获取,且可以看到这个属性已经被添加man.__dict__中了。


>>> man = Celsius()
>>> man.temperature = 37
>>> man.temperature
37
>>> man.to_fahrenheit()
98.60000000000001

>>> man.__dict__
{'temperature': 37}

但是此时如果我们需要对温度的设定进行一定的约束,此前的方案是没办法做到的。

2. 使用Getters和Setters

对于上边的约束,一个很容易想到的解决方案是隐藏其温度属性(使其私有化),并且定义新的用于操作温度属性的getter和setter接口。可以这么实现:


class Celsius:
    def __init__(self, temperature = 0):
        self.set_temperature(temperature)
 
    def to_fahrenheit(self):
        return (self.get_temperature() * 1.8) + 32

    def get_temperature(self):
        return self._temperature
 
    def set_temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        self._temperature = value

上述方案虽然满足了基本需求,但是有个问题是,在赋值和调用时,需要修改调用方式,例如obj.temperature需改为obj.get_temperature()obj.temperature = val改为obj.set_temperature(val)。

我们希望我们的更新是不向后兼容地。这就是需要property闪亮登场的地方。

3. property的作用

对于上边的问题,Python式的解决方式是使用property,在setter中进行参数校验:


class Celsius:
    def __init__(self, temperature = 0):
        self._temperature = temperature
 
    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32
 
    @property
    def temperature(self):
        print("Getting value")
        return self._temperature
 
    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self._temperature = value

在Python中,property()是一个内置函数,用于创建和返回一个property对象。该函数的签名为:


property(fget=None, fset=None, fdel=None, doc=None)

只定义getter方法,不定义setter方法就是一个只读属性;
否则为可读可写属性,且在setter中进行参数校验。

4. 小栗子


class Student():
    def __init__(self):
        self._score = 10000

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if value < 0:
            print("wrong value")
            return
        self._score = value

    @score.deleter
    def score(self):
        del self._score


a = Student()
a.score = 99
print(a.score)

del a.score
a.score = -1
print(a.score)

# 99
# wrong value
# AttributeError: 'Student' object has no attribute '_score'

staticmethod装饰器和classmethod装饰器

python面向对象编程中,类中定义的方法:

  • @classmethod 装饰的类方法:第一个参数必须是cls
  • @staticmethod 装饰的静态方法:和普通的函数没有区别
  • 不带装饰器的实例方法:第一个参数必须是 self

以一个简单的代码为例,执行方式如下:


class A(object):   # 创建一个类对象,初始化类属性和方法
    def m1(self, n):
        print("self:", self)

    @classmethod
    def m2(cls, n):
        print("cls:", cls)

    @staticmethod
    def m3(n):
        pass

a = A() # 调用类构造器,构造实例对象a
a.m1(1) # 内部把[实例对象a]传递给[self]进行绑定,self和a指向同一个实例对象。
A.m2(1) # 内部把[类对象A]传递给[cls],cls和A都指向类对象。
A.m3(1)

下面分别使用不同的类方法进行代码的测试

step1:定义实例方法count()。

Spam.numInstances为类调用,直接返回初始化的99;x.numInstances为实例化调用,在实例化时调用了init构造方法,调用了实例方法count,在99的基础上加1。

Sub.numInstances, Other.numInstances为类调用,直接返回初始化的1;y1.numInstances, z1.numInstances为实例化调用,由于sub和other子类继承了父类spam,且在内部没有定义init方法,因此返回父类的init,调用count,在初始化的基础上加1。


class Spam:
    numInstances = 99
    def count(self):
        self.numInstances += 1
    def __init__(self):
        self.count()

class Sub(Spam):
    numInstances = 0

class Other(Spam):
    numInstances = 0

x = Spam()
y1, y2 = Sub(), Sub()
z1, z2, z3 = Other(), Other(), Other()
print(x.numInstances, y1.numInstances, z1.numInstances)
print(Spam.numInstances, Sub.numInstances, Other.numInstances)

100 1 1
99 0 0

step2:定义静态方法count()。

每次实例化都会调用init方法,调用count对类属性Spam.numInstances的值进行累加,因此实例化几次,就会累加多少次。


class Spam:
    numInstances = 99
    @staticmethod
    def count():
        Spam.numInstances += 1
    def __init__(self):
        self.count()

class Sub(Spam):
    numInstances = 0

class Other(Spam):
    numInstances = 0

x = Spam()
y1, y2 = Sub(), Sub()
z1, z2, z3 = Other(), Other(), Other()
print(x.numInstances, y1.numInstances, z1.numInstances)
print(Spam.numInstances, Sub.numInstances, Other.numInstances)

105 0 0
105 0 0

step3:定义类方法count()。

在实例化Sub和Other子类时,子类内部定义了numInstances,因此会在cls.numInstances += 1时,分别在Sub和Other各自的numInstances 分别进行累加,实例化多少次,进行多少次累加。


class Spam:
    numInstances = 99
    @classmethod
    def count(cls):
        cls.numInstances += 1
    def __init__(self):
        self.count()

class Sub(Spam):
    numInstances = 0

class Other(Spam):
    numInstances = 0

x = Spam()
y1, y2 = Sub(), Sub()
z1, z2, z3 = Other(), Other(), Other()
print(x.numInstances, y1.numInstances, z1.numInstances)
print(Spam.numInstances, Sub.numInstances, Other.numInstances)

100 2 3
100 2 3

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!

--结束END--

本文标题: python总结之闭包和装饰器

本文链接: https://www.lsjlt.com/news/161311.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • python总结之闭包和装饰器
    目录一、装饰器1.装饰器的简单介绍2.装饰器的解析过程二、闭包三、闭包中nonlocal语句的使用1.外部变量的引用和改写2.nolocal的使用及特点四、闭包与装饰器五、闭包的作用...
    99+
    2022-11-12
  • python中函数总结之装饰器闭包详解
    1、前言 函数也是一个对象,从而可以增加属性,使用句点来表示属性。 如果内部函数的定义包含了在外部函数中定义的对象的引用(外部对象可以是在外部函数之外),那么内部函数被称之为闭包。 2、装饰器 装饰器就是包...
    99+
    2022-06-04
    详解 函数 python
  • Python必备基础之闭包和装饰器知识总结
    目录一、闭包1.1 三要素 1.2 语法1.3 优点1.4 缺点1.5 作用二、装饰器 Decorator2.1 定义2.2 语法2.3 本质2.4 装饰器链一、闭包 1.1 三要素...
    99+
    2022-11-12
  • Python 装饰器总结
    装饰器是高阶函数,装饰器是对传入函数的功能增强。 装饰器的副作用:原函数对象的属性都被替换了,而使用了装饰器,查看到的函数对象属性就变成装饰器的函数对象了,如果需要查被装饰的函数的属性?对用原函数的属性覆盖掉装饰器的属性。解决方法:通过co...
    99+
    2023-01-31
    Python
  • python 闭包&装饰器(一)
    一、闭包 1.举例 def outer(): x = 10 def inner(): # 内部函数 print(x) # 外部函数的一个变量 return inner # 调用in...
    99+
    2023-01-30
    python
  • python高级语法之闭包和装饰器详解
    目录一、闭包二、一个简单的例子三、装饰器3.1 简单装饰器3.1.1 使用装饰器的语法糖3.1.2 装饰器的执行时机3.2 通用装饰器3.2.1 装饰带有参数的函数3.2.2. 装饰...
    99+
    2022-11-12
  • Python装饰器-闭包与函数装饰器
    一、闭包在学习装饰器前,需要先了解闭包的概念。形成闭包的要点:函数嵌套将内部函数作为外部函数的返回值内部函数必须要使用到外部函数的变量下面以一个计算列表平均值的案例来讲解闭包:def make_average(): # 创建一个列表,用来保...
    99+
    2023-05-14
    Python 函数 装饰器
  • 简析Python的闭包和装饰器
    什么是装饰器? 装饰器(Decorator)相对简单,咱们先介绍它:“装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数”,听起来有点绕,没关系,直接...
    99+
    2022-06-04
    简析 Python
  • Python 装饰器的总结(一)
    先来说明下几个定义:1,函数在python中,函数通过def关键字、函数名和可选的参数列表定义。通过return关键字返回值。我们举例来说明如何定义和调用一个简单的函数:1234567#coding:UTF8 def foo():     ...
    99+
    2023-01-31
    Python
  • python闭包和装饰器你了解吗
    目录一、闭包1. 什么是闭包?2. 形成闭包的三个条件(缺一不可)3. 闭包的原理4. 闭包的好处二、装饰器1. 什么是装饰器2. 装饰器有什么用3. 小 练 习三. 编写...
    99+
    2022-11-12
  • 深入理解python中的闭包和装饰器
    python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。 以下说明主要针对 python2.7...
    99+
    2022-06-04
    python
  • 怎么在python中使用闭包和装饰器
    本篇文章为大家展示了怎么在python中使用闭包和装饰器,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一、闭包闭包的形成条件:函数嵌套。内部函数使用了外部函数的变量或者参数。外部函数返回了使用外 部...
    99+
    2023-06-15
  • Python pytest装饰器总结(实例详解)
    几个常用装饰器 pytest.ini 配置文件 例子: [pytest] addopts = -v -s --html=py_test/scripts/report/report...
    99+
    2022-11-12
  • Python闭包与装饰器怎么定义
    这篇文章主要介绍“Python闭包与装饰器怎么定义”,在日常操作中,相信很多人在Python闭包与装饰器怎么定义问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python闭包与装饰器怎么定义”的疑惑有所帮助!...
    99+
    2023-07-06
  • python中的闭包和装饰器的使用示例
    目录函数参数闭包的构成条件基础的闭包的使用nonloal关键字的使用基础代码实现(装饰器)装饰器的基本使用装饰器的使用有参数的装饰器的使用带有返回值的装饰器带有不定长参数的装饰器带有...
    99+
    2022-11-21
    python闭包和装饰器 python装饰器
  • Python的闭包和装饰器你真的了解吗
    目录闭包装饰器总结闭包 闭包就是能够读取其他函数内部变量的函数。 def test1(k, b): def test1_1(x): print(k*x+b) ...
    99+
    2022-11-13
  • Python 中闭包与装饰器案例详解
    目录1.Python中一切皆对象2.函数式第一类对象3.函数对象 vs 函数调用4.闭包&LEGB法则5.装饰器&语法糖(syntax sugar)6. 回归问题项目...
    99+
    2022-11-12
  • python 进阶学习之python装饰器小结
    装饰器总结 什么是装饰器?处理函数的函数,加一个功能,但是不影响原来函数的内部结构生活中的例子:给手机加一个外壳,外壳保护了手机 装饰器有什么用?增强函数的功能 装饰器使用场景增加被...
    99+
    2022-11-12
  • Python基础globlalnonlocal和闭包函数装饰器语法糖
    目录一、global与nonlocal1、global2、nonlocal二、函数名的多种用法三、闭包函数1、什么是闭包函数2、闭包函数需满足的条件3、闭包函数的作用4、闭包函数的实...
    99+
    2022-11-11
  • python中*args与**kwarsg及闭包和装饰器的用法
    目录*args与**kwarsg及闭包和装饰器过程Python fun(*args,**kwargs)中*args,**kwargs参数含义及用法1. Python函数中的两种参数2...
    99+
    2022-11-11
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作