【问题标题】:TypeError: str object is not an iteratorTypeError:str 对象不是迭代器
【发布时间】:2016-09-12 21:32:36
【问题描述】:

我有一个由单词组成的文件,每行一个单词。该文件如下所示:

aaa
bob
fff
err
ddd
fff
err

我想统计一对单词一个接一个出现的频率。

例如,

aaa,bob: 1
bob,fff:1
fff,err:2

等等。 这个我试过了

f=open(file,'r')
content=f.readlines()
f.close()
dic={}
it=iter(content)
for line in content:
    print line, next(line);
    dic.update({[line,next(line)]: 1})

我得到了错误:

TypeError: str object is not an iterator

然后我尝试使用迭代器:

it=iter(content)
for x in it:
    print x, next(x);

再次遇到同样的错误。 请帮忙!

【问题讨论】:

  • 你认为next(thing) 是做什么的?这并不意味着“thing 之后的事情”。
  • 旁白:一种解决方案是:pprint.pprint(collections.Counter(zip(content[0:],content[1:])).most_common())
  • @user2357112 :这就是我接下来的想法!接下来做什么? python 文档说:'通过调用其 next() 方法从迭代器中检索下一项。如果给出默认值,则在迭代器耗尽时返回,否则引发 StopIteration。
  • @rowana:你看到这个参数是如何被称为“迭代器”的了吗?它将迭代器作为参数,而不是从迭代器中检索的元素之一,并检索迭代器的下一项。 (这会推进迭代器的状态,因此该迭代器的下一次next 调用或for 迭代将转到之后的项目。)我想下一个问题是,你认为迭代器是什么? line 不是迭代器。
  • @user2357112,现在知道了!谢谢!

标签: python dictionary iterator generator


【解决方案1】:

你只需要跟踪上一行,文件对象返回它自己的迭代器,所以你根本不需要 iterreadlines,调用 next 一开始就创建一个变量 prev 然后在循环中不断更新 prev

from collections import defaultdict

d = defaultdict(int)

with open("in.txt") as f:
    prev = next(f).strip()
    for line in map(str.strip,f): # python2 use itertools.imap
        d[prev, line] += 1
        prev = line

这会给你:

defaultdict(<type 'int'>, {('aaa', 'bob'): 1, ('fff', 'err'): 2, ('err', 'ddd'): 1, ('bob', 'fff'): 1, ('ddd', 'fff'): 1})

【讨论】:

  • 这是如何使用迭代器的一个很好的例子。想想next 有点像[].pop():它返回下一个值,但也将它从迭代器中删除。一个很好的用途是像这样的伪归约:获取初始值,然后循环其余的值。
【解决方案2】:

line 和所有strs 一样,是一个iterable,这意味着它有一个__iter__ 方法。但是next 可以与具有__next__ 方法的iterators 一起使用(在Python 2 中它是next 方法)。当解释器执行next(line) 时,它会尝试调用line.__next__。由于line 没有__next__ 方法,它引发TypeError: str object is not an iterator

由于line 是一个迭代器able 并且有一个__iter__ 方法,我们可以设置it = iter(line)it 是一个带有__next__ 方法的iteratornext(it) 返回line 中的下一个字符。但是您正在寻找文件中的下一行,因此请尝试以下操作:

from collections import defaultdict

dic = defaultdict(int)
with open('file.txt') as f:
    content = f.readlines()
    for i in range(len(content) - 1):
        key = content[i].rstrip() + ',' + content[i+1].rstrip()
        dic[key] += 1

for k,v in dic.items():
    print(k,':',v)

输出file.txt 与 OP 相同)

err,ddd : 1
ddd,fff : 1
aaa,bob : 1
fff,err : 2
bob,fff : 1

【讨论】:

    【解决方案3】:
    from collections import Counter
    with open(file, 'r') as f:
        content = f.readlines()
    result = Counter((a, b) for a, b in zip(content[0:-1], content[1:]))
    

    这将是一个字典,其键是行对(按顺序),其值是该对出现的次数。

    【讨论】:

      【解决方案4】:

      正如其他人所说,line 是一个字符串,因此不能与 next() 方法一起使用。此外,您不能将列表用作字典的键,因为它们是可散列的。您可以改用元组。 一个简单的解决方案:

      f=open(file,'r')
      content=f.readlines()
      f.close()
      
      dic={}
      
      for i in range(len(content)-1):
          print(content[i], content[i+1])
          try:
              dic[(content[i], content[i+1])] += 1
          except KeyError:
              dic[(content[i], content[i+1])] = 1
      

      还要注意,通过使用 readlines(),您还可以保留每行的 '\n'。你可能想先把它去掉:

          content = []
          with open(file,'r') as f:
              for line in f:
                  content.append(line.strip('\n'))
      

      【讨论】:

        【解决方案5】:

        您可以使用 2 行 dequeCounter

        from collections import Counter, deque
        
        lc=Counter()
        d=deque(maxlen=2)
        with open(fn) as f:
            d.append(next(f))
            for line in f:
                d.append(line)
                lc+=Counter(["{},{}".format(*[e.rstrip() for e in d])])
        
        >>> lc
        Counter({'fff,err': 2, 'ddd,fff': 1, 'bob,fff': 1, 'aaa,bob': 1, 'err,ddd': 1})
        

        您还可以使用regex 进行捕获前瞻:

        with open(fn) as f:
            lc=Counter((m.group(1)+','+m.group(2),) for m in re.finditer(r"(\w+)\n(?=(\w+))", f.read()))
        

        【讨论】:

          【解决方案6】:

          正如其他人提到的,您不能在作为字符串的行上使用next。您可以使用 itertools.tee 从文件对象创建两个独立的迭代器,然后使用 collections.Counterzip 从行对创建计数器对象

          from itertools import tee
          from collections import Counter
          with open('test.txt') as f:
              # f = (line.rstrip() for line in f) # if you don't want the trailing new lines 
              f, ne = tee(f)
              next(ne)
              print(Counter(zip(f, ne)))
          

          请注意,由于文件对象包含在其尾部带有换行符的行,如果您不想这样做,您可以删除这些行。

          【讨论】:

            【解决方案7】:

            您的值x 包含一个字符串“ddd/ccc/etc”。它没有下一个。 next() 属于迭代器,用于从迭代器中获取下一个元素。正确的称呼方式是it.next()

            it=iter(content)
            for x in it:
                print x, it.next();
            

            但是在你消费完迭代器中的所有元素后你会得到一个异常。所以,你需要捕获 StopIteration 异常。

            for x in it:
                try:
                    line, next_line = x, it.next()
                    # do your count logic overhere
                except StopIteration:
                    break
            

            dic.update({[line,next_line]: 1}) 不起作用。您将跳过可能的组合。

            【讨论】:

            • 不应该是next(it)吗?
            • @levi,谢谢。它工作得很好。可悲的是,我没有足够的积分来投票。当我这样做时,我会的!
            • 我想你的意思是it.__next__() :)
            • @CraigBurgler 不是,next()。测试一下。
            • 有趣,在 Python 2 中是 it.next(),在 Python 3 中是 it.__next__()
            猜你喜欢
            • 1970-01-01
            • 2012-08-20
            • 1970-01-01
            • 1970-01-01
            • 2021-10-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多