zoukankan      html  css  js  c++  java
  • python迭代器,生成器和装饰器

    生成器

    通过列表生成式,可以直接创建一个列表,因为内存限制,列表容量肯定是有限的,而且创建一个包含100W个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数占用的空间都白白浪费了。

    所以我们不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,成为生成器:generator

    创建list和generator的区别仅在于最外成的[]和()

    直接打印出list的每一个元素;通过next()函数或者__next()__获取generator的下一个返回值

    generator保存的是算法,每次调用next()就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

    也可以使用for循环,因为generator也是可迭代对象;

    g = (x * x for x in range(10))
    for n in g:
        print(n, end=",")   # 0,1,4,9,16,25,36,49,64,81,

    所以我们创建一个generator后,基本上永远不会调用next(),而是通过for循环来迭代,并且不需要关心StopIteration的错误

    通过使用yield关键字定义

    生成器对象通过yield关键字定义的函数对象,因此,生成器也是一个函数。生成器用于一个值的序列,以便在迭代器中使用。

    def myYield(n):
        while n > 0:
            print("开始生成...")
            yield n
            print("完成一次...")
            n -= 1
    if __name__ == '__main__':
        test = myYield(3)
        for i in test:
            print(i)
    """开始生成...
    3
    完成一次...
    开始生成...
    2
    完成一次...
    开始生成...
    1
    完成一次...
    """

    yield 语句是生成器中的关键语句,生成器在实例化时并不会被执行,而是等待调用其__next__()方法才开始运行。并且当程序运行完yield语句后就会“吼(hold)住”,即保持当前状态且停止运行,等待下一次遍历时才恢复运行。

    著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

    斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

    def fib(max):
        n, a, b = 0, 0, 1
        while n < max:
            print(b, end=";")
            a, b = b, a+b
            n += 1
    fib(10)  # 1;1;2;3;5;8;13;21;34;55;

    生成器的方式

    def fib(max):
        n, a, b = 0, 0, 1
        while n < max:
            yield b
            a, b = b, a+b
            n += 1
    a = fib(10)
    b = [i for i in a]
    print(b)   # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

    这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

    生成器使用实例

    #encoding:utf-8
    import time
    import random
    
    food = ["韭菜鸡蛋","猪肉白菜","猪肉荠菜","羊肉白菜","猪肉大葱","虾仁海鲜"]
    def consumer(name):
        print("%s 准备吃包子啦!" % name)
        while True:
            baozi = yield "n"
            print("[%s]馅包子来了,被[%s]吃了!" % (baozi, name))
    
    def producer(name):
        c1 = consumer('大儿子')
        c2 = consumer('小儿子')
        c1.__next__()
        c2.__next__()
        print("%s开始准备做包子啦" % name)
        for i in range(6):
            print("第%d次做了%s个包子" % (i + 1, len(food)))
            time.sleep(random.randint(1, 3))
            f1 = food[i]
            c1.send(f1)
            food.append(f1)
            # 对列表随机排序
            random.shuffle(food)
            c2.send(food[i])
    
    producer('老子')
    """
    大儿子 准备吃包子啦!
    小儿子 准备吃包子啦!
    老子开始准备做包子啦
    第1次做了6个包子
    [韭菜鸡蛋]馅包子来了,被[大儿子]吃了!
    [虾仁海鲜]馅包子来了,被[小儿子]吃了!
    第2次做了7个包子
    [猪肉大葱]馅包子来了,被[大儿子]吃了!
    [虾仁海鲜]馅包子来了,被[小儿子]吃了!
    第3次做了8个包子
    [韭菜鸡蛋]馅包子来了,被[大儿子]吃了!
    [韭菜鸡蛋]馅包子来了,被[小儿子]吃了!
    第4次做了9个包子
    [韭菜鸡蛋]馅包子来了,被[大儿子]吃了!
    [虾仁海鲜]馅包子来了,被[小儿子]吃了!
    第5次做了10个包子
    [猪肉大葱]馅包子来了,被[大儿子]吃了!
    [猪肉大葱]馅包子来了,被[小儿子]吃了!
    第6次做了11个包子
    [虾仁海鲜]馅包子来了,被[大儿子]吃了!
    [猪肉大葱]馅包子来了,被[小儿子]吃了!
    """

    迭代器

    可以直接作用于for循环的数据类型有以下几种

    1:集合数据类型,如list,tuple,dict,set,str等

    2:generator,包括生成器和带yield的generator frunction

    这些可以 直接作用于for循环的对象统称为可迭代对象:Iterable

    list,dict,str虽然是Iterable,却不是Iterator

    from collections import Iterable
    from collections import Iterator
    print(isinstance([],Iterator))  # False
    print(isinstance([],Iterable))  # True 
    print(isinstance({},Iterable))  # True
    print(isinstance('abc',Iterable))  # True

    iter()函数 创建迭代器

    iter(iterable)  #一个参数,要求参数为可迭代的类型

    把list、dict、str等Iterable变成Iterator可以使用iter()函数:

    from collections import Iterator
    print(isinstance(iter([]), Iterator))  # True
    print(isinstance(iter({}), Iterator))  # True
    print(isinstance(iter('abc'), Iterator))  # True

    Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

    Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

    小结

    凡是可作用于for循环的对象都是Iterable类型;

    凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

    集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象

    Python的for循环本质上就是通过不断调用next()函数实现的

    for x in [1, 2, 3, 4, 5]:
        print(x, end=",")
    print("
    ")
    it = iter([1, 2, 3, 4, 5])
    while True:
        try:
            x = next(it)
            print(x, end=",")
        except StopIteration:
            break

    创建一个迭代器(类)

    把一个类作为一个迭代器使用需要在类中实现两个方法__iter__()与__next__().

    __iter__()方法返回一个特殊的迭代器对象,这个迭代器对象实现__next__() 方法并通过StopIteration异常标识迭代的完成。

    from itertools import islice
    
    class Fib:
        def __init__(self):
            self.pre = 0
            self.curr = 1
        def __iter__(self):
            return self
        def __next__(self):
            self.pre, self.curr = self.curr, self.pre+self.curr
            return self.pre
    f = Fib()
    print(list(islice(f, 0, 10)))   # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

    内置迭代器工具

    count无限迭代器

    from itertools import count
    counter = count(start=10)
    print(next(counter))    # 10
    print(next(counter))   # 11

    cycle无限迭代器,从一个有限序列中生成无限序列

    from itertools import cycle
    counter = cycle([1, 2, 3])
    print(next(counter))  # 1
    print(next(counter))  # 2
    print(next(counter))  # 3
    print(next(counter))  # 1

    itertools的子模块islice控制无限迭代器输出的方式

    islice的第二个参数控制何时停止迭代,从无限的序列中生成有限序列

    from itertools import islice, count
    for i in islice(count(10), 5):
        print(i)

    装饰器

    函数即“”变量“”

    高阶函数

    把一个函数名当做实参传给另一个函数

     返回值中包含函数名

    高阶函数+嵌套函数=装饰器

    import time
    def timer(func):
        def deco(*args, **kwargs):
            start_time = time.time()
            func(*args, **kwargs)
            stop_time = time.time()
            print("the func run time is %s"%(stop_time - start_time))
        return deco
    
    @timer
    def test_1():
        time.sleep(1)
        print("in the test_1")
    
    @timer
    def test_2():
        time.sleep(1)
        print("in the test_2")
    
    test_1()
    test_2()
    """
    in the test_1
    the func run time is 1.0200583934783936
    in the test_2
    the func run time is 1.0000574588775635
    """

    类装饰器

    类装饰器具有灵活度大,高内聚,封装性等优点。使用类装饰器主要依靠类的__call__方法

    class Foo():
        def __init__(self, func):
            self._func = func
        def __call__(self, *args, **kwargs):
            print("class running")
            self._func()
            print("class end")
    @Foo
    def bar():
        print("bar")
    bar()

    装饰器可以把与业务逻辑无关的代码抽离出来,让代码保持干净清爽,而且装饰器还能被多个地方重复利用。比如一个爬虫网页的函数,如果该 URL 曾经被爬过就直接从缓存中获取,否则爬下来之后加入到缓存,防止后续重复爬取。

    import urllib.request as urlib
    def cache(func):
        saved = {}
        def wrapper(url):
            if url in saved:
                return saved[url]
            else:
                page = func(url)
                saved[url] = page
                return page
        return wrapper
    
    @cache
    def web(url):
        return urlib.urlopen(url).read()

    带参数的decorator

    import functools
    def log(text):
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                print("%s %s()" %(text, func.__name__))
                return func(*args, **kwargs)
            return wrapper
        return decorator
    
    @log("execute")
    def now():
        print("2015-3-25")
        
    now()
    print(now.__name__)

    实例-登录认证

    import functools
    
    user, passwd = "test", "123456"
    def auth(auth_type):
        def decotator(func):
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                if auth_type == "local":
                    username = input("请输入用户名:").strip()
                    password = input("请输入密码:").strip()
                    if user == username and passwd == password:
                        print("33[32;1mUser has passed authentication33[0m")
                        res = func(*args, **kwargs)
                        print("--after authentication--")
                        return res
                    else:
                        exit("33[31;1mInvalid username or password33[0m")
                elif auth_type == "ldap":
                    res = func(*args, **kwargs)
                    print("ldap都不会")
                    return res
            return wrapper
        return decotator
    def index():
        print("welcome to index page")
    @auth(auth_type="local")
    def home():
        print("welcome to home page")
    @auth(auth_type="ldap")
    def bbs():
        print("welcome to bbs page")
    index()
    home()
    bbs()

    转自: https://blog.csdn.net/sunchengquan/article/details/84494101

  • 相关阅读:
    开源分布式任务调度工具,和你一起记住生命中每一个重要的时刻
    寒假学习进度
    寒假学习进度
    寒假学习进度
    寒假学习进度
    js第三个阶段的面试题
    vue3保证你看懂watch和watchEffect的详细详细使用
    VMtool 安装与使用
    PHP 远程 debug
    一些有趣的编程名言
  • 原文地址:https://www.cnblogs.com/zhouzetian/p/14226617.html
Copyright © 2011-2022 走看看