[Python-3000] sets in P3K?

Nick Coghlan ncoghlan at gmail.com
Sat Apr 29 05:37:29 CEST 2006


Raymond Hettinger wrote:
> The only bump in the road is that the {} notation is so similar to 
> dictionaries that care must be taken to keep the two as distinct as 
> possible.

Here's a summary of the suggestions we've seen so far for empty sets & dicts

Possibilities for an empty set:

set() - explicit, but kind of 'second-class' compared to (), [] and {}
{}    - most obvious option, but currently means dict()
{,}   - Guido's already said he thinks this is too ugly
{/}   - suggestive of phi, but considered too unintuitive/ugly by some

Possibilities for an empty dict:

dict() - explicit, but kind of 'second-class' compared to (), [] and {}
{}     - status quo, but ambiguous given the addition of set literals
{:}    - matches non-empty dicts by including ':' to indicate mapping



However, I'm now -0 on having a set literal at all, and it's because I believe 
we can solve this problem in a more general fashion that applies to more 
functions than just the set() constructor.

Currently, [] and () can appear both standalone or following an expression 
(list literals and comprehensions, and subscripting for [], tuple literals and 
precedence manipulation, and function calls for ())

{} however, can appear only standalone. It is not permitted for it to follow 
an expression. If we change that, we can use it to support an alternate 
invocation syntax for functions that currently expect an iterable as their 
first argument. "EXPR{ARGS}" would be equivalent to "EXPR((ARGS,))", with the 
following differences in the parsing of ARGS:
    x:y would be permitted, and map to a 2-tuple (x, y)
    x:y:z would be permitted, and map to a 3-tuple (x, y, z)
    x=y would be permitted, and map to a 2-tuple ('x', y)
    *x would be permitted, and would extend the passed in tuple with x

This could all be done in the front-end parser, with no back-end AST or 
compiler changes necessary (although we may decide they're desirable for 
optimisation purposes).

Dictionary displays would then be a strict subset of dict{EXPR} and tuple 
literals would be equivalent to tuple{EXPR} (although the latter wouldn't need 
the trailing comma for singleton tuples). List displays would generally be 
equivalent to list{EXPR}, except for list comprehensions, which map to 
list(EXPR) (as omitting the parentheses around the genexp would otherwise be 
illegal).

A constructor for named tuples, a clean syntax for which is the biggest hurdle 
named tuples face, could accept an iterable of name-value pairs, with explicit 
construction looking like:

   result = named_tuple{x=x, y=y}
   return result

Unlike keyword arguments in a normal call, this would preserve the argument 
ordering, so we know that result.x is result[0] and result.y is result[1]

Explicitly constructed sets would look like:
     set() == set{}
     set(x)              # No braced equivalent
     set([x]) == set{x}  # Ignoring list comprehensions
     set((x,)) == set{x}
     set((a, b, c)) == set{a, b, c}

And it would work for frozenset, too:

     frozenset() == frozenset{}
     frozenset(x)                    # No braced equivalent
     frozenset([x]) == froxenset{x}  # Ignoring list comprehensions
     frozenset((x,)) == frozenset{x}
     frozenset((a, b, c)) == frozenset{a, b, c}

itertools.cycle is another possible beneficiary, as it becomes easy to cycle 
through a simple sequence:

   cycle{1, 2, 3} # Infinite cycle through the values 1, 2 and 3

The any and all builtins also become more useful for chained 'or' and 'and' tests:

   if all{x, y, z}: return
   if any{x, y, z}: return

Joining a known set of strings, or extend a mutable sequence also benefits:

   ", ".join{x, y, z}
   seq.extend{x, y, z}

The sundry set methods that accept iterables also benefit:

   s.intersection{1, 2, 3}
   s.issuperset{1, 2, 3}
   s.update{1, 2, 3}

The question of "accept an iterable or accept *args" goes away - the obvious 
way to do it is to accept an iterable, and then use a braced call to 
conveniently invoke the call with a literal sequence instead of a pre-existing 
iterable.

The signatures of dict and dict.update could optionally be simplified back to 
accepting a single mapping or iterable, with the explicit keyword construction 
support being moved to the separate {} form.

The 'iterable of 2-tuples or mapping' questions can be addressed by making the 
keys(), values() and items() methods officially part of the definition of a 
mapping.

So dict.update might simplify to:
   try:
       items_view = obj.items
   except AttributeError:
       items = obj
   else:
       items = items_view()
   for key, value in items:
       self[key] = value

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
---------------------------------------------------------------
             http://www.boredomandlaziness.org


More information about the Python-3000 mailing list