初识装饰器(decorator )

Python的 decorator 本质上就是一个高阶函数,它接收一个函数作为参数,然后,返回一个新函数。

使用 decorator 用Python提供的 @ 语法,这样可以避免手动编写 f = decorate(f) 这样的代码。

先看一个例子:

#!/usr/bin/env python

def outer(func):
print('这是outer。') def inner():
print('这是inner。')
func()
print('这是最后。')
return inner @outer
def f1():
print('这是F1。') f1()

输出结果:

这是outer。
这是inner。
这是F1。
这是最后。

是不是对这个输出感到惊奇?下面详细说说这段代码的执行过程。

  • 代码从上到下执行,先将outer函数和f1函数载入内存,然后调用f1()
  • 在f1函数前一行加@outer表示调用outer装饰器
  • 执行f1函数时,将函数名f1当作参数传入装饰器outer,并执行outer
  • 装饰器outer的参数func其实就是函数f1,而outer的返回值inner将赋值给f1,所以“rerurn inner”这句代码必不可少,而原来的f1将被inner取代
  • 执行装饰器outer先输出“这是outer。”,下面在执行函数inner(inner其实就是新的f1),输出"这是inner。" , 然后执行func函数,func实际上就是上一步传入的f1,所以执行func()就是执行原函数f1(),所以输出”这是F1。“, 然后再输出"这是最后。"

此执行过程需不断体会方能领悟。

如果暂时无法领悟,可以理解为装饰器就是给函数做了一个装饰,让这个函数在执行之前或者执行之后或者执行前后可以做一些其他事情。

下面对刚才的代码略作修改,使之成为一个带参数的函数。

#!/usr/bin/env python

def outer(func):
print('这是outer。') def inner(y):
print('这是inner。')
r = func(y)
print('这是最后。')
return r
return inner @outer
def f1(x):
x += 100
print('这是F1。')
return x a = f1(10)
print(a) @outer
def f2(x):
x -= 100
print('这是F2')
    return x

a = f2(10)
print(a)

输出结果:

这是outer。
这是inner。
这是F1。
这是最后。
110
这是outer。
这是inner。
这是F2。
这是最后。
-90

可以看到我们给f1添加了参数和返回值,那么对应的,装饰器里的inner也需要添加参数和返回值。

并且我们添加了函数f2,同样可以只用装饰器outer。

sleep(100000000)

仔细体会之后我们进行下一步,如果函数f2带有2个或多个参数,还能和只带一个参数的f1一起使用装饰器outer吗?

当然可以:

def outer(func):
print('这是outer。') def inner(*args,**kwargs):
print('这是inner。')
r = func(*args,**kwargs)
print('这是最后。')
return r
return inner @outer
def f1(x):
x += 100
print('这是F1。')
return x a = f1(10)
print(a) @outer
def f2(x,y,z):
a = x+y+z
print('这是F2。')
return a a = f2(10,20,30)
print(a)

输出结果:

这是outer。
这是inner。
这是F1。
这是最后。
110
这是outer。
这是inner。
这是F2。
这是最后。
60

可见f1和f2分别带有不同数量的参数,但是装饰器outer都可以起到作用。其关键是万能参数:”*args,**kwargs“

那么,同一个函数可以有多个装饰器吗?

答案是:不能

哈哈,不能是不可能的。请看:

#!/usr/bin/env python

def outer1(func):

    def inner(*args,**kwargs):
print('这是装饰器1的inner。')
r = func(*args,**kwargs)
print('这是装饰器1的结束。')
return r
return inner def outer2(func): def inner(*args,**kwargs):
print('这是装饰器2的inner。')
r = func(*args,**kwargs)
print('这是装饰器2的结束。')
return r
return inner @outer2
@outer1
def f1(x):
x += 100
print('这是F1。')
return x a = f1(10)
print(a)

输出如下:

这是装饰器2的inner。
这是装饰器1的inner。
这是F1。
这是装饰器1的结束。
这是装饰器2的结束。
110

我们将两个装饰器的顺序颠倒:

#!/usr/bin/env python

def outer1(func):

    def inner(*args,**kwargs):
print('这是装饰器1的inner。')
r = func(*args,**kwargs)
print('这是装饰器1的结束。')
return r
return inner def outer2(func): def inner(*args,**kwargs):
print('这是装饰器2的inner。')
r = func(*args,**kwargs)
print('这是装饰器2的结束。')
return r
return inner @outer1
@outer2
def f1(x):
x += 100
print('这是F1。')
return x a = f1(10)
print(a)

输出如下:

这是装饰器1的inner。
这是装饰器2的inner。
这是F1。
这是装饰器2的结束。
这是装饰器1的结束。
110

看到没?两个装饰器就相当于在外面又套了一层,多了一层装饰而已。

结论:要想你的代码看起来逼格满满,多整点装饰。。。。。。。。。。。。

最后:

还有什么更吊的装饰器吗?

#!/usr/bin/env python
# Version = 3.5.2
__auth__ = '无名小妖' def fout(a1, a2): def outer(main_func): def wrapper(request, kargs):
print(a1())
main_result = main_func(request, kargs)
print(a2())
return main_result
return wrapper
return outer def f1():
a1 = 100
return a1 def f2():
a2 = 200
return a2 @fout(f1,f2)
def Index(request,kargs):
print(request,kargs) Index(20,30)

输出如下:

100
20 30
200

'''
1、第一步:把def fout(a1,a2): 加载到内存
2、第二步:@fout(f1,f2)  ==》 调用了装饰器  ==》  return outer 然后返回给函数Index
3、第三步:执行outer函数并 Index ==》 wrapper
4、执行wrapper 函数,这样做的意义就是除了原函数给的参数外,装饰器也可以调用自己定义的参数
 
'''
 
初识迭代器(iterator)

迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。

   使用迭代器的优点

对于原生支持随机访问的数据结构(如tuple、list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值)。但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。

另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。

迭代器更大的功劳是提供了一个统一的访问集合的接口,只要定义了__iter__()方法对象,就可以使用迭代器访问。

迭代器有两个基本的方法:

  (1)__next__ 返回容器的下一个项目

  (2)__iter__ 返回迭代器本身

下面用生成斐波那契数列为例子,说明为何用迭代器

代码1

 def fab(max):
n, a, b = 0, 0, 1
while n < max:
print b
a, b = b, a + b
n = n + 1

直接在函数fab(max)中用print打印会导致函数的可复用性变差,因为fab返回None。其他函数无法获得fab函数返回的数列。

代码2

 def fab(max):
L = []
n, a, b = 0, 0, 1
while n < max:
L.append(b)
a, b = b, a + b
n = n + 1
return L

代码2满足了可复用性的需求,但是占用了内存空间,最好不要。

代码3

对比

 for i in range(1000): pass
 for i in xrange(1000): pass

前一个返回1000个元素的列表,而后一个在每次迭代中返回一个元素,因此可以使用迭代器来解决复用可占空间的问题

 class Fab(object):
def __init__(self, max):
self.max = max
self.n, self.a, self.b = 0, 0, 1 def __iter__(self):
return self def next(self):
if self.n < self.max:
r = self.b
self.a, self.b = self.b, self.a + self.b
self.n = self.n + 1
return r
raise StopIteration()

执行

1
2
3
4
5
6
7
8
9
>>> for key in Fabs(5):
    print key
 
     
1
1
2
3
5

Fabs 类通过 next() 不断返回数列的下一个数,内存占用始终为常数  。

另外,我们之前提到过,使用内置函数iter()可以获取迭代器对象:

>>> lst = range(5)
>>> it = iter(lst)
>>> it
<range_iterator object at 0x001BC518>

使用__next__()方法可以访问下一个元素:

>>> it.__next__()
0
>>> it.__next__()
1
>>> it.__next__()
2

使用__iter__()返回迭代器本身:

>>> it.__iter__()
<range_iterator object at 0x001BC518>

初识生成器(generator)

带有 yield 的函数在 Python 中被称之为 generator(生成器),几个例子说明下(还是用生成斐波那契数列说明)

可以看出代码3远没有代码1简洁,生成器(yield)既可以保持代码1的简洁性,又可以保持代码3的效果。

代码4

>>> def fab(max):
... n, a, b = 0, 0, 1
... while n < max:
... yield b
... a, b = b, a + b
... n = n + 1
...
>>> for n in fab(5):
... print(n)
...
1
1
2
3
5
>>>

  简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

  return作用

在一个生成器中,如果没有return,则默认执行到函数完毕;如果遇到return,则直接抛出 StopIteration 终止迭代。

代码5  文件读取

def read_file(fpath):
BLOCK_SIZE = 1024
with open(fpath, 'rb') as f:
while True:
block = f.read(BLOCK_SIZE)
if block:
yield block
else:
return

  如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。

循序渐进Python3(四) -- 装饰器、迭代器和生成器的更多相关文章

  1. 第四天 内置函数2 随机码 装饰器 迭代器、生成器 递归 冒泡算法 JSON

    关于函数的return li = [11,22,33,44] def f1(arg): arg.append(55) li = f1(li) print(li) 因为li = f1(li) 实际赋值的 ...

  2. Day4 装饰器——迭代器——生成器

    一 装饰器 1.1 函数对象 一 函数是第一类对象,即函数可以当作数据传递 #1 可以被引用 #2 可以当作参数传递 #3 返回值可以是函数 #3 可以当作容器类型的元素 二 利用该特性,优雅的取代多 ...

  3. (转)Python3.5——装饰器及应用详解

    原文:https://blog.csdn.net/loveliuzz/article/details/77853346 Python3.5——装饰器及应用详解(下)----https://blog.c ...

  4. python3.7 装饰器

    #!/usr/bin/env python __author__ = "lrtao2010" #python3.7 装饰器 #装饰器 ''' 定义:本质就是一个函数,作用是为其他函 ...

  5. s14 第4天 关于python3.0编码 函数式编程 装饰器 列表生成式 生成器 内置方法

    python3 编码默认为unicode,unicode和utf-8都是默认支持中文的. 如果要python3的编码改为utf-8,则或者在一开始就声明全局使用utf-8 #_*_coding:utf ...

  6. day04 装饰器 迭代器&生成器 Json & pickle 数据序列化 内置函数

    回顾下上次的内容 转码过程: 先decode  为 Unicode(万国码 ) 然后encode 成需要的格式     3.0 默认是Unicode  不是UTF-8 所以不需要指定  如果非要转为U ...

  7. day4装饰器-迭代器&&生成器

    一.装饰器 定义:本质是函数,(装饰其他函数)就是为其它函数添加附加功能 原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 实现装饰器知识储备: 1.函数及“变量” 2.高阶 ...

  8. python_装饰器——迭代器——生成器

    一.装饰器 1.什么是装饰器? 器=>工具,装饰=>增加功能 1.不修改源代码 2.不修改调用方式 装饰器是在遵循1和2原则的基础上为被装饰对象增加功能的工具 2.实现无参装饰器 1.无参 ...

  9. python装饰器,迭代器,生成器,协程

    python装饰器[1] 首先先明白以下两点 #嵌套函数 def out1(): def inner1(): print(1234) inner1()#当没有加入inner时out()不会打印输出12 ...

  10. python笔记3 闭包 装饰器 迭代器 生成器 内置函数 初识递归 列表推导式 字典推导式

    闭包 1, 闭包是嵌套在函数中的 2, 闭包是内层函数对外层函数的变量(非全局变量)的引用(改变) 3,闭包需要将其作为一个对象返回,而且必须逐层返回,直至最外层函数的返回值 闭包例子: def a1 ...

随机推荐

  1. POJ 2533 动态规划入门 (LIS)

    Longest Ordered Subsequence Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 42914 Accepte ...

  2. ✡ leetcode 167. Two Sum II - Input array is sorted 求两数相加等于一个数的位置 --------- java

    Given an array of integers that is already sorted in ascending order, find two numbers such that the ...

  3. 《统计推断(Statistical Inference)》读书笔记——第2章 变换与期望

    第二章引入了两个重要问题,随机变量的期望和随机变量的变换.期望又引申出“矩”的概念,矩是统计学理论分析的一个重要关键词,而随机变量的变换是研究复杂统计现象的重要工具.下面是这一章的思维导图

  4. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(十一) 代码重构使用反射工厂解耦

    前言 自从此博客发表以及代码开源以来,得到了许多人的关注.也没许多吧,反正在我意料之外的.包括几位大牛帮我做订阅号推广,真的很感谢他们.另外,还有几个高手给我提了一些架构上的问题.其实本身这个项目是没 ...

  5. bzoj2518: [Shoi2010]滚动的正四面体

    Description 正四面体总共有4个面,每个面都是一个正三角形.现在把它的一个面标记上字母A,如图 3中所示,A标记在底面上: 于是,这个正四面体的滚动过程就可以用一个只包含“L”“R”“B”的 ...

  6. [驱动]内核添加USB转串口驱动支持

    转自:http://blog.csdn.net/gatieme/article/details/49491325 目录 1. 问题 2. 驱动源码 3. 内核配置 4. 编译内核和模块驱动 5. 加载 ...

  7. struts1&&Hibernate Demo1

    用struts1和Hibernate实现简单登录案例 主要思路:通过用户名name验证 如果一致则验证成功. 附上源码: 1.建立student表,这里使用的是mysql数据库,以下为该表的DDL: ...

  8. 千里积于跬步——流,向量场,和微分方程[转载]

    在很多不同的科学领域里面,对于运动或者变化的描述和建模,都具有非常根本性的地位--我个人认为,在计算机视觉里面,这也是非常重要的. 什么是"流"? 在我接触过的各种数学体系中,对于 ...

  9. JavaScript 在不刷新或跳转页面的情况下改变当前浏览器地址栏上的网址

    JavaScript 在不刷新或跳转页面的情况下改变当前浏览器地址栏上的网址 var stateObject = {}; var title = "改变后的网址的标题"; var ...

  10. (gridcontrol等)通用导出excel z

    关于DevExpress Winform 的所有可打印控件的导出excel 的通用方法,并且解决DevExpress控件自带的方法存在的缺陷问题 1.解决GridControl自带方法不能导出图片: ...