今天在研究pytorch的Dataset的时候,查看了下源码,如下:
class Dataset(object):
"""An abstract class representing a Dataset.
All other datasets should subclass it. All subclasses should override
``__len__``, that provides the size of the dataset, and ``__getitem__``,
supporting integer indexing in range from 0 to len(self) exclusive.
"""
def __getitem__(self, index):
raise NotImplementedError
def __len__(self):
raise NotImplementedError
def __add__(self, other):
return ConcatDataset([self, other])
突然有一个疑问,pytorch中说Dataset是一个迭代器,但是迭代器不是要实现__iter__和__next__方法吗?为啥Dataset没有呢?并且,如下操作并是正确的。
train_loader = BaseDatalayer(cfg) # BaseDatalayer继承于Dataset
for data in train_loader:
print(data)
进一步探究发现,发现 __iter__ 和 __getitem__ 中实现任意一个,对象就是可迭代的,怎么解释这种情况呢,Python 解释器中有这样的描述:
解释器需要迭代对象 x 时, 会自动调用 iter(x)。 内置的 iter 函数有以下作用:
(1) 检查对象是否实现了 __iter__ 方法, 如果实现了就调用它, 获取 一个迭代器;
(2) 如果没有实现 __iter__ 方法, 但是实现了 __getitem__ 方法, Python 会创建一个迭代器, 尝试按顺序(从索引 0 开始) 获取元素;
(3) 如果尝试失败, Python 抛出 TypeError 异常, 通常会提示“C object is not iterable”(C 对象不可迭代), 其中 C 是目标对象所属的类。任何 Python 序列都可迭代的原因是, 它们都实现了 __getitem__ 函数。 其实, 标准的序列都实现了 __iter__ 函数, 因此你也应该这么做。
总结
因此我们在自定义可迭代对象需要实现 __iter__ 函数;严格来讲,我们还需要实现 __getitem__ 函数,这个函数的主要作用是当前对象可通过下标取值。
另外,如果有一个未知的对象,我们怎么判断该对象是否可迭代呢,两种方式。
(1):iter(x) :这是最准确的判断方式,传入要判断的对象,只要对象实现了__getitem__ 、 __iter__中的其中一个,该对象都是可迭代的,否则抛出TypeError: 'C' object is not iterable 异常;这儿有个区别,如果仅仅实现了 __getitem__ ,调用 iter(x) 返回的是迭代器对象,如果实现的是 __iter__ ,那么调用 iter(x) 返回的是生成器对象;
(2): isinstance(x, abc.Iterable) :该方式会自动忽略 __getitem__ 函数,仅当对象实现了 __iter__ 函数才返回 True,其他一律返回 False。
文章参考:
Python中自定义可迭代对象www.jianshu.com