Python秘技之迭代器,生成器和装饰器

Python秘技之迭代器,生成器和装饰器_第1张图片

python编程利器

  • 一、迭代器
    • 1、迭代
    • 2、可迭代对象
    • 3、迭代器
    • 4、自定义一个迭代器
  • 二、生成器
    • 1、生成器
    • 2、创建一个生成器
    • 3、深入生成器
  • 三、装饰器
    • 1、函数里面定义函数
    • 2、函数里面返回函数
    • 3、将函数作为参数传给另⼀个函数
    • 4、装饰器概述
  • 四、学习体会
      • 加油!!!
        • 你可以的!你总是这样相信着自己!

  迭代器、生成器与装饰器都是python中的非常常用的语法形式。但是初学的时候还是有点不好理解,但是还必须要掌握,这里试图简单阐述出它们的原理。
作用:

迭代器和生成器的作用简单点说就是可以节约大量的内存
1、迭代器是一种可以连续迭代的一个容器,
2、生成器是函数中包含yield语句的一类特殊的函数。
3、装饰器的作用就是为一个对象或者函数添加相关的功能,在程序设计中,可以灵活地设计出所需要的功能。

一、迭代器

1、迭代

  Iteration is the repetition of a process in order to generate a (possibly unbounded) sequence of outcomes. The sequence will approach some end point or end value. Each repetition of the process is a single iteration, and the outcome of each iteration is then the starting point of the next iteration.
  迭代是重复反馈过程的活动,其目的通常是为了接近并到达所需的目标或结果。每一次对过程的重复被称为一次“迭代”,而每一次迭代得到的结果会被用来作为下一次迭代的初始值。
  ⽤简单的话讲,它就是从某个地⽅(⽐如⼀个列表)取出⼀个元素的过程。当我们使⽤⼀ 个循环来遍历某个东西时,这个过程本⾝就叫迭代。

2、可迭代对象

  Python中任意的对象,只要它定义了可以返回⼀个迭代器的__iter__⽅法,或者定义了 可以⽀持下标索引的__getitem__⽅法, 那么它就是⼀个可迭代对象。简单说,可迭代对象就是能提供迭代器的任意对象。

3、迭代器

简单来说,迭代器就是实现了迭代器协议方法的对象或类。迭代器的协议方法主要是两个:
1、__iter__():返回对象本身,它是for语句使用迭代器的要求.
2、__next__():返回容器中下一个元素或数据。当容器中数据用尽时,应该引发StopIteration异常。
总之、任何类实现这两个方法,就可以称为一个迭代器了。同时,也就可以使用for循环来进行遍历了。

4、自定义一个迭代器

class MyIterator:
    '''自定义类迭代器MyIterator'''
    def __init__(self,x=2,xmax=1000):  # 定义构造方法,初始化实例属性
        self.__mul = x
        self.__x = x
        self.__xmax = xmax

    def __iter__(self):  # 定义迭代器协议方法,返回类自身
        return self

    def __next__(self):  # 定义迭代器协议方法  
        if self.__x and self.__x != 1:
            self.__mul *= self.__x
            if self.__mul <= self.__xmax:
                return self.__mul  # 返回值
            else:
                raise StopIteration  # 抛出StopIteration错误
        else:
            raise StopIteration

if __name__ == '__main__':
    myiter = MyIterator()  # 实例化迭代器
    for i in myiter:  # 遍历并输出值
        print('迭代的数据元素为{}'.format(i))

可以看出,初始化迭代器使用了默认参数,遍历得到的序列是2的n次方的值,最大值不超过100。
注:从代码可以看出,如果需要产生很大范围的数的序列,采用列表或者元组等进行一次性生成,则必须占据大量的内存空间,对系统有着较大的压力,使用迭代器,每次调用时生成一个,显然节约了大量的内存空间。
来做个测试
我用for循环写了一个判断2的n次方的代码,范围是(2,100000000),我的辣鸡电脑跑出来的时间是21.21182918548584 s,用刚刚写的迭代器代码跑出来的时间仅仅为0.0s(保留20位小数仍然是0)。当测试数据开到1000000000000000000000的时候,迭代时间仍然是0,循环已经废掉了。能由此可见,效率问题,值得注意。

二、生成器

1、生成器

  ⽣成器也是⼀种迭代器,但是你只能对其迭代⼀次。这是因为它们并没有把所有的值存在 内存中,⽽是在运⾏时⽣成值。你通过遍历来使⽤它们,要么⽤⼀个“for”循环,要么将它们传递给任意可以进⾏迭代的函数和结构。⼤多数时候⽣成器是以函数来实现的。

2、创建一个生成器

def myYield(n):
    while n > 0:
        print('开始生成...')
        yield n  # yield语句,用于返回给调用者其后表达式的值
        print('完成一次...')
        n -= 1
if __name__ == '__main__':
    for i in myYield(4):
        print('遍历得到的值:',i)
结果:

开始生成...
遍历得到的值: 4
完成一次...
开始生成...
遍历得到的值: 3
完成一次...
开始生成...
遍历得到的值: 2
完成一次...
开始生成...
遍历得到的值: 1
完成一次...

当然,也可以使用手工遍历,调用__next__()方法。

my_yield = myYield(3)
a = my_yield.__next__()
print(a)

3、深入生成器

  1. yield语句是生成器中的关键语句。生成器在实例化之后不会立即执行,而是等待调用__next__()方法才开始执行。并且当程序运行完之后yield就会“吼(hold)住”,即保持其当前状态并停止运行,等待下一次遍历时才恢复执行。
  2. yield语句不仅可以使函数成为生成器和返回值,还可以接受调用者传来的数值。但值得注意的是:第一次调用生成器时不能传给生成器None以外的值,否则会引发错误。

防止难理解,写个代码测试一下:

def myYield(n):
    while n > 0:
        rcv = yield n
        n -= 1
        if rcv is not None:
            n = rcv

if __name__ == '__main__':
    my_yield = myYield(3)
    print(my_yield.__next__())
    print(my_yield.__next__())
    print('传给生成器一个值,重新初始化生成器')
    print(my_yield.send(10))
    print(my_yield.__next__())

说明:代码中首先定义了一个生成器函数,其中yield语句为“rcv = yield n”。rcv就可以接收调用者传来的值。如果调用时,提供了一个值,就会从这个值开始递减产生序列。如下图:
Python秘技之迭代器,生成器和装饰器_第2张图片

  1. 事实上上述代码中使用的send()方法来重置生成器的生成序列,其实也称之为协程。类似于线程或者进程,也是一种解决程序并发的方法。关于协程,其实要是详细说的话,还会引出之后的greenlet和gevent甚至于epoll。会多出相当多的一部分内容。所以在这里不具体讨论协程这个概念了,以后找个时间专门写下这些吧。

三、装饰器

装饰器说简单点就是一种增加函数或者类的功能的方法,可以快速插入相同的功能。本质上来讲,它是一种代码的实现方式。

1、函数里面定义函数

其实函数里面定义函数不是什么稀奇的东西,写个代码测试一下:

def hi():
    print("now you are inside the hi() function")

    def greet():
        return "now you are in the greet() function"

    def welcome():
        return "now you are in the welcome() function"

    print(greet())
    print(welcome())
    print("now you are back in the hi() function")


hi()

很容易是不是,那现在我们知道了可以在函数中定义另外的函数。也就是说:我们可以创建嵌套的函数。 但是你需要再多学⼀点,就是函数也能返回函数。

2、函数里面返回函数

其实并不需要在⼀个函数⾥去执⾏另⼀个函数,我们也可以将其作为输出返回出来:

def hi(name='hello'):

    def greet():
        return "now you are in the greet() function"

    def welcome():
        return "now you are in the welcome() function"

    if name == "hello":
        return greet
    else:
        return welcome
    print(greet())
    print(welcome())
    print("now you are back in the hi() function")


a = hi()
print(a)
# .greet at 0x00000286AB4B29D8>

#上⾯清晰地展示了`a`现在指向到hi()函数中的greet()函数 
#现在试试这个
print(a())
# now you are in the greet() function

why?
当你把⼀对⼩括号放在后⾯,这个函数就会执⾏; 然⽽如果你不放括号在它后⾯,那它可以被到处传递,并且可以赋值给别的变量⽽不去执⾏它。

3、将函数作为参数传给另⼀个函数

def hi():
    return "hi python!"


def test(func):
    print("I am doing some boring work before executing hi()")
    print(func())


test(hi)
结果:
I am doing some boring work before executing hi()
hi python!

现在你已经具备所有必需知识,来进⼀步学习装饰器真正是什么了。装饰器让你在⼀个函 数的前后去执⾏代码。

4、装饰器概述

装饰器的表示语法是使用一个特殊的符号“@”来实现的。装饰器装饰函数或者类就用“@装饰器名称”放在函数或者类的定义行之前即可。

注:我个人觉得如果概念不是很容易懂那就写写代码看一下,总会有收获的
def abc(fun):
    def wrapper(*args, **kwargs):
        print('开始运行。。。。。。')
        fun(*args, **kwargs)
        print('运行结束!')
    return wrapper


@abc
def demo_decoration(x):
    a = []
    for i in range(x):
        a.append(i)
    print(a)

@abc
def hello(name):
    print('Hello', name)

if __name__ == '__main__':
    demo_decoration(5)
    print()
    hello('Jane')

上面的代码首先定义了一个装饰器函数(abc),它带有一个可以使用函数对象的参数。然后用它装饰了两个普通函数。最后就是进行函数的调用,你会发现,并没有调用abc,但是会使用这个函数的功能。
简单的看,这就是装饰器了,我们可以看一下它的运行结果:
Python秘技之迭代器,生成器和装饰器_第3张图片
当然,装饰器还可以用来装饰一个类,不过这里不过多阐述,感兴趣就去网上找个例子学习一下。如果想要更深入的了解装饰器,还可以去查阅一下闭包的概念,这样肯定会加深对装饰器的理解。

参考书目:

  1. Introducing Python by Bill Lubanovic(O’Reilly). Copyright 2015 Bill Lubanovic, 978-1-449-35936-2.”
  2. Intermediate Python by Muhammad Yasoob Ullah Khalid.

四、学习体会

  python的迭代器,生成器以及装饰器都是比较重要的概念,同时也都有很丰富的应用场景。不过刚开始接触python的时候,不是特别容易理解。做个简单的了解就行,用到的时候再去具体学习,千万不要被搞没积极性了!!!!!!
想学习python或者正在转行python相关的行业的朋友们可以进qq群798665505,群里有许多对python感兴趣的朋友,里面也有很多有趣的灵魂。同样你可以获得很多资料,大家一起学习。一起进步,身体和灵魂,总要有一个在路上。

加油!!!

你可以的!你总是这样相信着自己!

你可能感兴趣的:(Python)