【问题标题】:Python: Classes: Need to turn my list into a generatorPython:类:需要将我的列表变成生成器
【发布时间】:2018-03-08 17:54:34
【问题描述】:

我尝试在代码中实现“yield”,以便它返回一个生成器,但它正在返回:

生成器对象 PText.possible 位于 0x000001C9F4E03DB0>

我本质上需要使用生成器,因为我使用的 txt 文件非常长并且创建列表需要一段时间而且速度很慢,所以我需要以某种方式实现生成器以使其不需要尽可能多的时间。

我使用 return 语句编写了所有代码,并尝试插入 yield,但它不起作用。我错过了什么吗?我相信代码中有两个实例需要生成器,但我无法弄清楚。

这是一个连续的代码,我标记了我使用 yield 的位置:

WORD_LIST_FILE = 'English_words_by_usage_rank.txt'
# this is just a file with a list of words

class PText(object):
    keyMap = {'1': "'-", '2': 'abc', '3': 'def',
              '4': 'ghi', '5': 'jkl', '6': 'mno',
              '7': 'pqrs', '8': 'tuv', '9': 'wxyz'}
    wordsByPrefix = None
    rankByWord = None

    def __init__(self):
        self.keys = []

    def add(self, keys_typed):
        """ Append a new key press (or a sequence of them) onto current word.
        keyPress -- '2', '3', ..., '9' from telephone keypad.
        >>> pt= Ptext()
        >>> pt.add('345')
        >>> pt.keys
        ['3', '4', '5']
        """
        for key in  keys_typed:
            if key in PText.keyMap:
                self.keys.append(key)

    def backspace(self):
        """Remove last key press from current word (backspace)."""
        if len(self.keys) > 0:
            self.keys.pop()

    def match(self, word):
        """See if word might be what the phone user is trying to type."""
        if len(word) < len(self.keys):
            return False
        for i in range(len(self.keys)):
            letter = word[i]
            keystroke = self.keys[i]
            ok_letters = PText.keyMap[keystroke]
            if letter not in ok_letters:
                return False
        # if all the keys matched the corrresponding letters in word
        return True

我在这部分使用了产量:

    def possiblePrefixes(self):
        """ Generate a list of all 3-char prefixes that can be gernerated
        from the beginning key presses.
        """
        if len(self.keys) < 3:
            raise ValueError("not enough keys for prefixes")
        ret = []
        kmap = PText.keyMap
        for c1 in kmap[self.keys[0]]:
            for c2 in kmap[self.keys[1]]:
                for c3 in kmap[self.keys[2]]:
                    ret.append(c1 + c2 + c3)
        yield ret

    def _performCaching(self):
        """Read word list into data structures that we will use:

        wordsByPrefix -- a dictionary that contains a list of words in rank
                         order for each 3-letter prefix encountered in the
                         word list

        rankByWord -- a dictionary that maps each word to its rank

        If these are already filled in (not None), then return immediately.

        Reads the word list file, WORD_LIST_FILE, which has all possible
        English words (that we will consider) listed in rank order.
        """
        if PText.wordsByPrefix is not None:
            return
        wbp = {}
        rbw = {}
        rank = 1
        file = open(WORD_LIST_FILE, 'r')
        for word in file:
            if word[-1:] == '\n':
                word = word[:-1]
            #print("'" + word + "'")
            if len(word) >= 3:
                prefix = word[:3].lower()
                if prefix in wbp:
                    wbp[prefix].append(word)
                else:
                    wbp[prefix] = [word]
                rbw[word] = rank
                rank += 1
        file.close()

        PText.wordsByPrefix = wbp
        PText.rankByWord = rbw

这里的另一个收益:

    def possible(self):
        """Returns a list of possible words given the current key sequence.
        Assumes at least 3 keystrokes have been entered (raises ValueError
        otherwise). Returned in rank order.
        """
        self._performCaching()
        if len(self.keys) < 3:
            raise ValueError('Too few keys for predicting word')
        wbp = PText.wordsByPrefix
        ret = []
        for prefix in self.possiblePrefixes():
            if prefix in wbp:
                wordList = wbp[prefix]
                for word in wordList:
                    if self.match(word):
                        ret.append(word)
        ret1 = []
        [ret1.append(item) for item in ret if item not in ret1]
        yield ret1


    def best(self):
        """Return the most likely English word for the current key sequence.
        Assumes at least 3 keystrokes have been entered (raises ValueError
        otherwise). If there a no words that match the current key sequence,
        ValueError is raised.
        """
        rank = 1
        highest = 1
        ranking = {}
        for prefix in self.possible():
            ranking[prefix] = rank
            rank += 1
        for vals in ranking.values():
            if vals == highest:
                highest = vals
        bests = ([k for k,v in ranking.items() if v == highest])
        for i in bests:
            answer = i
        return i

【问题讨论】:

  • 你想要一个生成器,你得到了一个生成器。有什么问题?
  • 欢迎来到 StackOverflow。请阅读并遵循帮助文档中的发布指南。 Minimal, complete, verifiable example 适用于此。在您发布 MCVE 代码并准确描述问题之前,我们无法有效地帮助您。我们应该能够将您发布的代码粘贴到文本文件中并重现您描述的问题。
  • 如果你打印出一个生成器,你得到的是看起来像&lt;generator blah blah at 0x12345678&gt;的东西。如果你期望得到一个看起来像值列表的东西……好吧,想想看:它可以做到这一点的唯一方法是如果它立即消耗生成器,之后它将不再有用。
  • 如果您只是想在一个小测试用例上检查生成器,出于调试目的,最简单的做法是thing = list(thing)。然后你可以打印出thing(之后也可以继续使用它)。显然不要把它放在你的 real 代码中,但是对于调试来说,将(小)生成器转换为这样的列表通常很有帮助。

标签: python function class generator


【解决方案1】:

您对生成器的理解和使用不正确。这是您当前尝试执行的操作的简化示例:

def possiblePrefixes():
    ret = []
    keys = ['abc','def','ghi']
    for c1 in keys[0]:
        for c2 in keys[1]:
            for c3 in keys[2]:
                ret.append(c1 + c2 + c3)
    yield ret

print(possiblePrefixes())

输出:

<generator object possiblePrefixes at 0x000001DCFFCE99E8>

这通常是您打印生成器的返回值时得到的结果。您对生成器所做的操作是对其进行迭代,但逻辑存在缺陷。您已经在一个列表中生成了所有答案,并产生了一个大列表。这违背了生成器的目的:

for p in possiblePrefixes():
    print(p)

输出:

['adg', 'adh', 'adi', 'aeg', 'aeh', 'aei', 'afg', 'afh', 'afi', 'bdg', 'bdh', 'bdi', 'beg', 'beh', 'bei', 'bfg', 'bfh', 'bfi', 'cdg', 'cdh', 'cdi', 'ceg', 'ceh', 'cei', 'cfg', 'cfh', 'cfi']

生成器一次应该产生一个值。它不会预先生成所有值:

def possiblePrefixesFixed():
    ret = []
    keys = ['abc','def','ghi']
    for c1 in keys[0]:
        for c2 in keys[1]:
            for c3 in keys[2]:
                yield c1 + c2 + c3

print(possiblePrefixesFixed())

for p in possiblePrefixesFixed():
    print(p)

输出:

<generator object possiblePrefixesFixed at 0x000001DCFFCE99E8>
adg
adh
adi
aeg
aeh
aei
afg
afh
afi
bdg
bdh
bdi
beg
beh
bei
bfg
bfh
bfi
cdg
cdh
cdi
ceg
ceh
cei
cfg
cfh
cfi

【讨论】:

    【解决方案2】:

    您似乎对yield 的工作原理有点困惑。

    一个例子是

    def square_gen_fun(seq):
        for i in seq:
            yield x
    

    我将返回一个生成器对象。要访问这个生成器的结果,我可以像这样迭代它:

    gen = square_gen_fun(range(5))
    for i2 in gen:
         print(i2, end=' ')
    

    这将打印出0 1 4 9 16

    你在这里做什么

    ret = []
    for c1 in kmap[self.keys[0]]:
        for c2 in kmap[self.keys[1]]:
            for c3 in kmap[self.keys[2]]:
                ret.append(c1 + c2 + c3)
    yield ret
    

    错了。您将返回一个包含一个元素、一个列表或c1+c2+c3 元素的生成器对象。你真正想要的是:

    for c1 in kmap[self.keys[0]]:
        for c2 in kmap[self.keys[1]]:
            for c3 in kmap[self.keys[2]]:
                yield c1 + c2 + c3
    

    【讨论】:

      猜你喜欢
      • 2016-12-22
      • 2017-11-18
      • 2012-07-25
      • 2013-06-30
      • 2015-07-06
      • 2014-02-16
      • 2015-01-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多