>>> for item in data.items(): item[0], item[1]
874 µs ± 21.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> for key, value in data.items(): key, value
524 µs ± 4.26 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> for item in items_tuple(data): item.key, item.value
5.82 ms ± 117 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Thanks for sharing the results, in particular the amount of difference between "for item in data.items(): item[0], item[1]" and "for key in data.items(): key, value" is a bit surprising to me. I'd have assumed they'd be a bit closer in performance. I expected the named tuple to be significantly slower than the other two, but not quite by that much. Good to know.

I'm -1 on the proposal overall. It's not a bad idea, but in practice it would likely be too much of a detriment to performance and backwards compatibility to "dict.items()". I wouldn't be opposed to considering a different method though, such as "dict.named_items()" or something similar that allowed usage of "item.key" and "item.value".

On Sun, Dec 1, 2019 at 7:56 AM Wes Turner <wes.turner@gmail.com> wrote:
My mistake, I skimmed and assumed that the question was asking for this:

from collections import namedtuple
data = {'1': 1, 'two-2': 2}
x = namedtuple('x', data.keys())
# ValueError: Type names and field names must be valid identifiers: '1'

# But now understand that the request was for this:

from collections import namedtuple
Item = namedtuple('Item', ['key', 'value'])

def items_tuple(self):
    for key, value in self.items():
        yield Item(key, value)

# So that this test would pass:

def test_items():
    data = {'1': 1, 'two-2': 2}
    for item in data.items():
        assert hasattr('key', item)
        assert hasattr('value', item)
        key, value = item
        assert item.key == key
        assert item.value == value

# FWIW, here are rough timings:

data = dict.fromkeys(range(10000))

def timeit(code):
    print(f">>> {code}")
    get_ipython().run_line_magic('timeit', code)

timeit('for item in data.items(): item[0], item[1]')
timeit('for key, value in data.items(): key, value')
timeit('for item in items_tuple(data): item.key, item.value')

>>> for item in data.items(): item[0], item[1]
874 µs ± 21.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> for key, value in data.items(): key, value
524 µs ± 4.26 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> for item in items_tuple(data): item.key, item.value
5.82 ms ± 117 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

On Sun, Dec 1, 2019 at 1:24 AM Andrew Barnert <abarnert@yahoo.com> wrote:
>
> On Nov 30, 2019, at 20:21, Wes Turner <wes.turner@gmail.com> wrote:
> >
> > What about keys that contain invalid characters for attribute names?
>
> What about them?
>
> > items = {'1': 1, 'two-3': 4,}
> > x = object()
> > x.__dict__.update(items) # dangerous
> > x = AttrDict(**items)
> > x.1     # error
> > x.two-3 # error
>
> The message you quoted was about how in Python 2 (but not 3) you could destructure parameters:
>
>     sorted({1:300, 2:4}.items(), key=lambda (key, value): value)
>
> The wider discussion is about how if items() were a view of namedtuples instead of just sequences you could do something even better:
>
>     sorted({1:300, 2:4}.items(), key=lambda it: it.value)
>
> What does either of those have to do with using a dict whose keys are not identifiers as an attribute dictionary for an object? Neither restoring Python 2’s parameter destructuring nor making items namedtuples would in any way affect any of the code you wrote.
>
>
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/OA7NQRF4VTFG2DJVNJAAJ7YIQNB3IVVR/
Code of Conduct: http://python.org/psf/codeofconduct/