循序渐进Python3(四) -- 装饰器、迭代器和生成器

初识装饰器(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

#!/usr/bin/env python

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

。‘)
    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,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。

时间: 2024-10-30 01:38:43

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

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

关于函数的return li = [11,22,33,44] def f1(arg): arg.append(55) li = f1(li) print(li) 因为li = f1(li) 实际赋值的是f1的return,那么在这种情况下函数f1并未定义return,所以默认返回None 因此li的值应该是none 如果是 li = [11,22,33,44] def f1(arg): arg.append(55) f1(li) print(li) 因为函数传递的参数实际是参数的引用,因此在函数

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

一 装饰器 1.1 函数对象 一 函数是第一类对象,即函数可以当作数据传递 #1 可以被引用 #2 可以当作参数传递 #3 返回值可以是函数 #3 可以当作容器类型的元素 二 利用该特性,优雅的取代多分支的if def foo(): print('foo') def bar(): print('bar') dic={ 'foo':foo, 'bar':bar, } while True: choice=input('>>: ').strip() if choice in dic: dic[ch

python_day04 函数嵌套 名称空间和作用域 闭包 装饰器 迭代器 生成器 列表解析 三元表达式 生成器表达式

本节课重要知识点内容如下: 函数嵌套 名称空间和作用域 闭包 装饰器 迭代器 生成器 列表解析 三元表达式 生成器表达式 1.函数嵌套 函数的嵌套调用:在调用一个函数的过程中,又调用了其他函数函数的嵌套定义:在一个函数的内部,又定义另外一个函数 def bar(): print('from nbar')def foo(): print('from foo') bar()foo()def max2(x,y): if x > y: return x else: return ydef max4(a,

函数嵌套 ,名称空间与作用域 ,闭包函数 ,装饰器 ,迭代器, 生成器 三元表达式,列表解析,生成器表达式 递归与二分法, 内置函数

函数嵌套名称空间与作用域闭包函数装饰器迭代器生成器三元表达式,列表解析,生成器表达式递归与二分法内置函数--------------------------------------------函数的嵌套调用:在调用一个函数的过程中,又调用了其他函数函数的嵌套定义:在一个函数的内部,又定义另外一个函数def max(x,y): if x>y: return x else: return ydef max1(a,b,c,d): res=max(a,b) res2=max(res,c) res3=ma

Python3.5:装饰器的使用

在Python里面函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数,简单来说函数也是变量也可以作文函数的参数 >>> def funA(): ... print('i an funA') ... time.sleep(1) >>> f = now >>> f() i am funA 函数对象有一个__name__属性,可以拿到函数的名字: >>> funA.__name__ 'funA' >>&

python第四天装饰器+迭代器+生成器

1.函数嵌套:在调用一个函数的过程中,调用了其他函数 def f1(): x=1 def f2(): print('from f2') f2() f1()  2.名称空间与作用域 a. 名称空间:存放名字与变量值绑定关系的地方 (1)内置名称空间:在python解释器启动时产生,存放一些python内置的名字 (2)全局名称空间:在执行文件时产生,存放文件级别定义的名字. (3)局部名称空间:在执行过程中,如果调用了该函数则会产生该函数的局部名称空间.在调用该函数的时候生效,调用结束时失效 加载

&lt;04day&gt;_函数嵌套--闭包函数--装饰器--迭代器--生成器

一.函数的嵌套定义 1.python函数支持嵌套 def f1(): #f1函数的定义 def f2(): #f2函数的定义 print('from f2') def f3(): #f3函数的定义 print('from f3') f2() f1() 嵌套函数--运行结果说明: 1首先调用f1()结果,f1函数为空.担保函f2函数,f2函数有内容打印并且有调用,f2函数包含f3函数,但f3函数无调用. 运行结果: 列子:多个数据之间的大小比较. #!/usr/bin/python # -*- c

day4装饰器-迭代器&amp;&amp;生成器

一.装饰器 定义:本质是函数,(装饰其他函数)就是为其它函数添加附加功能 原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 实现装饰器知识储备: 1.函数及“变量” 2.高阶函数 a.把一个函数名当做实参传给另一个函数(在不修改被装饰器函数源代码的情况下为其添加新功能) b.返回值中包含函数名 3.嵌套函数 高阶函数+嵌套函数 = 装饰器 延迟3秒 import time def test1(): time.sleep(3) print('in the test1')

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

python3 编码默认为unicode,unicode和utf-8都是默认支持中文的. 如果要python3的编码改为utf-8,则或者在一开始就声明全局使用utf-8 #_*_coding:utf-8_*_ 或者将字符串单独声明: a = "中文".encode("utf-8") 函数式编程 函数式编程中的函数指代的是数学中的函数. 函数式编程输入如果确定,则输出一定是确定的.函数中并没有很多逻辑运算 python是一个面向对象的语言,只是一部分支持函数式编程.

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

一.装饰器 1.什么是装饰器? 器=>工具,装饰=>增加功能 1.不修改源代码 2.不修改调用方式 装饰器是在遵循1和2原则的基础上为被装饰对象增加功能的工具 2.实现无参装饰器 1.无参装饰器的模板 def outter(func): def wrapper(*args,**kwargs): res=func(*args,**kwargs) return res return wrapper 2.使用:在被装饰对象正上方单独一行 @无参装饰器名 def foo(): pass 3.实现有参装