Python 类方法返回迭代器
我实现了一个迭代器类,如下所示:Python 类方法返回迭代器,python,iterator,Python,Iterator,我实现了一个迭代器类,如下所示: import numpy as np import time class Data: def __init__(self, filepath): # Computationaly expensive print("Computationally expensive") time.sleep(10) print("Done!") def __iter__(self):
import numpy as np
import time
class Data:
def __init__(self, filepath):
# Computationaly expensive
print("Computationally expensive")
time.sleep(10)
print("Done!")
def __iter__(self):
return self
def __next__(self):
return np.zeros((2,2)), np.zeros((2,2))
count = 0
for batch_x, batch_y in Data("hello.csv"):
print(batch_x, batch_y)
count = count + 1
if count > 5:
break
count = 0
for batch_x, batch_y in Data("hello.csv"):
print(batch_x, batch_y)
count = count + 1
if count > 5:
break
然而,构造函数的计算代价很高,for循环可能会被多次调用。例如,在上面的代码中,构造函数被调用两次(每个都用于创建一个新的数据对象)
如何分离构造函数和迭代器?我希望有以下代码,其中构造函数只调用一次:
data = Data(filepath)
for batch_x, batch_y in data.get_iterator():
print(batch_x, batch_y)
for batch_x, batch_y in data.get_iterator():
print(batch_x, batch_y)
您可以直接迭代iterable对象,
for..in
不需要任何其他内容:
data = Data(filepath)
for batch_x, batch_y in data:
print(batch_x, batch_y)
for batch_x, batch_y in data:
print(batch_x, batch_y)
这就是说,这可能是有缺陷的,具体取决于您如何实现\uuuuuuuuuiter()
例如:
坏的
因为这样你就不能在同一个对象上迭代两次,因为self.\u i
仍然会指向循环的末尾
好的
这将在每次迭代时重置索引,修复上述问题。如果在同一个对象上嵌套迭代,那么这将不起作用
更好
要解决此问题,请将迭代状态保留在单独的迭代器对象中:
class Data:
class Iter:
def __init__(self, data):
self._data = data
self._i = 0
def __next__(self):
if self._i >= len(self._data._items): # check for available data
raise StopIteration
result = self._data._items[self._i]
self._i = self._i + 1
def __init__(self, filepath):
self._items = load_items(filepath)
def __iter__(self):
return self.Iter(self)
这是最灵活的方法,但如果您可以使用以下任一方法,那么它就不必要地冗长
简单,使用yield
如果您使用Python的生成器,该语言将负责跟踪迭代状态,即使在嵌套循环时,它也应该正确地跟踪迭代状态:
class Data:
def __init__(self, filepath):
self._items= load_items(filepath)
def __iter__(self):
for it in self._items: # Or whatever is appropriate
yield return it
简单,传递到底层iterable
如果“计算代价高昂”的部分是将所有数据加载到内存中,那么您可以直接使用缓存的数据
class Data:
def __init__(self, filepath):
self._items = load_items(filepath)
def __iter__(self):
return iter(self._items)
与其创建
数据的新实例
,不如创建第二个类IterData
,该类包含一个\uuuuu init\uuuuu
方法,该方法运行的进程在计算上没有实例化数据
那么昂贵。然后,在Data
中创建classmethod
,作为IterData
的替代构造函数:
class IterData:
def __init__(self, filepath):
#only pass the necessary data
def __iter__(self):
#implement iter here
class Data:
def __init__(self, filepath):
# Computationaly expensive
@classmethod
def new_iter(cls, filepath):
return IterData(filepath)
results = Data.new_iter('path')
for batch_x, batch_y in results:
pass
我是说。。。上面的代码有什么问题?构造和迭代已经是分开的。只需重用通过调用
Data()
创建的对象,而不是创建一个新对象。您是否有一个当前代码的示例,其中构造函数被调用了两次,但您不想这样做?我更新了这个问题。在第一个版本中,构造函数被调用了两次。不明白为什么data.get\u iterator()
。如果你只为数据中的batch_x,batch_y编写,那有什么错?@TomášCerha:它调用构造函数\uu_init_uuu
,这在计算上是昂贵的。我添加了一个带有大量变体的答案,哪一个是“最好的”真的取决于你案例的具体情况-哪一部分是“计算上昂贵的”,之后的数据是否全部在列表或内存中的其他可编辑对象中可用,或者您是否仍在按需获取数据\
class Data:
def __init__(self, filepath):
self._items = load_items(filepath)
def __iter__(self):
return iter(self._items)
class IterData:
def __init__(self, filepath):
#only pass the necessary data
def __iter__(self):
#implement iter here
class Data:
def __init__(self, filepath):
# Computationaly expensive
@classmethod
def new_iter(cls, filepath):
return IterData(filepath)
results = Data.new_iter('path')
for batch_x, batch_y in results:
pass