共计 1507 个字符,预计需要花费 4 分钟才能阅读完成。
导读 | 今天给大家介绍的设计模式非常简单,叫做 iterator,也就是迭代器模式。迭代器是 Python 语言当中一个非常重要的内容,借助迭代器我们可以很方便地实现很多复杂的功能。在深度学习当中,数据的获取往往也是通过迭代器实现的。因此这部分的内容非常重要,推荐大家一定要掌握。 |
在开始介绍设计模式之前,我们先来看一个简单的需求。假设现在我们需要根据传入的变量获取每周的前几天,比如说我们传入 3 返回的就是 [Mon, Tue, Wed],我们传入 5 返回 [Mon, Tue, Wed, Thu, Fri]。这个需求大家应该都能理解,非常非常简单。
如果用一个函数来实现的话,就是这样:
def return_days(n):
week = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
return week[:n]
你看三行代码就实现了,在这个问题场景当中这样写当然是没有问题。但假如我们把题目稍微变一变,这里的 week 不是一个固定的数据,而是从上游或者是某个文件当中读取的。这里的 n 也是一个很大的数,我们把这个函数改写成这样:
def get_data(n):
data = []
for i in range(n):
data.append(get_from_upstream())
return data
我们假设 get_from_upstream 这个函数当中实现了获取数据的具体逻辑,那么上面这一段函数有一个什么问题?
有些同学会说这没有问题啊,因为像是其他语言实现数据获取的时候也都是这么干的。的确,像是 Java 等语言可能都是这么干的。但是其他语言这么干没错,不代表 Python 这么干也没错。因为我们没有把 Python 的能力发挥到最大。
这里有两个问题,第一个问题是延迟,因为前面说了,n 是一个很大的数。我们从上游获取数据,无论是通过网络还是文件读取,本质上都是 IO 操作,IO 操作的延迟是非常大的。那么我们把这 n 条数据全部搜集完可能需要很长的时间,导致下游的漫长等待。第二个问题就是内存,因为我们存储了这 n 条数据一起返回的,如果 n 很大,对于内存的开销压力也很大,如果机器内存不够很有可能导致崩溃。
那怎么解决呢?
其实解决的方法很简单,如果对迭代器熟悉的话,会发现迭代器针对的恰恰是这两个问题。我们把上面的逻辑改写成迭代器实现即可,这也就是 iterator 模式。
iterator 模式严格说起来其实只是迭代器的一种应用,它非常巧妙地将迭代器与匿名函数结合在一起,里面也没有太多的门道可以说,我们把刚才的代码改写一下,细节都在代码当中。
“`Python
def get_data(n):
for i in range(n):
yield get_from_upstream()
data_10 = lambda: get_data(10)
data_100 = lambda: get_data(100)
use
for d in data_10:
print(d)
““
很简单吧,但可能你要问了,我们既然写出了 get_data 这个迭代器,那么我们使用的时候直接 for d in get_data(10) 这样用不就好了,为什么中间要用匿名函数包一层呢?
道理也很简单,如果这个数据是我们自己使用,当然是没必要中间包一层的。但如果我们是传给下游使用的话,对于下游来说它肯定是不希望考虑上游太多的细节的,越简单越好。所以我们直接丢一个包装好的迭代器过去,下游直接 call 即可。否则的话,下游还需要感知 get_data 这个函数传入的参数,显然是不够合理的。