【问题标题】:Explanation on python __iter__ and next() printing outputspython __iter__ 和 next() 打印输出的解释
【发布时间】:2014-08-04 12:04:41
【问题描述】:

我从 python 2.7.8 的官方文档中了解到如何使用迭代器和生成器。我有一个关于好奇心的问题。

it = iter("abcde")
print it
>>> <iterator object at 0x7ff4c2b3bad0>

class example1():
    def __init__(self, word):
        self.word = word
        self.index = len(word)
    def __iter__(self):
        for x in range(self.index - 1, -1, -1):
            yield self.word[x]

a = example1("altalena")
print iter(a)
>>> <generator object __iter__ at 0x7f24712000a0>

在上面的示例中,当我打印迭代器时,我读取“生成器”、“迭代器”对象和十六进制 ID。为什么我不能用下面的代码做同样的事情?

class example2():
    def __init__(self, word):
        self.word = word
        self.index = len(word)
    def __iter__(self):
        return self
    def next (self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.word[self.index]
a = example2()
print iter(a)
>>> <__main__.example2 instance at 0x7f89ee2de440>

我认为这是由 iter 中的“return self”引起的,导致类实例,但我不知道获得更正确输出的解决方案。它可能没用,但我不知道为什么要避免它。

【问题讨论】:

  • 如果你想改变print对你的实例所做的事情,你必须使用__repr__和/或__str__
  • 拜托,以后尽量尊重PEP8
  • 所以我没有犯错,对吧?我只是创建我喜欢的行为,以获得具有正确样式的返回字符串。我已经做了几次以解决与 im_data 和 im_self 相关的其他问题......但是我怎样才能获得迭代器的内存 ID 而不是类实例的内存 ID?
  • PEP8 怎么说?我正在从“教程”的开头部分阅读python doc,虽然过去我一般都学过python,但现在我想学习所有文档,所以我还在学习。
  • 惰性解决方案:通过定义example2__str__ 方法,使print iter(a) 显示您想要的任何内容,返回所需的字符串。现在它将不再显示&lt;main.example2 instance...

标签: python class iterator generator theory


【解决方案1】:

生成器是一种迭代器。您的 example1 类正在返回一个生成器,因为您在 __iter__ 中使用了 yield;它是一个独立的迭代器对象,从__iter__ 返回。这使您的example1可迭代,而不是迭代器本身。

在您的第二个示例中,您的课程 __iter__ 返回 self。它不仅仅是可迭代的,它还是它自己的迭代器。因为它返回的是self这里没有单独的对象

Python 明确区分了 iterableiteratoriterable 可以可能被迭代。 iterator 在迭代时执行实际工作。通过使用iter(),您要求 Python 为给定的 iterable 生成一个 iterator

这就是iter(stringobject)返回一个新对象的原因;你已经从 iterable 字符串生成了一个 iterator

您需要这种区别,因为迭代过程需要保持状态的东西;即我们现在在哪里iterator 是跟踪它的对象,因此每次您在迭代器上调用 .next() 方法时,您都会获得下一个值,或者如果没有下一个则引发 StopIteration不再产生价值。

所以在您的示例中,字符串和example1 都只是iterable,对它们调用iter() 会产生一个新的独立迭代器

但是,您的example2 是它自己的 迭代器。在其上调用 iter() 不会产生单独的对象。您不能从已经迭代器的东西创建单独的迭代器

如果您想为您的example2 类生成一个新的独立迭代器,您需要从__iter__ 返回一个不同的、独立的对象:

class ExampleIterator(object):
    def __init__(self, source):
        self.source = source
        self.position = len(source)

    def __iter__(self):
        return self

    def next(self):
        if self.position <= 0:
            raise StopIteration
        self.position -= 1
        return self.source[self.position]

class Example2():
    def __init__(self, word):
        self.word = word

    def __iter__(self):
        return ExampleIterator(self)

这里Example2 又是一个iterable,而不是一个iterator__iter__ 方法返回一个新的、不同的 ExampleIterator() 实例,它负责跟踪迭代状态。

【讨论】:

  • 好的,它已经是一个迭代器了。那么为什么我得到“实例”而不是“迭代器”?仅仅因为它是一个类实例化得到的对象?
  • 您正在查看自定义类的默认 __repr__ 表示字符串。如果你想让它说一些不同的东西,实现你自己的__repr__ 方法。
  • 如果你not在对象上调用iter(),它也将使用相同的表示字符串。
  • 如果我说错了,请纠正我:因为我的示例中的 example2 已经是迭代器和可迭代的,所以调用函数 iter(example2) 不会返回任何效果,因此它是无用的。
  • @PatrickRoncagliolo:这不是没用的。迭代器的迭代器就是对象本身,但总是使用iter() 来支持可迭代对象的代码不必知道这个细节。
【解决方案2】:

我认为你没有问题,只是你不明白这里发生了什么。

一般来说,iter(object) 会为此可迭代对象返回一个迭代器。

这可以通过调用__iter__() 获得,或者,如果不存在,则通过提供一个调用__getitem__() 直到耗尽(引发IndexError)的包装对象。

__iter__() 返回的对象可以是可迭代对象本身(如您的第二个示例中)或其他对象。特别是,如果您像在第一个示例中那样将 __iter__() 设为生成器函数,则它可以是生成器对象。

迭代本身发生在迭代器对象及其next() 上。 __next__() 方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多