【问题标题】:How to iterate through a defaultdict(list) in Python?如何在 Python 中遍历 defaultdict(list)?
【发布时间】:2012-01-28 14:46:21
【问题描述】:

如何在 Python 中遍历 defaultdict(list)? 有没有更好的方法在 Python 中拥有一个列表字典? 我已经尝试了正常的iter(dict),但我得到了错误:

>>> import para
>>> para.print_doc('./sentseg_en/essentials.txt')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "para.py", line 31, in print_doc
    for para in iter(doc):
TypeError: iteration over non-sequence

主类:

import para
para.print_doc('./foo/bar/para-lines.txt')

para.pyc:

# -*- coding: utf-8 -*-
## Modified paragraph into a defaultdict(list) structure
## Original code from http://code.activestate.com/recipes/66063/
from collections import defaultdict
class Paragraphs:
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
    # Separator here refers to the paragraph seperator,
    #  the default separator is '\n'.
    def __init__(self, filename, separator=None):
        # Set separator if passed into object's parameter,
        #  else set default separator as '\n'
        if separator is None:
            def separator(line): return line == '\n'
        elif not callable(separator):
            raise TypeError, "separator argument must be callable"
        self.separator = separator
        # Reading lines from files into a dictionary of lists
        self.doc = defaultdict(list)
        paraIndex = 0
        with open(filename) as readFile:
            for line in readFile:
                if line == separator:
                    paraIndex+=1
                else:
                    self.doc[paraIndex].append(line)

# Prints out populated doc from txtfile
def print_doc(filename):
    text = Paragraphs(filename)
    for para in iter(text.doc):
        for sent in text.doc[para]:
            print "Para#%d, Sent#%d: %s" % (
                para, text.doc[para].index(sent), sent)

例如./foo/bar/para-lines.txt 看起来像这样:

This is a start of a paragraph.
foo barr
bar foo
foo foo
This is the end.

This is the start of next para.
foo boo bar bar
this is the end.

主类的输出应该是这样的:

Para#1,Sent#1: This is a start of a paragraph.
Para#1,Sent#2: foo barr
Para#1,Sent#3: bar foo
Para#1,Sent#4: foo foo
Para#1,Sent#5: This is the end.

Para#2,Sent#1: This is the start of next para.
Para#2,Sent#2: foo boo bar bar
Para#2,Sent#3: this is the end.

【问题讨论】:

    标签: python loops dictionary iterator defaultdict


    【解决方案1】:

    你遇到的问题

    for para in iter(doc):
    

    doc 是 Paragraph 的一个实例,而不是 defaultdict。您在 __init__ 方法中使用的默认字典超出范围并丢失。所以你需要做两件事:

    1. __init__方法中创建的doc保存为实例变量(例如self.doc)。

    2. 要么使Paragraphs 自身可迭代(通过添加__iter__ 方法),要么允许它访问创建的doc 对象。

    【讨论】:

    • 我尝试将docself.doc 保存在self.doc = defaultdict(list)self.doc[paraIndex].append(line) 中。但同样的超出范围的问题也会发生。
    • @2er0:它在作用域内,但作为doc.doc(这意味着还有一个命名问题——你应该在print_doc中使用paragraphs而不是doc) .
    • 是的,感谢您注意到命名问题,经过一些小的更改后,迭代工作。但是让我看看我是否可以将self.doc 解决方案与 unutbu 的循环解决方案结合起来。
    【解决方案2】:

    您链接到的食谱相当陈旧。它是在 2001 年编写的,当时 Python 还没有更现代的工具,例如 itertools.groupby(在 Python2.4 中引入,released in late 2003)。以下是您的代码使用groupby 的样子:

    import itertools
    import sys
    
    with open('para-lines.txt', 'r') as f:
        paranum = 0
        for is_separator, paragraph in itertools.groupby(f, lambda line: line == '\n'):
            if is_separator:
                # we've reached paragraph separator
                print
            else:
                paranum += 1
                for n, sentence in enumerate(paragraph, start = 1):
                    sys.stdout.write(
                        'Para#{i:d},Sent#{n:d}: {s}'.format(
                            i = paranum, n = n, s = sentence))
    

    【讨论】:

    • 我是否正确地说,当我退出 for 循环时,paragraph 会超出范围?如何保留该段落并在itertools.groupby 循环之外继续访问它?
    • 不,名称paragraph 不会超出范围。 Python 不会为 withfor 之类的块结构打开新的作用域,而只是为函数。
    • paragraph 每次通过循环时都会重新分配一个新值。如果您希望保留旧段落,您可以在循环外定义一个列表paragraphs = [],并将每个段落附加到循环内:paragraphs.append(paragraph)
    • 我在您对 Daniel Roseman 的评论中读到该文本文件很大。尝试将所有段落保存在列表(或字典)中可能会占用大量内存。你需要它们全部还是只需要前面的 n 段(使用双端队列)?您需要随机访问它们(使用 dict)还是迭代访问(使用列表或双端队列)?了解您需要这些段落的内容将影响我们推荐使用的最佳算法/数据结构。
    • 我需要交叉引用并检查另一个具有相似段落的文档的相似性,并对齐段落内的句子。从技术上讲,我至少有 150 个文本文件,每个文本文件有 3-10 个段落,每个段落有 5-6 个句子,每个句子至少 200-300 个字符。我的字典中有 150*7*5*250 个字符。我认为应该不是问题。但是在测试代码之后,我需要将系统扩展到 1500 个文本,每个文本 50 个段落,我认为这将成为一个问题。
    【解决方案3】:

    问题似乎是您正在迭代 Paragraphs 类,而不是字典。此外,不要遍历键然后访问字典条目,而是考虑使用

    for (key, value) in d.items():
    

    【讨论】:

      【解决方案4】:

      它失败了,因为您没有在 Paragraphs 类中定义 __iter__(),然后尝试调用 iter(doc)(其中 doc 是 Paragraphs 实例)。

      要可迭代的类必须有__iter__(),它返回迭代器。 Docs here.

      【讨论】:

        【解决方案5】:

        我想不出你在这里使用字典的任何原因,更不用说默认字典了。列表列表会简单得多。

        doc = []
        with open(filename) as readFile:
            para = []
            for line in readFile:
                if line == separator:
                    doc.append(para)
                    para = []
                else:
                    para.append(line)
            doc.append(para)
        

        【讨论】:

        • 因为我的txt文件会是一个很大的txt文件,所以通过嵌套列表访问会占用很多时间。也许我需要一本字典词典。如果我想要字典,我该怎么办?
        • 这是怎么回事?为什么你认为嵌套列表会比 dicts 的 dict 花费更长的时间?
        猜你喜欢
        • 2015-12-25
        • 2019-07-28
        • 2013-06-02
        • 2018-08-16
        • 1970-01-01
        • 2012-01-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多