python迭代器、生成器與裝飾器

可迭代對(duì)象與迭代器

可迭代對(duì)象

可以直接作用于for循環(huán)的對(duì)象統(tǒng)稱為可迭代對(duì)象,代碼中可以通過(guò)isinstance()判斷一個(gè)對(duì)象是否是Iterable對(duì)象

Example:

>>> from collections import Iterable
>>> isinstance([],Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('test', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(1, Iterable)
False

迭代器

可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)值的對(duì)象稱為迭代器,代碼中可以使用isinstance()判斷一個(gè)對(duì)象是否是Iterator對(duì)象

Example:

>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False

可以看到,生成器都是Iterator對(duì)象,但list、dict、str雖然是Iterable,卻不是Iterator。

迭代器與列表

aList = ['a', 'b']
aIter = iter(aList)

while True:
    try:
        print aIter.next()
    except StopIteration:
        print 'Done'
        break

迭代器與字典

aDict = {'a': 1, 'b': 2}

for key, value in aDict.iteritems():
    print 'key: %s, value: %s' % (key, value)

for循環(huán)原理

for x in [1, 2, 3, 4, 5]:
    pass

實(shí)際上完全等價(jià)于:

# 首先獲得Iterator對(duì)象:
it = iter([1, 2, 3, 4, 5])
# 循環(huán):
while True:
    try:
        # 獲得下一個(gè)值:
        x = next(it)
    except StopIteration:
        # 遇到StopIteration就退出循環(huán)
        break

自定義迭代器

根據(jù)上面的for循環(huán)原理,我們可以自定義迭代器,只需要實(shí)現(xiàn)iter,next方法

class MyRange(object):
    def __init__(self, end):
        self.curr = 0
        self.end = end

    def __iter__(self):
        return self

    def next(self):
        if self.curr < self.end:
            val = self.curr
            self.curr += 1
            return val
        else:
            raise StopIteration()

生成器

帶有 yield 關(guān)鍵字的的函數(shù)在 Python 中被稱之為 generator(生成器)

Example:

def test():
    for i in range(5):
        yield i


a = test()
print a.next()
print a.next()
print a.next()
print a.next()
print a.next()
print a.next()

輸出結(jié)果:

0
1
2
3
4
Traceback (most recent call last):
  File "test8.py", line 12, in <module>
    print a.next()
StopIteration

代碼中a = test()時(shí),會(huì)返回一個(gè)generator對(duì)象,它保存的是算法,在我們調(diào)用a.next(),會(huì)執(zhí)行至yield語(yǔ)句并返回,再次執(zhí)行時(shí)從上次返回的yield語(yǔ)句處繼續(xù)執(zhí)行.

在nova加載extentions時(shí)的代碼如下:

    def sorted_extensions(self):
        if self.sorted_ext_list is None:
            self.sorted_ext_list = sorted(self.extensions.iteritems())

        for _alias, ext in self.sorted_ext_list:
            yield ext
           

get_resource方法中調(diào)用了此函數(shù)

    for ext in self.sorted_extensions():
        resources.extend(ext.get_resources())

這樣做的就是好處就是extensions 對(duì)象不會(huì)占用大量的內(nèi)存,它們只會(huì)要調(diào)用的時(shí)候在內(nèi)存里生成

斐波拉契數(shù)列的實(shí)現(xiàn)

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

裝飾器

裝飾器實(shí)際上就是一個(gè)函數(shù), 而且是一個(gè)接收函數(shù)對(duì)象的函數(shù). 有了裝飾器我們可以在執(zhí)行被裝飾函數(shù)之前做一個(gè)預(yù)處理, 也可以在處理完函數(shù)之后做清除工作.

一個(gè)直觀的例子:

def hello(fn):
    def wrapper():
        print "hello, %s" % fn.__name__
        fn()
        print "goodby, %s" % fn.__name__
    return wrapper
    
@hello
def foo():
    print "i am foo"
 
foo()

輸出如下:

?  python python hello.py                                                                                                                      
hello, foo
i am foo
goodby, foo

你可以看到如下的東西:

1)函數(shù)foo前面有個(gè)@hello的“注解”,hello就是我們前面定義的函數(shù)hello

2)在hello函數(shù)中,其需要一個(gè)fn的參數(shù)(這就用來(lái)做回調(diào)的函數(shù))

3)hello函數(shù)中返回了一個(gè)inner函數(shù)wrapper,這個(gè)wrapper函數(shù)回調(diào)了傳進(jìn)來(lái)的fn,并在回調(diào)前后加了兩條語(yǔ)句。

對(duì)于Python的這個(gè)@注解語(yǔ)法糖- Syntactic Sugar 來(lái)說(shuō),這個(gè)例子實(shí)際上被解釋成了:

foo = hello(foo)

它的執(zhí)行過(guò)程相當(dāng)于

# 理解函數(shù)其實(shí)是一個(gè)對(duì)象的概念
fn = foo
print "hello, %s" % fn.__name__
fn()
print "goodby, %s" % fn.__name__

當(dāng)有多個(gè)decorator或是帶參數(shù)的decorator時(shí),如:

@decorator_one
@decorator_two
def func():
    pass

相當(dāng)于

func = decorator_one(decorator_two(func))

文本轉(zhuǎn)換的例子:

def makeHtmlTag(tag, *args, **kwds):
    def real_decorator(fn):
        css_class = " class='{0}'".format(kwds["css_class"]) \
                                     if "css_class" in kwds else ""
        def wrapped(*args, **kwds):
            return "<"+tag+css_class+">" + fn(*args, **kwds) + "</"+tag+">"
        return wrapped
    return real_decorator
 
@makeHtmlTag(tag="b", css_class="bold_css")
@makeHtmlTag(tag="i", css_class="italic_css")
def hello():
    return "hello world"
 
print hello()
 
# 輸出:
# <b class='bold_css'><i class='italic_css'>hello world</i></b>

下面,我們來(lái)看看用類的方式來(lái)重寫上面的html.py的代碼:

class makeHtmlTagClass(object):
 
    def __init__(self, tag, css_class=""):
        self._tag = tag
        self._css_class = " class='{0}'".format(css_class) \
                                       if css_class !="" else ""
 
    def __call__(self, fn):
        def wrapped(*args, **kwargs):
            return "<" + self._tag + self._css_class+">"  \
                       + fn(*args, **kwargs) + "</" + self._tag + ">"
        return wrapped
 
@makeHtmlTagClass(tag="b", css_class="bold_css")
@makeHtmlTagClass(tag="i", css_class="italic_css")
def hello(name):
    return "Hello, {}".format(name)
 
print hello("Hao Chen")

上面這段代碼中,我們需要注意這幾點(diǎn):
1)如果decorator有參數(shù)的話,init() 成員就不能傳入fn了,而fn是在call的時(shí)候傳入的。
2)這段代碼還展示了 wrapped(*args, **kwargs) 這種方式來(lái)傳遞被decorator函數(shù)的參數(shù)。

functools的wraps

def hello(fn):
    def wrapper():
        print "hello, %s" % fn.__name__
        fn()
        print "goodby, %s" % fn.__name__
    return wrapper

@hello
def foo():
    print "i am foo"

foo()
print foo.__name__

來(lái)看看這段代碼的輸出:

?  python python hello.py                                                                                                                     
hello, foo
i am foo
goodby, foo
wrapper

會(huì)發(fā)現(xiàn)其輸出的是“wrapper”,而不是我們期望的“foo”,因?yàn)槲覀兦懊嫣岬竭^(guò),這樣的裝飾器相當(dāng)于foo = hello(foo), 而hello函數(shù)中返回值是wrapper, 所以foo.__name__ 自然是wrapper。所以,Python的functool包中提供了一個(gè)叫wrap的decorator來(lái)消除這樣的副作用。下面是我們新版本的hello.py。

from functools import wraps
def hello(fn):
    @wraps(fn)
    def wrapper():
        print "hello, %s" % fn.__name__
        fn()
        print "goodby, %s" % fn.__name__
    return wrapper
 
@hello
def foo():
    print "i am foo"
 
foo()
print foo.__name__

參考

Jmilk's blog
廖雪峰的官方網(wǎng)站
酷 殼 – COOLSHELL

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 Python的修飾器的英文名叫Decorator,當(dāng)你看到這個(gè)英文名的時(shí)候,你可能會(huì)把其跟Design Pa...
    linheimx閱讀 656評(píng)論 0 4
  • //Clojure入門教程: Clojure – Functional Programming for the J...
    葡萄喃喃囈語(yǔ)閱讀 4,036評(píng)論 0 7
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評(píng)論 19 139
  • 一.我的夢(mèng)想 “為師問(wèn)你你將來(lái)要做個(gè)什么樣的人?。渴亲鰝€(gè)俠客?” “不……不!我立志要做個(gè)采花賊!” “你……確定...
    仲夏章閱讀 1,552評(píng)論 0 0

友情鏈接更多精彩內(nèi)容