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,这个就是闭包。

如何判断判断闭包?

# 例一:
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_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. 闭包必须是内层函数对外层函数的变量(非全局变量)的引用。

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

闭包的应用

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

闭包主要应用于装饰器

装饰器

开放封闭原则:

1.对扩展开放 -- 支持增加新功能

2.对修改源代码是封闭,对调用方式是封闭的

装饰:在原有的基础上额外添加功能

语法糖必须要放在被装饰的函数正上方

# import time
# def run_time(f):  #index 传到f 这里,系统写了一个f = index   代表这index的内存地址
                    所以底下f()是index()   还有装饰器本质就是闭包!!!!
#     def inner():
#         strat_time = time.time()    # 被装饰函数之前
#         f()
#         print(time.time() - strat_time)     # 被装饰函数之后
#    return inner      # 不能加括号

#@run_time        #语法糖的本质就是   index=run_time(index)
#def index():
    print("is index")
#
@run_time
#def func():
    print("is func")

#index()
#func()

标准版装饰器

def wrapper(func):
?       def inner(* args,**kwargs)
?       """执行被装饰函数前的操作“”
?               func(*args,**kwargs)
?       """执行被装饰函数后的操作“”“
return inner

@wrapper
def index():
?   print("is index")
index()

装饰器传参

def log_time(func):
    def make_decorater(*args, **kwargs):  # 接受调用语句的实参,在下面传递给被装饰函数(原函数)
        print('现在开始装饰')
        test_func = func(*args, **kwargs)  # 如果在这里return,则下面的代码无法执行,所以引用并在下面返回
        print('现在结束装饰')
        return test_func  # 因为被装饰函数里有return,所以需要给调用语句(test(2))一个返回,又因为test_func = func(*args,**kwargs)已经调用了被装饰函数,这里就不用带()调用了,区别在于运行顺序的不同。

    return make_decorater

@log_time   #test=log_time(test)=make_decorater
def test(num):
    print('我是被装饰的函数')
    return num + 1

a = test(2)  # test(2)=make_decorater(2)  2传到make_decorater  所以必须写*arg接收参数
print(a)

带参数的装饰器

def auth(x):
    def auth2(func):
        def inner(*args, **kwargs):
            if login_status['status']:
                ret = func()
                return ret

            if x == 'wechat':
                username = input('请输入用户名:').strip()
                password = input('请输入密码:').strip()
                if username == '太白' and password == '123':
                    login_status['status'] = True
                    ret = func()
                    return ret
            elif x == 'qq':
                username = input('请输入用户名:').strip()
                password = input('请输入密码:').strip()
                if username == '太白' and password == '123':
                    login_status['status'] = True
                    ret = func()
                    return ret
        return inner
    return auth2

@auth('wechat')
def jitter():
    print('记录美好生活')

@auth('qq')
def pipefish():
    print('期待你的内涵神评论')

@auth(‘wechat‘) :分两步:

第一步先执行auth(‘wechat‘)函数,得到返回值auth2

第二步@与auth2结合,形成装饰器@auth2 然后在依次执行。

这样就是带参数的装饰器,参数可以传入多个,一般带参数的装饰器在以后的工作中都是给你提供的, 你会用就行,但是自己也一定要会写,面试经常会遇到。

装饰器装饰多个函数

def wrapper1(func):
    def inner1(*args,**kwargs):
        print("这是装饰器一开始")
        func(*args,**kwargs)
        print("这是装饰器一结束")
    return inner1

def wrapper2(func):
    def inner2(*args,**kwargs):
        print("这是装饰器二开始")
        func(*args,**kwargs)
        print("这是装饰器二结束")
    return inner2

@wrapper1  #func = wrapper1(func)=inner1
@wrapper2  #func = wrapper2(func) = inner2
def func():
    print("这是被装饰的函数")

func()

Python规定多个装饰器装饰一个函数的时候先执行离被装饰的函数最近的装饰器
执行流程:
首先走wrapper2 最近的装饰器,语法糖wrapper2的操作用代码表示是  func = wrapper2(func) = inner2
此时 func=inner2  然后再执行wrapper1 语法糖wrapper1的操作用代码表示func = wrapper1(func)=inner1
但是wrapper1中func已经是inner2了 所以代码是func = wrapper1(inner2)=inner1
最后func()调用,先执行inner1() 先打印了这是装饰器一开始,然后执行inner里的func(),但是wrapper中func已经是inner2所以执行inner2函数,打印了这是装饰器二开始,到ineer2函数中func()执行时,此时的func是wrapper2的语法糖中的func,所以正常执行func(),打印了这是被装饰函数,然后接这执行,打印了这是装饰器二结束,由于是inner1中的func()调用的,所以返回去,继续执行print("这是装饰器一结束“),所以整个流程的结果:
这是装饰器一开始
这是装饰器二开始
这是被装饰的函数
这是装饰器二结束
这是装饰器一结束

原文地址:https://www.cnblogs.com/zzsy/p/12227816.html

时间: 2024-10-10 02:20:41

python基础三大器之装饰器的相关文章

python三大器之装饰器的练习

装饰器 加载顺序从下至上 执行顺序从上至下 ''' 多层装饰器 ''' def deco1(func): #func=deco2 def wrapper1(*args, **kwargs): '''this is wrapper1''' print('start 1') result = func(*args, **kwargs)# wrapper2() print('end 1') return result return wrapper1 def deco2(func):#func=deco

python 基础篇 12 装饰器进阶

本节主要内容:1. 通?装饰器回顾2. 函数的有?信息3. 带参数的装饰器4. 多个装饰器同时装饰?个函数 ?. 通?装饰器的回顾开闭原则: 对增加功能开放. 对修改代码封闭装饰器的作?: 在不改变原有代码的基础上给?个函数增加功能通?装饰器的写法: 执行过程: 其实执行  target_func()就是执行inner函数.  inner函数会先执行目标函数之前的代码,然后执行目标函数,之后再执行目标函数之后的代码. 如何获取函数的有用信息: 函数名.__name__可以查看函数名字 函数名._

python3 速查参考- python基础 -&gt; 函数编程之 装饰器、生成器

装饰器 1.速查笔记 #-- 函数装饰器:是它后边的函数的运行时的声明 由@符号以及后边紧跟的"元函数"(metafunction)组成 @staticmethod def smeth(x): print(x) # 等同于: def smeth(x): print(x) smeth = staticmethod(smeth) 定义:在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator) 经典示例: # -*- coding:utf-8 -*- import time

python基础四:装饰器

装饰器本质:就是函数,功能是为其他函数添加附加功能 装饰器原则: 不修改被修饰函数的源代码 不修改修饰函数的调用方式 装饰器的知识储备: 装饰器 = 高阶函数 + 函数嵌套 + 闭包 初识装饰器 先看一个需求:下面这个函数用来计算1到20的和 def calc(l): res = 0 for i in l: time.sleep(0.01) res += i return res result = calc(range(1,21)) print(result) 但现在需求有变,不仅要计算1到20

python基础-函数之装饰器、迭代器与生成器

1. 函数嵌套 1.1 函数嵌套调用 函数的嵌套调用:在调用一个函数的过程中,又调用了其他函数 def bar(): print("from in the bar.") def foo(): print("from in the foo.") bar() foo() 1.2 求函数最大值 def max2(x,y): if x > y: return x else: return y def max4(a,b,c,d): res1 = max2(a,b) re

Python基础——函数的装饰器

等待更新…………………… 后面再写 原文地址:https://www.cnblogs.com/mashangsir/p/11330234.html

Python学习之三大名器-装饰器、迭代器、生成器

Python学习之三大名器-装饰器.迭代器.生成器 一.装饰器     装饰,顾名思义就是在原来的基础上进行美化及完善,器这里指函数,所以说装饰器就是装饰函数,也就是在不改变原来函数的代码及调用方式的前提下对原函数进行功能上的完善.其核心原理其实是利用闭包.     格式 @关键字+装饰函数          被装饰函数()      注意:@行必须顶头写而且是在被装饰函数的正上方     按照形式可以分为:无参装饰器和有参装饰器,有参装饰器即给装饰器加上参数     以下示例是一个无参装饰器,

Python学习之路-装饰器&生成器&正则表达式

装饰器 通俗的讲,装饰器就是在不改变源代码基础上,给源代码增加新功能. 不改变函数的源代码.调用方式.返回值等,给函数增加新功能. 经典案例:登录装饰器, def login_decorator(func):     def inner():         if USER_TEMP["status"] == False:             print("\033[31;1m用户未登录,请先登录\033[0m")             login_atm()

python 3.x 的装饰器笔记

今天学到了python的装饰器,感觉这个东西还是稍微有些复杂,所以记录下来,方便以后的查找.虽然标题是python 3.x的装饰器,但是我也没有怎么用过python 2.x,感觉上应该是和python 2.7在用法上差不多. 现在某个视频公司有一段代码,,代码的主要功能就是看电影. 1 def watchfilm(): 2 print('You are watching film now....') 3 4 watchfil() 运行之后输出: You are watching film now