Python之闭包与装饰器

闭包

由于闭包这个概念比较难以理解,尤其是初学者来说,相对难以掌握,所以我们通过示例去理解学习闭包。

给大家提个需求,然后用函数去实现:完成一个计算不断增加的系列值的平均值的需求。

例如:整个历史中的某个商品的平均收盘价。什么叫平局收盘价呢?就是从这个商品一出现开始,每天记录当天价格,然后计算他的平均值:平均值要考虑直至目前为止所有的价格。

比如大众推出了一款新车:小白轿车。

第一天价格为:100000元,平均收盘价:100000元

第二天价格为:110000元,平均收盘价:(100000 + 110000)/2 元

第三天价格为:120000元,平均收盘价:(100000 + 110000 + 120000)/3 元

series = []
def make_averager(new_value):
    series.append(new_value)
    total = sum(series)
    return total / len(series)

print(make_averager(100000))
print(make_averager(110000))
print(make_averager(120000))

从上面的例子可以看出,基本上完成了我们的要求,但是这个代码相对来说是不安全的,因为你的这个series列表是一个全局变量,只要是全局作用域的任何地方,都可能对这个列表进行改变。

series = []
def make_averager(new_value):
    series.append(new_value)
    total = sum(series)
    return total / len(series)

print(make_averager(100000))
print(make_averager(110000))
series.append(666)  # 如果对数据进行相应改变,那么你的平均收盘价就会出现很大的问题。
print(make_averager(120000))

那么怎么办呢?有人说,你把他放在函数中不就行了,这样不就是局部变量了么?数据不就相对安全了么?

def make_averager(new_value):
    series = []
    series.append(new_value)
    total = sum(series)
    return total / len(series)

print(make_averager(100000))  # 100000.0
print(make_averager(110000))  # 110000.0
print(make_averager(120000))  # 120000.0

这样计算的结果是不正确的,那是因为执行函数,会开启一个临时的名称空间,随着函数的结束而消失,所以你每次执行函数的时候,都是重新创建这个列表,那么这怎么做呢?这种情况下,就需要用到我们讲的闭包了,我们用闭包的思想改一下这个代码。

def make_averager():

    series = []
    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)

    return averager

avg = make_averager()
print(avg(100000))
print(avg(110000))
print(avg(120000))

大家仔细看一下这个代码,我是在函数中嵌套了一个函数。那么avg 这个变量接收的实际是averager函数名,也就是其对应的内存地址,我执行了三次avg 也就是执行了三次averager这个函数。那么此时你们有什么问题?

肯定有学生就会问,那么我的make_averager这个函数只是执行了一次,为什么series这个列表没有消失?反而还可以被调用三次呢?这个就是最关键的地方,也是闭包的精华所在。我给大家说一下这个原理,以图为证:

? 上面被红色方框框起来的区域就是闭包,被蓝色圈起来的那个变量应该是make_averager()函数的局部变量,它应该是随着make_averager()函数的执行结束之后而消失。但是他没有,是因为此区域形成了闭包,series变量就变成了一个叫自由变量的东西,averager函数的作用域会延伸到包含自由变量series的绑定。也就是说,每次我调用avg对应的averager函数 时,都可以引用到这个自用变量series,这个就是闭包。

闭包的定义:

? 1. 闭包是嵌套在函数中的函数。

? 2. 闭包必须是内层函数对外层函数的变量(非全局变量)的引用。

如何判断判断闭包?举例让同学回答:

# 例一:
def wrapper():
    a = 1
    def inner():
        print(a)
    return inner
ret = wrapper()

# 例二:
a = 2
def wrapper():
    def inner():
        print(a)
    return inner
ret = wrapper()

# 例三:

def wrapper(a,b):
    def inner():
        print(a)
        print(b)
    return inner
a = 2
b = 3
ret = wrapper(a,b)

以上三个例子,最难判断的是第三个,其实第三个也是闭包,如果我们每次去研究代码判断其是不是闭包,有一些不科学,或者过于麻烦了,那么有一些函数的属性是可以获取到此函数是否拥有自由变量的,如果此函数拥有自由变量,那么就可以侧面证明其是否是闭包函数了(了解):

def make_averager():

    series = []
    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)

    return averager
avg = make_averager()
# 函数名.__code__.co_freevars 查看函数的自由变量
print(avg.__code__.co_freevars)  # ('series',)
当然还有一些参数,仅供了解:

# 函数名.__code__.co_varnames 查看函数的局部变量
print(avg.__code__.co_varnames)  # ('new_value', 'total')
# 函数名.__closure__ 获取具体的自由变量对象,也就是cell对象。
# (<cell at 0x0000020070CB7618: int object at 0x000000005CA08090>,)
# cell_contents 自由变量具体的值
print(avg.__closure__[0].cell_contents)  # []

闭包的作用:保存局部信息不被销毁,保证数据的安全性。

闭包的应用

  1. 可以保存一些非全局变量但是不易被销毁、改变的数据。
  2. 装饰器。

闭包的解释:

闭包是存在嵌套函数当中的 内层对外层非全局变量的的引用 ,称之为自由变量 它不会随着函数的结束而消失,一直保存在内存,最终的目的是保证数据的安全

装饰器

开放封闭原则

软件面世时,不可能吧所有的功能都设计好,当前的未来一两年功能给你上线,定期更新迭代.对于软件之前写的源代码一般都不会修改,对函数里面的代码以及函数的调用方式.

开放原则: 在源码不改变的情况下,增加一些额外的功能

封闭原则: 不要改变源代码

python中装饰器: 完美诠释的开放封闭原则

装饰器 就是个函数 : 他要装饰一个函数,在不改变原函数以及调用方式的前提下,给其增加一个额外的功能

装饰器初识

# 1 李业,在一家xx科技有限公司工作,主管安排了一个任务,
# 写一个代码测试怼怼哥写的函数的执行效率。
# import time
# def index():
#     time.sleep(2)
#     print('欢迎访问博客园首页')

# print(time.time())
# start_time = time.time()
# index()
# end_time = time.time()
# print(f'此函数的执行效率{end_time-start_time}')

# 2. 主管让你测试小邓,李大象,重复代码太多。
#
# def func1():
#     time.sleep(2)
#     print('欢迎访问日记首页')
#
#
# def func2():
#     time.sleep(1)
#     print('欢迎访问评论首页')

# start_time = time.time()
# func1()
# end_time = time.time()
# print(f'此函数的执行效率{end_time-start_time}')
#
# start_time = time.time()
# func2()
# end_time = time.time()
# print(f'此函数的执行效率{end_time-start_time}')

# 3.  整合到函数中

# def func1():
#     time.sleep(2)
#     print('欢迎访问日记首页')
#
#
# def func2():
#     time.sleep(1)
#     print('欢迎访问评论首页')

# def test_time(x):
#     start_time = time.time()
#     x()
#     end_time = time.time()
#     print(f'此函数的执行效率{end_time-start_time}')

# test_time(func1)
# test_time(func2)

# 4. 怼怼哥这个函数在实际项目中被500执行,主管要求:在被执行此函数时,
# 同时要测试一下被执行函数的效率。

# def index():
#     time.sleep(2)
#     print('欢迎访问博客园首页')
#
# # index()
# def test_time(x):
#     start_time = time.time()
#     x()
#     end_time = time.time()
#     print(f'此函数的执行效率{end_time-start_time}')
#
# test_time(index)

# 版本4的问题: 开放原则满足了,封闭原则:不改变原函数的源码,以及调用方式。
# 违反了封闭原则:改变了函数的调用方式。

# 版本5: 不能改变原函数的调用方式(闭包):

# def index():
#     time.sleep(2)
#     print('欢迎访问博客园首页')
#
# # index()
#
# # def func1():
# #     time.sleep(2)
# #     print('欢迎访问日记首页')
#
# def test_time(x):  # x = index
#     def inner():
#         start_time = time.time()
#         x()
#         end_time = time.time()
#         print(f'此函数的执行效率{end_time-start_time}')
#     return inner
#
# index = test_time(index)
# index()

# 语法糖 @加上装饰器函数的名

# def f():
#     print(666)
#
#
# f = '太白'
# print(f)

# def test_time(x):  # x = index
#     def inner():
#         start_time = time.time()
#         x()
#         end_time = time.time()
#         print(f'此函数的执行效率{end_time-start_time}')
#     return inner
#
#
# # @test_time  # index = test_time(index)
# def index():
#     time.sleep(2)
#     print('欢迎访问博客园首页')

# index = test_time(index)
# index()

# def func1():
#     time.sleep(2)
#     print('欢迎访问日记首页')

# @test_time
# def func2():
#     time.sleep(1)
#     print('欢迎访问评论首页')

# func2 = test_time(func2)
# func3 = test_time(func3)
# func2()
'''100行代码'''
# index()

'''10行代码'''
# index()

'''50行代码'''
# index()

# 版本6:被装饰函数有返回值

# def test_time(x):  # x = index
#     def inner():
#         start_time = time.time()
#         ret = x()
#         # print(F'ret: {ret}')
#         end_time = time.time()
#         print(f'此函数的执行效率{end_time-start_time}')
#         return ret
#     return inner
#
#
# @test_time  # index = test_time(index)
# def index():
#     time.sleep(0.5)
#     print('欢迎访问博客园首页')
#     return True
#
# print(index())  # inner()
# 你应该是让True返回给index()这样才完美了,但是现在index是inner,所以你要是完全不改变原函数的使用,
# 你print(index()) ---> True

# 版本7: 被装饰函数带参数,无论加不加装饰器,你的实参'太白金星'应该传给形参n,。
# 但版本6不能实现传参,index('太白金星') ==  inner('太白金星')
#
# def test_time(x):  # x = index
#     def inner(*args,**kwargs):
#         # 函数的定义:* ** 聚合。
#         # args = ('苹果')
#         #args = (1, 3)
#         start_time = time.time()
#         ret = x(*args,**kwargs)
#         # 函数的执行:* ** 打散。
#         # ret = x(*('苹果'))  ==x('苹果',)
#         # ret = x(*(1, 3))  ==x(1,3)
#         # print(F'ret: {ret}')
#         end_time = time.time()
#         print(f'此函数的执行效率{end_time-start_time}')
#         return ret
#     return inner
#
#
# # @test_time  # index = test_time(index)
# def index(n):
#     time.sleep(0.5)
#     print(f'欢迎{n}访问博客园首页')
#     return True
#
# # @test_time  # index = test_time(index)
# def func2(a,b):
#     time.sleep(0.5)
#     print(f'最终结果:{a+b}')
#     return a + b
#
#
# print(index('苹果'))  # inner('苹果')
# print(func2(1,3)) # == inner(1,3)

# def warpper(f):
#     def inner(*args,**kwargs):
#         '''被装饰函数之前的操作'''
#         # print(666)
#         ret = f(*args,**kwargs)
#         '''被装饰函数之后的操作'''
#         # print('执行完毕了')
#         return ret
#     return inner
#
# @warpper
# def func():
#     print(111)
# #
# func()
# func()
# func()
# func()
# func()

# 装饰器的应用:在不改变原函数的源码以及调用方式前提下,为其增加额外的功能。
# 登陆认证,打印日志等。

原文地址:https://www.cnblogs.com/Jacob-yang/p/11107496.html

时间: 2024-08-30 07:45:08

Python之闭包与装饰器的相关文章

Python函数编程——闭包和装饰器

Python函数编程--闭包和装饰器 一.闭包 关于闭包,即函数定义和函数表达式位于另一个函数的函数体内(嵌套函数).而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量.参数.当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包.也就是说,内部函数会在外部函数返回后被执行.而当这个内部函数执行时,它仍然必需访问其外部函数的局部变量.参数以及其他内部函数.这些局部变量.参数和函数声明(最初时)的值是外部函数返回时的值,但也会受到内部函数的影响. def outer(

Python之闭包and装饰器

闭包和装饰器是Python中非常重要的一种语法格式,在日常工作中应用非常广泛. 首先,我先为大家简单的接受一下闭包的概念. 闭包:闭包是在函数嵌套的基础上,内层函数使用到外层函数的变量,且外层函数返回内层函数的引用的一种语法格式. 闭包的基本格式,代码实现: def outer(): num = 0 def inner(): # 使用外部函数的变量 print(num) return inner # 返回inner函数在内存空间中的地址 # 将outer函数的返回值赋值给变量f,也就是说将f指向

python的闭包及装饰器

闭包: 闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数.这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外.所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体 1.函数是一个对象 2.函数执行完成后内部变量回收 3.函数属性 4.函数的返回值 实例一. 分别检测分数科目总分为100.150两种情况的成绩 初级代码如下: # -*- coding:utf-8 -*- ###检测分数总数为100分的及格情况

python的闭包、装饰器和lambda等(笔记)

参考: http://blog.csdn.net/marty_fu/article/details/7679297(闭包,推荐看这个) https://foofish.net/python-decorator.html(装饰器,推荐) http://www.cnblogs.com/tqsummer/archive/2010/12/27/1917927.html(yield) http://www.cnblogs.com/longdouhzt/archive/2012/05/19/2508844.

Python高级--闭包与装饰器

前言:在Python中,闭包是一种非常有用的功能!它通常与装饰器一起搭配使用,可以在不改变被装饰函数的功能的基础上,完成更多的功能.如权限认证. 一.如何定义闭包 1.闭包就是两个嵌套的函数,外层函数返回内层函数的引用,而且外层函数必须携带参数!为什么外层函数必须要有参数呢?可以思考一下! 基本格式如下: 1 def outer_fun(func): 2 def inner_fun(): 3 pass 4 return inner_fun 2.与普通函数的区别: 闭包的外层函数的变量可以在内存中

python中闭包和装饰器

---恢复内容开始--- 使用像javascript和python这样支持面向对象范式的语言进行编程时,都会涉及到闭包的概念以及闭包的使用.我们今天就从这两个方面来讨论一下闭包: 首先是维基百科中关于闭包的概念:在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包.闭包可以用来在一个函数与一组"私有"变量之间创建关联关系.在给定函数被多次调用的过程中,这些私有变量能够保持其持久性. 根据这句话,其实我们自己就可以总结出在python语

Python作用域--&gt;闭包函数--&gt;装饰器

1.作用域: 在python中,作用域分为两种:全局作用域和局部作用域. 全局作用域是定义在文件级别的变量,函数名.而局部作用域,则是定义函数内部. 关于作用域,我要理解两点:a.在全局不能访问到局部定义的变量 b.在局部能够访问到全局定义的变量,但是不能修改全局定义的变量(当然有方法可以修改) 下面我们来看看下面实例: x = 1 def funx(): x = 10 print(x) # 打印出10 funx() print(x) # 打印出1 如果局部没有定义变量x,那么函数内部会从内往外

python之闭包,装饰器

目录 函数对象 :相当于变量名 函数对象的作用: 1. 可以引用 2. 可以作为函数的返回值 3. 可以作为容器的元素 4. 可以作为函数的参数 闭包 定义: python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure) 闭包的意义: 返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得该函数无论在何处调用,优先使用自己外层包裹的作用域 装饰器 本质:装饰器 = 函数.

python的闭包和装饰器

闭包 闭包的定义 1.闭:定义在函数内部的函数 2.包:内部函数引用了外部函数作用域的名字 函数的参数传达 1.直接传参 def index1(username): print(username) 2.闭包来实现函数内参数传达 def outter(x,y): # x = 1 # y = 40 def my_max(): if x > y: return x+y return y return my_max res1 = outter(1, 40) print(res1) res2 = outt