Mailing List Archive

use set notation for repr of dict_keys?
Having a dict like
d = {'one': 1, 'two': 2}
the representation of its keys
repr(d.keys())
gives
"dict_keys(['one', 'two'])"

But since the keys are unique, wouldn't a representation using the set
notation
be more intuitive, i.e. what about changing the output of
dict_keys.__repr__ to
"dict_keys({'one', 'two'})"
(using curly braces instead of brackets)

Wolfgang

--
https://mail.python.org/mailman/listinfo/python-list
Re: use set notation for repr of dict_keys? [ In reply to ]
On 2/20/2021 2:25 AM, Wolfgang Stöcher wrote:
> Having a dict like
>   d = {'one': 1, 'two': 2}
> the representation of its keys
>   repr(d.keys())
> gives
>   "dict_keys(['one', 'two'])"
>
> But since the keys are unique, wouldn't a representation using the set
> notation
> be more intuitive, i.e. what about changing the output of
> dict_keys.__repr__ to
>     "dict_keys({'one', 'two'})"
> (using curly braces instead of brackets)

From 3.0 to 3.7?, when dict keys were unordered, that might have made
sense. But now that dict keys are insertion ordered, I think the list
brackets suggesting a significant key order is better. There is also
the issue that representation changes can break code and therefore need
substantial reason.


--
Terry Jan Reedy


--
https://mail.python.org/mailman/listinfo/python-list
Re: use set notation for repr of dict_keys? [ In reply to ]
On 20/02/2021 20.25, Wolfgang Stöcher wrote:
> Having a dict like
>   d = {'one': 1, 'two': 2}
> the representation of its keys
>   repr(d.keys())
> gives
>   "dict_keys(['one', 'two'])"
>
> But since the keys are unique, wouldn't a representation using the set
> notation
> be more intuitive, i.e. what about changing the output of
> dict_keys.__repr__ to
>     "dict_keys({'one', 'two'})"
> (using curly braces instead of brackets)


When considering the data-returned, the logic of formatting as a set
makes sense. So, why did the Python-gods decide otherwise?


Let's start by asking what it actually is:

>>> d = {'one': 1, 'two': 2}
>>> repr(d.keys())
"dict_keys(['one', 'two'])"
>>> k = d.keys()
>>> type( k )
<class 'dict_keys'>

So, the output is not a set (as you say) but nor (as
apparently-indicated by the square-brackets) is it actually a list!

Not much help there, then. So, let's use help() to see if that helps,
hah! (long listing, so not reproduced here). No joy there either.


Is it actually one of our 'standard' collections at all?

>>> k is dict
False
>>> k is list
False
>>> k is set
False
>>> k is tuple
False

OK, that makes reasonable sense. Perhaps dict_keys are a sub-class then?

>>> isinstance( k, dict )
False
>>> isinstance( k, list )
False
>>> isinstance( k, set )
False
>>> isinstance( k, tuple )
False

Still going 'nowhere' fast!

It is (apparently) reported as a list, and we'd like to think of it as a
set. So, compare help() output with the attributes of list and set
(and/or other collections).

There are considerable differences (again, not reproduced here due to
length).

However, still not revealing any answers!


If we de-construct the data contained in a dictionary, then as well as
the keys, we should consider the values:

>>> d
{'one': 1, 'two': 2}
>>> k = d.keys()
>>> k
dict_keys(['one', 'two'])
>>> v = d.values()
>>> v
dict_values([1, 2])

Hah, they are (apparently) considered a list as well, but have another
distinct type/are another custom-object.

Still not making progress though!


We've looked at the data/the output and found nothing much!

Now, let's totally invert our view of the matter: Instead of
deconstructing the original dictionary, consider the purpose of repr()?

<<<built-in function to compute the “official” string representation of
an object.>>> #repr

Thus, we should be able to take the output of repr() and re-create the
original object.

Because a dictionary consists of key-value pairs, we will need both
'sets' of components:

>>> new_d = dict( zip( k, v ) )
>>> new_d
{'one': 1, 'two': 2}

Ahah! Now, we realise that whereas a list-like #list data structure
maintains the sequence of its elements, a set #set is not required to do
so. Thus, if "k" were a set, what is produced on your machine may be
different to what happens on mine/no guarantees:

Possibly @Wolfgang's machine =
>>> k
{ 'one', 'two' }

Possibly @dn's machine =
>>> k
{ 'two', 'one' }

Thus no guarantee that when we try to re-combine keys and values they
would correspond correctly!

- and if we applied the same to data - even worse: combinatorial issue!


Web.Refs:
#repr:
https://docs.python.org/3/reference/datamodel.html#basic-customization
#list: and #set:
https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy
--
Regards,
=dn
--
https://mail.python.org/mailman/listinfo/python-list
Re: use set notation for repr of dict_keys? [ In reply to ]
On Sat, Feb 20, 2021, at 15:00, dn via Python-list wrote:
> So, the output is not a set (as you say) but nor (as
> apparently-indicated by the square-brackets) is it actually a list!

To be clear, it is an instance of collections.abc.Set, and supports most binary operators that sets support.

I was surprised, though, to find that you can't remove items directly from the key set, or in general update it in place with &= or -= (these operators work, but give a new set object).

AIUI the keys, values, and items collections have always had the ordering guarantee that iterating over them while not modifying the underlying dictionary will give the same order each time [.with respect to each other, and possibly also with respect to iterating over the original dictionary to obtain the keys]
--
https://mail.python.org/mailman/listinfo/python-list
Re: use set notation for repr of dict_keys? [ In reply to ]
On Wed, Feb 24, 2021 at 4:29 PM Random832 <random832@fastmail.com> wrote:
>
> AIUI the keys, values, and items collections have always had the ordering guarantee that iterating over them while not modifying the underlying dictionary will give the same order each time [.with respect to each other, and possibly also with respect to iterating over the original dictionary to obtain the keys]
>

Correct, on all counts. I believe the old guarantee (before insertion
order was maintained) was that *any* change could change the iteration
order, but that .items(), .keys(), and .values() (and their iter*
counterparts) would always change together. But in practice, I believe
this kind of code has always been safe:

for key in some_dict:
some_dict[key] += 1

I can't find anywhere in the docs that says that changes to *values*
(without changing the set of keys) won't change iteration order, but
given how common this kind of code is, I would expect that it's at
least a de facto guarantee.

ChrisA
--
https://mail.python.org/mailman/listinfo/python-list
Re: use set notation for repr of dict_keys? [ In reply to ]
On Wed, 24 Feb 2021 at 06:29, Random832 <random832@fastmail.com> wrote:
> I was surprised, though, to find that you can't remove items directly from the key set, or in general update it in place with &= or -= (these operators work, but give a new set object).

This is because they are a view. Changing the key object means you
will change the underlying dict. Probably not that you want or expect.
You can just "cast" them into a "real" set object.

There was a discussion to implement the whole Set interface for dicts.
Currently, only `|` is supported.
--
https://mail.python.org/mailman/listinfo/python-list
Re: use set notation for repr of dict_keys? [ In reply to ]
On Wed, Feb 24, 2021, at 02:59, Marco Sulla wrote:
> On Wed, 24 Feb 2021 at 06:29, Random832 <random832@fastmail.com> wrote:
> > I was surprised, though, to find that you can't remove items directly from the key set, or in general update it in place with &= or -= (these operators work, but give a new set object).
>
> This is because they are a view. Changing the key object means you
> will change the underlying dict. Probably not that you want or expect.

Why wouldn't it be what I want or expect? Java allows exactly this [.and it's the only way provided to, for example, remove all keys matching a predicate in a single pass... an operation that Python sets don't support either]

> You can just "cast" them into a "real" set object.
>
> There was a discussion to implement the whole Set interface for dicts.
> Currently, only `|` is supported.
--
https://mail.python.org/mailman/listinfo/python-list
Re: use set notation for repr of dict_keys? [ In reply to ]
On Wed, 24 Feb 2021 at 15:02, Random832 <random832@fastmail.com> wrote:
> On Wed, Feb 24, 2021, at 02:59, Marco Sulla wrote:
> > On Wed, 24 Feb 2021 at 06:29, Random832 <random832@fastmail.com> wrote:
> > > I was surprised, though, to find that you can't remove items directly from the key set, or in general update it in place with &= or -= (these operators work, but give a new set object).
> >
> > This is because they are a view. Changing the key object means you
> > will change the underlying dict. Probably not that you want or expect.
>
> Why wouldn't it be what I want or expect? Java allows exactly this

I didn't know this. I like Java, but IMHO it's quite confusing that
you can remove a key from a Map using the keys object. In my mind it's
more natural to think views as read-only, while changes can be done
only using the original object. But maybe my mind has too strict
bounds.

> [.and it's the only way provided to, for example, remove all keys matching a
> predicate in a single pass... an operation that Python sets don't support either]

I hope indeed that someday Python can do:

filtered_dict = a_dict - a_set
--
https://mail.python.org/mailman/listinfo/python-list