python初始装饰器
python装饰器:
一,函数名的运用.
函数名是一个变量,但他是一个特殊的变量与括号配合可以执⾏行行函数的变量
1.函数名的内存地址
- def func():
- print("呵呵")
- print(func)
- 结果:
- <function func at 0x1101e4ea0>
2. 函数名可以赋值给其他变量
- def func():
- print("呵呵")
- print(func)
- a = func # 把函数当成一个变量赋值给另一个变量
- a() # 函数调⽤用 func()
3.函数名可以当做容器类的元素
- def func1():
- print("呵呵")
- def func2():
- print("呵呵")
- def func3():
- print("呵呵")
- def func4():
- print("呵呵")
- lst = [func1, func2, func3]
- for i in lst:
- i()
4.函数名可以当做函数的参数
- def func():
- print("吃了么")
- def func2(fn):
- print("我是func2")
- fn() # 执行传递过来的fn
- print("我是func2")
- func2(func) # 把函数func当成参数传递给func2的参数fn.
5.函数名可以作为函数的返回值
- def func_1():
- print("这⾥里里是函数1")
- def func_2():
- print("这⾥里里是函数2")
- print("这⾥里里是函数1")
- return func_2
- fn = func_1() # 执行函数1. 函数1返回的是函数2, 这时fn指向的就是上面函数2
- fn() # 执行上面返回的函数
二,闭包
什么是闭包? 闭包就是内层函数, 对外层函数(非全局)的变量的引用. 叫闭包
- def func1():
- name = "alex"
- def func2():
- print(name) # 闭包
- func2()
- func1()
- 结果:
- alex
我们可以使用__closure__来检测函数是否是闭包. 使用函数名.__closure__返回cell就是闭包,返回None就不是闭包
- def func1():
- name = "alex"
- def func2():
- print(name) # 闭包
- func2()
- print(func2.__closure__) # (<cell at 0x10c2e20a8: str object at
- 0x10c3fc650>,)
- func1()
问题, 如何在函数外边调用内部函数呢?
- def outer():
- name = "alex"
- # 内部函数
- def inner():
- print(name)
- return inner
- fn = outer() # 访问外部函数, 获取到内部函数的函数地址
- fn() # 访问内部函数
那如果多层嵌套呢? 很简单, 只需要一层一层的往外层返回就行了
- def func1():
- def func2():
- def func3():
- print("嘿嘿")
- return func3
- return func2
- func1()()()
由它我们可以引出闭包的好处. 由于我们在外界可以访问内部函数. 那这个时候内部函
数访问的时间和时机就不一定了了, 因为在外部, 我可以选择在任意的时间去访问内部函数. 这
个时候. 想一想. 我们之前说过, 如果一个函数执行完毕. 则这个函数中的变量量以及局部命名
空间中的内容都将会被销毁. 在闭包中. 如果变量量被销毁了了. 那内部函数将不能正常执⾏行行. 所
以. python规定. 如果你在内部函数中访问了了外层函数中的变量量. 那么这个变量量将不会消亡.
将会常驻在内存中. 也就是说. 使用闭包, 可以保证外层函数中的变量量在内存中常驻. 这样做
有什什么好处呢? 非常大的好处. 我们来看一个关于爬虫的代码:
- from urllib.request import urlopen
- def but():
- content = urlopen("http://www.xiaohua100.cn/index.html").read()
- def get_content():
- return content
- return get_content
- fn = but() # 这个时候就开始加载校花100的内容
- # 后⾯面需要⽤用到这⾥里里⾯面的内容就不不需要在执⾏行行⾮非常耗时的⽹网络连接操作了了
- content = fn() # 获取内容
- print(content)
- content2 = fn() # 重新获取内容
- print(content2)
综上, 闭包的作用就是让一个变量量能够常驻内存. 供后面的程序使用.
三. 装饰器初识
在说装饰器之前啊. 我们先说⼀一个软件设计的原则: 开闭原则, 又被成为开放封闭原则,你的代码对功能的扩展是开放的, 你的程序对修改源代码是封闭的. 这样的软件设计思路路可以更好的维护和开发.
开放: 对功能扩展开放
封闭: 对修改代码封闭
接下来我们来看装饰器. ⾸首先我们先模拟⼀一下女娲造人.
- def create_people():
- print("女娲很厉害. 捏个泥人吹口气就成了人了")
- create_people()
ok! 很简单. 但是现在问题来了了. 上古时期啊. 天⽓气很不稳定. 这时候呢⼤大旱三年年. 女娲再去
造人啊就很困难了了. 因为啥呢? 没水. 也就是说. 女娲想造人必须得先和泥. 浇点儿水才能造
人
- def create_people():
- print("浇水") # 添加了了个浇⽔水功能
- print(女娲很厉害. 捏个泥人吹口气就成了人了")
- create_people()
搞定. 但是, 我们来想想. 是不是违背了了我们最开始的那个约定"开闭原则", 我们是添加了了
新的功能. 对添加功能开放. 但是修改了了源代码啊. 这个就不好了了. 因为开闭原则对修改是封
闭的. 那怎么办. 我们可以这样做.
- def create_people():
- # print("浇水") # 添加了了个浇水功能, 不符合开闭原则了了
- print("女娲很厉害. 捏个泥人吹口气就成了人了")
- def warter():
- print("先浇水")
- create_people() # 造人
- # create_people() # 这个就不行行了了
warter() # 访问浇⽔水就好了了
现在问题又来了. 你这个函数写好了. 但是由于你添加了了功能. 重新创建了个函数. 在这之
前访问过这个函数的人就必须要修改代码来访问新的函数water() 这也要修改代码. 这个也不
好. 依然违背开闭原则. 而且. 如果你这个函数被大量的人访问过. 你让他们所有人都去改. 那
你就要倒霉了. 不干死你就见鬼了.
那怎么办才能既不修改原代码, 又能添加新功能呢? 这个时候我们就需要一个装饰器了. 装
饰器的作用就是在不修改原有代码的基础上, 给函数扩展功能
- def create_people():
- # print("浇⽔水") # 添加了了个浇⽔水功能, 不不符合开闭原则了了
- print("女娲很厉害. 捏个泥人吹口气就成了了人了")
- def warter(fn):
- def inner():
- print("浇浇水")
- fn()
- print("施肥")
- return inner
- # # create_people() # 这个就不行了.
- # warter() # 访问浇水就好了了
- func = warter(create_people)
- func() # 有人问了. 下游访问的不不依然是func么, 不还是要改么?
- create_people = warter(create_people)
- create_people() # 这回就好了了吧,
说一下执行流程:
1. 首先访问warter(create_people).
2. 把你的目标函数传递给warter的形参fn. 那么后面如果执行了了fn意味着执行了了你的目
标函数create_people
3. warter()执行就一句话. 返回inner函数. 这个时候. 程序认为warter()函数执行完. 那么
前面的create_people函数名被重新覆盖成inner函数
4. 执行create_people函数. 实际上执行的是inner函数. 而inner中访问的恰使我们最开
始传递进去的原始的create_people函数
结论: 我们使用warter函数把create_people给包装了一下. 在不修改create_people的前提下.
完成了了对create_people函数的功能添加
这是一个装饰器的雏形. 接下来我们观察一下代码. 很不好理解. 所以呢. 我们可以使用语法
糖来简化我们的代码
- def warter(fn):
- def inner():
- print("浇浇水")
- fn()
- print("施肥")
- return inner
- @warter # 相当于 create_people = warter(create_people)
- def create_people():
- print("女娲很厉害. 捏个泥人吹口气就成了人了")
- create_people()
- 结果:
- 浇浇⽔水
- 女娲很厉害. 捏个泥人吹口气就成了人了
- 施肥
我们发现, 代码运行的结果是一样的. 所谓的语法糖语法: @装饰器
类似的操作在我们生活中还有很多. 比方说. 约一约.
- # 2--> 现在啊, 这个⾏行行情⽐比较不不好, 什什么⽜牛⻤鬼蛇神都出来了了.
- # 那怎么办呢? 问问金老板. 金老板在前面给大家带路路
- # 这时候我们就需要在约之前啊. 先问问金老板了. 所以也要给函数添加一个功能, 这里依然可以使
- 用装饰器器
- def wen_jin(fn):
- def inner():
- print("问问金老板, 行情怎么样, 质量好不好")
- fn()
- print("⺾艹, 金老板骗我")
- return inner
- @wen_jin
- def yue(): # 1--> 约⼀一约函数
- print("约一约")
- yue()
ok, 接下来. 我们来看⼀一下, 我约的话, 我想约个人. 比如约wusir, 这时, 我们要给函数添加
一个参数
- # 2--> 现在啊, 这个⾏行行情⽐比较不不好, 什什么⽜牛⻤鬼蛇神都出来了了.
- # 那怎么办呢? 问问⾦金金⽼老老板. ⾦金金⽼老老板在前⾯面给⼤大家带路路
- # 这时候我们就需要在约之前啊. 先问问⾦金金⽼老老板了了. 所以也要给函数添加一个功能, 这⾥里里依然可以使
- 用装饰器器
- def wen_jin(fn):
- def inner():
- print("问问金老板, 行情怎么样, 质量好不好")
- fn()
- print("⺾艹, 金老板骗我")
- return inner
- @wen_jin
- def yue(name): # 1--> 约一约函数
- print("约一约", name)
- yue("wusir")
- 结果:
- Traceback (most recent call last):
- File "/Users/sylar/PycharmProjects/oldboy/fun_2.py", line 131, in
- <module>
- yue("wusir")
- TypeError: inner() takes 0 positional arguments but 1 was given
程序报错. 分析原因: 我们在外⾯面访问yue()的时候. 实际上访问的是inner函数. 而inner函数
没有参数. 我们给了了参数. 这肯定要报错的. 那么该怎么改呢? 给inner加上参数就好了
- def wen_jin(fn):
- def inner(name):
- print("问问金老板, 行情怎么样, 质量好不好")
- fn(name)
- print("⺾艹, 金老板骗我")
- return inner
- @wen_jin
- def yue(name):
- print("约一约", name)
- yue("wusir")
这样就够了了么? 如果我的yue()改成两个参数呢? 你是不是还要改inner. 对了. 用*args和
**kwargs来搞定多个参数的问题
- def wen_jin(fn):
- def inner(*args, **kwargs): # 接收任意参数
- print("问问⾦金金⽼老老板, ⾏行行情怎么样, 质量量好不不好")
- fn(*args, **kwargs) # 把接收到的内容打散再传递给⽬目标函数
- print("⺾艹, ⾦金金⽼老老板骗我")
- return inner
- @wen_jin
- def yue(name):
- print("约一约", name)
- yue("wusir")
搞定. 这时 wen_jin()函数就是⼀一个可以处理理带参数的函数的装饰器
光有参数还不够. 返回值呢?
- def wen_jin(fn):
- def inner(*args, **kwargs):
- print("问问金老板, 行情怎么样, 质量好不好")
- ret = fn(*args, **kwargs) # 执⾏行行⽬目标函数. 获取⽬目标函数的返回值
- print("⺾艹, 金老板骗我")
- return ret # 把返回值返回给调⽤用者
- return inner
- @wen_jin
- def yue(name):
- print("约⼀一约", name)
- return "⼩小萝莉" # 函数正常返回
- r = yue("wusir") # 这⾥里里接收到的返回值是inner返回的. inner的返回值是目标函数的返回
- 值
- print(r)
返回值和参数我们都搞定了了. 接下来给出装饰器的完整模型代码(必须记住)
- # 装饰器器: 对传递进来的函数进行包装. 可以目标函数之前和之后添加任意的功能.
- def wrapper(func):
- def inner(*args, **kwargs):
- '''在执行目标函数之前要执行的内容'''
- ret = func(*args, **kwargs)
- '''在执行目标函数之后要执行的内容'''
- return ret
- return inner
- # @wrapper 相当于 target_func = wrapper(target_func) 语法糖
- @wrapper
- def target_func():
- print("我是目标函数")
- # 调用目标函数
- target_func()
python初始装饰器的更多相关文章
- Python 初始—(装饰器)
本质上也是函数的另一种表现形式,为其它函数丰富其功能,装饰其他函数,附加功能 在不改变被装饰的函数的源代码,而且不改变被装饰的函数的调用方式,因此被装饰的函数感知不到装饰器函数的存在 分解装饰器 函数 ...
- Python各式装饰器
Python装饰器,分两部分,一是装饰器本身的定义,一是被装饰器对象的定义. 一.函数式装饰器:装饰器本身是一个函数. 1.装饰函数:被装饰对象是一个函数 [1]装饰器无参数: a.被装饰对象无参数: ...
- Python札记 -- 装饰器补充
本随笔是对Python札记 -- 装饰器的一些补充. 使用装饰器的时候,被装饰函数的一些属性会丢失,比如如下代码: #!/usr/bin/env python def deco(func): def ...
- python基础——装饰器
python基础——装饰器 由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数. >>> def now(): ... print('2015-3-25 ...
- 【转】详解Python的装饰器
原文链接:http://python.jobbole.com/86717/ Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里. 为什么需要装饰器 我们假设你的程序实现 ...
- 两个实用的Python的装饰器
两个实用的Python的装饰器 超时函数 这个函数的作用在于可以给任意可能会hang住的函数添加超时功能,这个功能在编写外部API调用 .网络爬虫.数据库查询的时候特别有用 timeout装饰器的代码 ...
- python 基础——装饰器
python 的装饰器,其实用到了以下几个语言特点: 1. 一切皆对象 2. 函数可以嵌套定义 3. 闭包,可以延长变量作用域 4. *args 和 **kwargs 可变参数 第1点,一切皆对象,包 ...
- 理解Python中的装饰器//这篇文章将python的装饰器来龙去脉说的很清楚,故转过来存档
转自:http://www.cnblogs.com/rollenholt/archive/2012/05/02/2479833.html 这篇文章将python的装饰器来龙去脉说的很清楚,故转过来存档 ...
- python基础—装饰器
python基础-装饰器 定义:一个函数,可以接受一个函数作为参数,对该函数进行一些包装,不改变函数的本身. def foo(): return 123 a=foo(); b=foo; print(a ...
随机推荐
- JS框架_(Qrcode.js)将你的内容转换成二维码格式
百度云盘 传送门 密码:304e 输入网址点击按钮生成二维码,默认为我的博客首页 二维码格式演示 <!DOCTYPE html> <html lang="en"& ...
- windows 简单实用Elasticsearch
官网下载地址:https://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/zip/e ...
- C++入门经典-例6.3-字符串之未使用字符串结束符“\0”和使用字符串结束符“\0”的区别
1:为字符串数组赋值的方式有两种,即数组元素逐一赋值和使用聚合方式赋值. 为数组元素逐一赋值.例如: pWord[0]='H'; 使用聚合方式赋值如: char pWord[]={'H','E','L ...
- charts_03
table 数值获取: 1.http://www.w3school.com.cn/jsref/dom_obj_all.asp 2.http://blog.csdn.net/xs_zgsc/articl ...
- sql server中 设置与查看锁的超时时间(ZT) @@LOCK_TIMEOUT
在数据库的应用系统中,死锁是不可避免的.通过设置死锁的处理优先级方法,可以在数据库引擎中自动检测到死锁,对发生的死锁会话进行干预,从而达到解除死锁的目点,但在这种情况下,会话只能被动的等待数据库引 ...
- 1.zookeeper原理解析-数据存储之Zookeeper内存结构
Zookeeper是怎么存储数据的,什么机制保证集群中数据是一致性,在网络异常,当机以及停电等异常情况下恢复数据的,我们知道数据库给我们提供了这些功能,其实zookeeper也实现了类似数据库的功能. ...
- P2239 螺旋矩阵
P2239 螺旋矩阵 题解 这题看上去是个暴力,但是你看数据范围啊,暴力会炸 实际上这是一道数学题QWQ 先看看螺旋矩阵是个什么亚子吧 好吧,找找规律 1 2 ... ... ... ... ... ...
- SQL server中的一些查询
SQL 不同于与其他编程语言的最明显特征是处理代码的顺序.在大数编程语言中,代码按编码顺序被处理,但是在SQL语言中,第一个被处理的子句是FROM子句,尽管SELECT语句第一个出现,但是几乎总是最后 ...
- rocketMQ 消息的 tag
tag 的使用场景:不同的消费组,订阅同一 topic 不同的 tag,拉取不同的消息并消费.在 topic 内部对消息进行隔离. producer 发送消息,指定 tag Message msg = ...
- rocketMQ 订阅关系
场景:2 个消费者进程中,创建了 2 个消费者,同属于 1 个消费组,但是订阅了不同的 topic,会因为订阅信息相互覆盖,导致拉不到消息. 原因是 rocketMQ 的订阅关系,是根据 group ...