繁体   English   中英

关于python迭代器的困惑

[英]Confusions about python Iterators

我练习迭代器的次数越多,我就越会感到困惑。 我对对象和类很有信心(只有我们学到的知识,而没有学到继承),但是迭代器和生成器使我很困惑。 非常感谢您的帮助。

我有一些问题:

1)在以下代码中:

class main():
    def __init__(self):
        self.items=[1,2,3,4,5,6,7]
        self.index= 0

    def __iter__(self):
        return self 

    def __next__(self):
        self.index+=1

        return self.items[self.index]

a = main()

for i in a:
    print(i)  
  1. 我们在这里有两个自我。 一个在init中,它指向对象“ a”,而另一个则由self返回。 自我的真实数据类型是什么? 是main()类型还是迭代器?
  2. 与上述问题类似-当我们给予next (自我)时,我们给予next(iterator或type(a)类型)的自我是什么?
  3. 如果由__iter__返回的self之后(也由next使用)为iterator类型,那么如何访问self.index

2)在下面的代码中,我试图遍历特定的事物,例如键或值或字典类中的项。 它抛出错误“迭代器”对象没有属性“索引”。 为什么self.index无法访问字典类的实例变量索引?

class Pair():
    def __init__(self, key ,value):
        self.key = key
        self.value = value

class Dictionary():
    def __init__(self):
        self.items =[]
        self.index = -1     ################## INDEX DEFINED HERE

    def __setitem__(self, key, value):
        for i in self.items:
            if i.key == key:
                i.value = value
                return
        self.items.append(Pair(key,value))

    def __keys__(self):
        return iterator(self, 'keys')

    def __values__(self):
        return iterator(self, 'values')

    def __items__(self):
        return iterator(self , 'items')

class iterator():
    def __init__(self, object, typo):
        self.typo = typo

    def __iter__(self):
        return self

    def __next__(self):
        if self.typo == 'keys': 
            self.index +=1  #################### ERROR
            if self.index >= len(self.items):
                raise StopIteration
            return self.items[self.index].keys

        ` # Similarly for keys and items as well`

collins = Dictionary()

collins['google'] = 'pixel'
collins['htc'] = 'M8'
collins['samsung'] = 'S9'


for i in collins.__keys__():
    print(i)

我用很多注释重写了您的代码,以尝试解释示例(1)中发生的情况。

class MainClass():
    def __init__(self):
        # The value 'self' always refers to the object we are currently working
        # on. In this case, we are instantiating a new object of class
        # MainClass, so self refers to that new object.
        # self.items is an instance variable called items within the object
        # referred to as self.
        self.items = [1, 2, 3, 4, 5, 6, 7]
        # We do not want to declare self.index here. This is a slightly subtle
        # point. If we declare index here, then it will only be set when we first
        # create an object of class MainClass. We actually want self.index to be
        # set to zero each time we iterate over the object, so we should set it
        # to zero in the __iter__(self) method.
        # self.index = 0

    def __iter__(self):
        # This is an instance method, which operates on the current instance of
        # MainClass (an object of class MainClass). This method is called when
        # we start iteration on an object, so as stated above, we'll set
        # self.index to zero.
        self.index = 0
        return self

    def __next__(self):
        # This is also an instance method, which operates on the current
        # instance of MainClass.
        if self.index < len(self.items):
            self.index += 1
            return self.items[self.index - 1]
        else:
            # This is how we know when to stop iterating.
            raise StopIteration()


a = MainClass()

# a is now an object of class MainClass
# Because we have implemented __iter__ and __next__ methods in MainClass,
# objects of class MainClass are iterable, so a is also iterable.

# When we say "for i in a" this is like shorthand for  saying "a.__iter__()"
# and then "i = a.__next__()" until we raise
# a StopIterationException

# Here we are iterating over the result of a.__iter__() until a.__next__()
# raises a StopIterationException
for i in a:
    # Here we are printing the value returned by a.__next__()
    print(i)

我认为这可能有助于您在继续进行(2)并仔细检查有关对象和类的知识之前先复习一下。 我们可以在(2)中看到的第一个问题是,您将一个object传递给了iterator类,但是没有将其存储在任何地方,因此您以后无法访问它。 但是,当您更全面地了解(1)中的要求时,您可能会发现还有其他更改方式。

这只会回答您的第一个问题,并可能会帮助您解决问题2。

引用“ Fluent Python”(第420页):

[...]实现__iter__方法并返回迭代器的对象是可迭代的。 [...]

这意味着,(理论上)您可以执行以下操作:

class Main:
    def __init__(self):
        self.items = list(range(1, 8))
        self.length = len(self.items)

    def __iter__(self):
        return MainIterator(self)

现在,但是MainIterator类的外观如何? 迭代器只需要一个__next__ dunder方法即可确定它返回的下一个值。 一个实现可能看起来像这样:

class MainIterator:
    def __init__(self, iterable):
        self.iterable = iterable
        self.index = 0

    def __next__(self):
        if self.index >= self.iterable.length:
            raise StopIteration

        self.index += 1
        return self.iterable.items[self.index - 1]

我基本上要做的是创建对可迭代调用的引用,并将其保存在self.iterable 现在,每次调用__next__ dunder方法时,它都会返回数组的元素,直到迭代器耗尽为止。 这通过提高StopIteration来指示。

您不会经常看到这样的实现,因为这两个类经常合并为一个类。 我只是想证明可以将两者分开。 结果是@rbricheno已经发布的内容:

class Main:
    def __init__(self):
        self.items = list(range(1, 8))
        self.length = len(self.items)

    def __iter__(self):
        self.index = 0
        return self

    def __next__(self):
        if self.index >= self.length:
            raise StopIteration

        self.index += 1
        return self.items[self.index - 1]

区别在于__init__返回实例本身,因为该类本身现在是可迭代的并且是迭代器(请记住:迭代器具有__next__ dunder方法,而可迭代的具有__iter__ dunder方法可返回迭代器)。

最后一个有趣的地方是,当调用这些dunder方法时。 实际上,在使用for in语法时,它是以下语法糖:

a = Main()

## recreating the for in loop

itr = a.__iter__()

while True:
    try:
        print(itr.__next__())
    except StopIteration:
        break

您首先初始化迭代器,然后__next__返回一个值,直到迭代器用尽。

编辑:

您真的应该再次阅读我的帖子。 分离迭代器不是一个好习惯。 只是为了演示它们在内部如何工作。 另外,请不要定义自己的dunder方法。 那会在某些时候破坏您的代码。 我已经在下面更正了您的dict类,但是我遍历了这对,而不是它的组件。

class Pair:

    def __init__(self, key, value):
        self.key = key
        self.value = value

    ## you need this to display your class in a meaningful way
    def __repr__(self):
        return f'{__class__.__name__}({self.key}, {self.value})'

class Dictionary:

    def __init__(self):
        self.items = []
        self.length = len(self.items)

    def add(self, objects):
        self.items.append(objects)
        self.length += 1

    def __iter__(self):
        self.index = 0
        return self

    def __next__(self):
        if self.index >= self.length:
            raise StopIteration

        self.index += 1
        return self.items[self.index - 1]

a = Dictionary()

a.add(Pair('up', 'above'))
a.add(Pair('down', 'below'))

for i in a:
    print(i.key)
    print(i.value)

我的机器上的输出:

up
above
down
below

那就是我想出的:

class Pair():
    def __init__(self, key, value):
        self.key = key
        self.value = value


class dictionary():
    def __init__(self):
        self.items = []

    def add(self, objects):
        self.items.append(objects)

    def __keys__(self):
        return iterator(self, 'keys')

    def __values__(self):
        return iterator(self, 'values')

class iterator():
    def __init__(self, to_be_iterated , over_what):
        self.to_be_iterated = to_be_iterated
        self.over_what = over_what


    def __iter__(self):
        self.index = -1
        return self

    def __next__(self):
        self.index += 1
        if self.over_what == 'keys':
            try:
                    return self.to_be_iterated.items[self.index].key
            except Exception:
                raise StopIteration

        elif self.over_what == 'values':
            try:
                    return self.to_be_iterated.items[self.index].value
            except Exception:
                raise StopIteration


collins = dictionary()

collins.add(Pair('up', 'above'))
collins.add(Pair('down', 'below'))

for i in collins.__keys__():
    print(i)

for i in collins.__values__():
    print(i)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM