在 python 中调用 next 之前装饰迭代器以更改值的好方法是什么?

发布于 2024-11-15 15:45:21 字数 1797 浏览 4 评论 0原文

我正在解决一个问题,涉及验证统一差异补丁中的格式。

内部格式中的变量可以一次跨越多行,因此我编写了一个生成器,它提取每一行并在完成时生成变量。

为了避免在从统一差异文件读取时必须重写此函数,我创建了一个生成器,在将其传递到内部格式验证器之前从该行中去除统一差异字符。然而,我陷入了无限循环(无论是在代码中还是在我的脑海中)。我已将问题抽象为以下代码。我确信有更好的方法来做到这一点。我只是不知道那是什么。

from collections import Iterable

def inner_format_validator(inner_item):
    # Do some validation to inner items
    return inner_item[0] != '+'

def inner_gen(iterable):
    for inner_item in iterable:
        # Operates only on inner_info type data
        yield inner_format_validator(inner_item)

def outer_gen(iterable):
    class DecoratedGenerator(Iterable):
        def __iter__(self):
            return self
        def next(self):
            # Using iterable from closure
            for outer_item in iterable:
                self.outer_info = outer_item[0]
                inner_item = outer_item[1:]
                return inner_item
    decorated_gen = DecoratedGenerator()
    for inner_item in inner_gen(decorated_gen):
        yield inner_item, decorated_gen.outer_info

if __name__ == '__main__':    
    def wrap(string):
        # The point here is that I don't know what the first character will be
        pseudo_rand = len(string)
        if pseudo_rand * pseudo_rand % 2 == 0:
            return '+' + string
        else:
            return '-' + string

    inner_items = ["whatever"] * 3
    # wrap screws up inner_format_validator
    outer_items = [wrap("whatever")] * 3
    # I need to be able to
    # iterate over inner_items
    for inner_info in inner_gen(inner_items):
        print(inner_info)
    # and iterate over outer_items
    for outer_info, inner_info in outer_gen(outer_items):
        # This is an infinite loop
        print(outer_info)
        print(inner_info)

关于更好、更Python化的方法有什么想法吗?

I am working on a problem that involves validating a format from within unified diff patch.

The variables within the inner format can span multiple lines at a time, so I wrote a generator that pulls each line and yields the variable when it is complete.

To avoid having to rewrite this function when reading from a unified diff file, I created a generator to strip the unified diff characters from the line before passing it to the inner format validator. However, I am getting stuck in an infinite loop (both in the code and in my head). I have abstracted to problem to the following code. I'm sure there is a better way to do this. I just don't know what it is.

from collections import Iterable

def inner_format_validator(inner_item):
    # Do some validation to inner items
    return inner_item[0] != '+'

def inner_gen(iterable):
    for inner_item in iterable:
        # Operates only on inner_info type data
        yield inner_format_validator(inner_item)

def outer_gen(iterable):
    class DecoratedGenerator(Iterable):
        def __iter__(self):
            return self
        def next(self):
            # Using iterable from closure
            for outer_item in iterable:
                self.outer_info = outer_item[0]
                inner_item = outer_item[1:]
                return inner_item
    decorated_gen = DecoratedGenerator()
    for inner_item in inner_gen(decorated_gen):
        yield inner_item, decorated_gen.outer_info

if __name__ == '__main__':    
    def wrap(string):
        # The point here is that I don't know what the first character will be
        pseudo_rand = len(string)
        if pseudo_rand * pseudo_rand % 2 == 0:
            return '+' + string
        else:
            return '-' + string

    inner_items = ["whatever"] * 3
    # wrap screws up inner_format_validator
    outer_items = [wrap("whatever")] * 3
    # I need to be able to
    # iterate over inner_items
    for inner_info in inner_gen(inner_items):
        print(inner_info)
    # and iterate over outer_items
    for outer_info, inner_info in outer_gen(outer_items):
        # This is an infinite loop
        print(outer_info)
        print(inner_info)

Any ideas as to a better, more pythonic way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

三月梨花 2024-11-22 15:45:21

我会做一些更简单的事情,如下所示:

def outer_gen(iterable):

    iterable = iter(iterable)
    first_item = next(iterable)
    info = first_item[0]

    yield info, first_item[1:]

    for item in iterable:
        yield info, item

这将仅执行前 4 行一次,然后进入循环并产生您想要的结果。

您可能想在 cacth IndexErrors 各处添加一些 try/except

如果您想在值开始时获取值或相反,请记住您可以使用 itertools 工具箱中的很多内容,特别是 dropwhiletakewhilechain

>>> import itertools
>>> l = ['+foo', '-bar', '+foo']
>>> list(itertools.takewhile(lambda x: x.startswith('+'), l))
['+foo']
>>> list(itertools.dropwhile(lambda x: x.startswith('+'), l))
['-bar', '+foo']
>>> a = itertools.takewhile(lambda x: x.startswith('+'), l)
>>> b = itertools.dropwhile(lambda x: x.startswith('+'), l)
>>> list(itertools.chain(a, b))
['+foo', '-bar', '+foo']

记住,您可以创建像理解列表这样的生成器,将它们存储在变量中并将它们链接起来,就像您通过管道传输 Linux 命令一样:

import random

def create_item():
    return random.choice(('+', '-')) + random.choice(('foo', 'bar'))

random_items = (create_item() for s in xrange(10))
added_items = ((i[0], i[1:]) for i in random_items if i.startswith('+'))
valid_items = ((prefix, line) for prefix, line in added_items if 'foo' in line)

print list(valid_items)

有了这一切,您应该能够找到一些Python式的方法来解决你的问题:-)

I would do something simpler, like this:

def outer_gen(iterable):

    iterable = iter(iterable)
    first_item = next(iterable)
    info = first_item[0]

    yield info, first_item[1:]

    for item in iterable:
        yield info, item

This will execute the 4 first lines only once, then enter the loop and yield what you want.

You probably want to add some try/except to cacth IndexErrors here and there.

If you want to take values while they start with something or the contrary, remember you can use a lot of stuff from the itertools toolbox, and in particular dropwhile, takewhile and chain:

>>> import itertools
>>> l = ['+foo', '-bar', '+foo']
>>> list(itertools.takewhile(lambda x: x.startswith('+'), l))
['+foo']
>>> list(itertools.dropwhile(lambda x: x.startswith('+'), l))
['-bar', '+foo']
>>> a = itertools.takewhile(lambda x: x.startswith('+'), l)
>>> b = itertools.dropwhile(lambda x: x.startswith('+'), l)
>>> list(itertools.chain(a, b))
['+foo', '-bar', '+foo']

And remember that you can create generators like comprehension lists, store them in variables and chain them, just like you would pipe linux commands:

import random

def create_item():
    return random.choice(('+', '-')) + random.choice(('foo', 'bar'))

random_items = (create_item() for s in xrange(10))
added_items = ((i[0], i[1:]) for i in random_items if i.startswith('+'))
valid_items = ((prefix, line) for prefix, line in added_items if 'foo' in line)

print list(valid_items)

With all this, you should be able to find some pythonic way to solve your problem :-)

东北女汉子 2024-11-22 15:45:21

我仍然不太喜欢这个,但至少它更短,更Pythonic:

from itertools import imap, izip
from functools import partial

def inner_format_validator(inner_item):
    return not inner_item.startswith('+')

inner_gen = partial(imap, inner_format_validator)

def split(astr):
    return astr[0], astr[1:]

def outer_gen(iterable):
    outer_stuff, inner_stuff = izip(*imap(split, iterable))
    return izip(inner_gen(inner_stuff), outer_stuff)

[编辑] inner_gen()outer_gen() 没有 imap 和部分:

def inner_gen(iterable):
    for each in iterable:
        yield inner_format_validator(each)

def outer_gen(iterable):
    outer_stuff, inner_stuff = izip(*(split(each) for each in iterable))
    return izip(inner_gen(inner_stuff), outer_stuff)

也许这是一个更好但不同的解决方案:

def transmogrify(iter_of_iters, *transmogrifiers):
    for iters in iter_of_iters:
        yield (
            trans(each) if trans else each
                for trans, each in izip(transmogrifiers, iters)
        )

for outer, inner in transmogrify(imap(split, stuff), inner_format_validator, None):
    print inner, outer

I still don't like this very much, but at least it's shorter and a tad more pythonic:

from itertools import imap, izip
from functools import partial

def inner_format_validator(inner_item):
    return not inner_item.startswith('+')

inner_gen = partial(imap, inner_format_validator)

def split(astr):
    return astr[0], astr[1:]

def outer_gen(iterable):
    outer_stuff, inner_stuff = izip(*imap(split, iterable))
    return izip(inner_gen(inner_stuff), outer_stuff)

[EDIT] inner_gen() and outer_gen() without imap and partial:

def inner_gen(iterable):
    for each in iterable:
        yield inner_format_validator(each)

def outer_gen(iterable):
    outer_stuff, inner_stuff = izip(*(split(each) for each in iterable))
    return izip(inner_gen(inner_stuff), outer_stuff)

Maybe this is a better, though different, solution:

def transmogrify(iter_of_iters, *transmogrifiers):
    for iters in iter_of_iters:
        yield (
            trans(each) if trans else each
                for trans, each in izip(transmogrifiers, iters)
        )

for outer, inner in transmogrify(imap(split, stuff), inner_format_validator, None):
    print inner, outer
轻许诺言 2024-11-22 15:45:21

我认为,如果您将 DecoratedGenerator 的定义更改为:

class DecoratedGenerator(Iterable):
    def __iter__(self):
        # Using iterable from closure
        for outer_item in iterable:
            self.outer_info = outer_item[0]
            inner_item = outer_item[1:]
            yield inner_item

您的原始版本永远不会终止,因为它的 next() 方法是无状态的,并且每次调用时都会返回相同的值。不过,您根本不需要 next() 方法——您可以自己实现 __iter__() (就像我所做的那样),然后一切正常。

I think it will do what you intended if you change the definition of DecoratedGenerator to this:

class DecoratedGenerator(Iterable):
    def __iter__(self):
        # Using iterable from closure
        for outer_item in iterable:
            self.outer_info = outer_item[0]
            inner_item = outer_item[1:]
            yield inner_item

Your original version never terminated because its next() method was stateless and would return the same value every time it was called. You didn't need to have a next() method at all, though--you can implement __iter__() yourself (as I did), and then it all works fine.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文