简体   繁体   中英

obscure iterator behavior in Python

So I was experimenting with somo Odoo code (Qweb templates to be more specific) and I found this function

def foreach_iterator(base_ctx, enum, name):
    ctx = base_ctx.copy()
    if not enum:
        return
    if isinstance(enum, int):
        enum = xrange(enum)
    size = None
    if isinstance(enum, Sized):
        ctx["%s_size" % name] = size = len(enum)
    if isinstance(enum, Mapping):
        enum = enum.iteritems()
    else:
        enum = izip(*tee(enum))
    value_key = '%s_value' % name
    index_key = '%s_index' % name
    first_key = '%s_first' % name
    last_key = '%s_last' % name
    parity_key = '%s_parity' % name
    even_key = '%s_even' % name
    odd_key = '%s_odd' % name
    for index, (item, value) in enumerate(enum):
        ctx[name] = item
        ctx[value_key] = value
        ctx[index_key] = index
        ctx[first_key] = index == 0
        if size is not None:
            ctx[last_key] = index + 1 == size
        if index % 2:
            ctx[parity_key] = 'odd'
            ctx[even_key] = False
            ctx[odd_key] = True
        else:
            ctx[parity_key] = 'even'
            ctx[even_key] = True
            ctx[odd_key] = False
        yield ctx
    # copy changed items back into source context (?)
    # FIXME: maybe values could provide a ChainMap-style clone?
    for k in base_ctx.keys():
        base_ctx[k] = ctx[k]

This function is how t-foreach is implemented so it is supposed to create an iterator over whatever you pass into it.

But...

>>> list(foreach_iterator({},[1,2,3],"name"))
[{'name': 2,
  'name_even': False,
  'name_first': False,
  'name_index': 1,
  'name_last': True,
  'name_odd': True,
  'name_parity': 'odd',
  'name_size': 2,
  'name_value': 2},
 {'name': 2,
  'name_even': False,
  'name_first': False,
  'name_index': 1,
  'name_last': True,
  'name_odd': True,
  'name_parity': 'odd',
  'name_size': 2,
  'name_value': 2}]

and then

>>> k = foreach_iterator({},[1,2],"name")
>>> k.next()
{'name': 1,
 'name_even': True,
 'name_first': True,
 'name_index': 0,
 'name_last': False,
 'name_odd': False,
 'name_parity': 'even',
 'name_size': 2,
 'name_value': 1}
>>> k.next()
{'name': 2,
 'name_even': False,
 'name_first': False,
 'name_index': 1,
 'name_last': True,
 'name_odd': True,
 'name_parity': 'odd',
 'name_size': 2,
 'name_value': 2}

I've been doing the Python thing for a while now so I know there must be missing some silly thing but for the life of me I can't figure out what is it.

Any takers? Thanks in advance.

The generator function is overwriting the contents of same dict referenced by ctx again and again.

In the second variant of your test code contents are printed out between overwrites so the changes are visible.

Probably this was meant as some kind of optimization to not create new dictionaries all the time but is obviously dangerous.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM