[Python-Dev] (no subject)

Donald Stufft donald at stufft.io
Tue Feb 10 01:53:39 CET 2015


> On Feb 9, 2015, at 7:29 PM, Neil Girdhar <mistersheik at gmail.com> wrote:
> 
> For some reason I can't seem to reply using Google groups, which is is telling "this is a read-only mirror" (anyone know why?)  Anyway, I'm going to answer as best I can the concerns.
> 
> Antoine said:
> 
> To be clear, the PEP will probably be useful for one single line of 
> Python code every 10000. This is a very weak case for such an intrusive 
> syntax addition. I would support the PEP if it only added the simple 
> cases of tuple unpacking, left alone function call conventions, and 
> didn't introduce **-unpacking. 
> 
> To me this is more of a syntax simplification than a syntax addition.  For me the **-unpacking is the most useful part. Regarding utility, it seems that a many of the people on r/python were pretty excited about this PEP: http://www.reddit.com/r/Python/comments/2synry/so_8_peps_are_currently_being_proposed_for_python/ <http://www.reddit.com/r/Python/comments/2synry/so_8_peps_are_currently_being_proposed_for_python/>
> 
>> 
> Victor noticed that there's a mistake with the code:
> 
> >>> ranges = [range(i) for i in range(5)] 
> >>> [*item for item in ranges] 
> [0, 0, 1, 0, 1, 2, 0, 1, 2, 3] 
> 
> It should be a range(4) in the code.  The "*" applies to only item.  It is the same as writing:
> 
> [*range(0), *range(1), *range(2), *range(3), *range(4)]
> 
> which is the same as unpacking all of those ranges into a list.
> 
> > function(**kw_arguments, **more_arguments) 
> If the key "key1" is in both dictionaries, more_arguments wins, right? 
> 
> There was some debate and it was decided that duplicate keyword arguments would remain an error (for now at least).  If you want to merge the dictionaries with overriding, then you can still do:
> 
> function(**{**kw_arguments, **more_arguments})
> 
> because **-unpacking in dicts overrides as you guessed.
> 
>> 
> 
> 
> On Mon, Feb 9, 2015 at 7:12 PM, Donald Stufft <donald at stufft.io <mailto:donald at stufft.io>> wrote:
> 
>> On Feb 9, 2015, at 4:06 PM, Neil Girdhar <mistersheik at gmail.com <mailto:mistersheik at gmail.com>> wrote:
>> 
>> Hello all,
>> 
>> The updated PEP 448 (https://www.python.org/dev/peps/pep-0448/ <https://www.python.org/dev/peps/pep-0448/>) is implemented now based on some early work by Thomas Wouters (in 2008) and Florian Hahn (2013) and recently completed by Joshua Landau and me.
>> 
>> The issue tracker http://bugs.python.org/issue2292 <http://bugs.python.org/issue2292>  has  a working patch.  Would someone be able to review it?
>> 
> 
> I just skimmed over the PEP and it seems like it’s trying to solve a few different things:
> 
> * Making it easy to combine multiple lists and additional positional args into a function call
> * Making it easy to combine multiple dicts and additional keyword args into a functional call
> * Making it easy to do a single level of nested iterable "flatten".
> 
> I would say it's:
> * making it easy to unpack iterables and mappings in function calls
> * making it easy to unpack iterables  into list and set displays and comprehensions, and
> * making it easy to unpack mappings into dict displays and comprehensions.
> 
>  
> 
> Looking at the syntax in the PEP I had a hard time detangling what exactly it was doing even with reading the PEP itself. I wonder if there isn’t a way to combine simpler more generic things to get the same outcome.
> 
> Looking at the "Making it easy to combine multiple lists and additional positional args into a  function call" aspect of this, why is:
> 
> print(*[1], *[2], 3) better than print(*[1] + [2] + [3])?
> 
> That's already doable in Python right now and doesn't require anything new to handle it.
> 
> Admittedly, this wasn't a great example.  But, if [1] and [2] had been iterables, you would have to cast each to list, e.g.,
> 
> accumulator = []
> accumulator.extend(a) 
> accumulator.append(b)
> accumulator.extend(c)
> print(*accumulator)
> 
> replaces
> 
> print(*a, b, *c)
> 
> where a and c are iterable.  The latter version is also more efficient because it unpacks only a onto the stack allocating no auxilliary list.

Honestly that doesn’t seem like the way I’d write it at all, if they might not be lists I’d just cast them to lists:

print(*list(a) + [b] + list(c))

But if casting to list really is that big a deal, then perhaps a better solution is to simply make it so that something like ``a_list + an_iterable`` is valid and the iterable would just be consumed and +’d onto the list. That still feels like a more general solution and a far less surprising and easier to read one.


> 
> 
> Looking at the "making it easy to do a single level of nsted iterable 'flatten'"" aspect of this, the example of:
> 
> >>> ranges = [range(i) for i in range(5)]
> >>> [*item for item in ranges]
> [0, 0, 1, 0, 1, 2, 0, 1, 2, 3]
> 
> Conceptually a list comprehension like [thing for item in iterable] can be mapped to a for loop like this:
> 
> result = []
> for item in iterable:
>     result.append(thing)
> 
> However [*item for item in ranges] is mapped more to something like this:
> 
> result = []
> for item in iterable:
>     result.extend(*item)
> 
> I feel like switching list comprehensions from append to extend just because of a * is really confusing and it acts differently than if you just did *item outside of a list comprehension. I feel like the itertools.chain() way of doing this is *much* clearer.
> 
> Finally there's the "make it easy to combine multiple dicts into a function call" aspect of this. This I think is the biggest thing that this PEP actually adds, however I think it goes around it the wrong way. Sadly there is nothing like [1] + [2] for dictionaries. The closest thing is:
> 
> kwargs = dict1.copy()
> kwargs.update(dict2)
> func(**kwargs)
> 
> So what I wonder is if this PEP wouldn't be better off just using the existing methods for doing the kinds of things that I pointed out above, and instead defining + or | or some other symbol for something similar to [1] + [2] but for dictionaries. This would mean that you could simply do:
> 
> func(**dict1 | dict(y=1) | dict2)
> 
> instead of
> 
> dict(**{'x': 1}, y=2, **{'z': 3})
> 
> I feel like not only does this genericize way better but it limits the impact and new syntax being added to Python and is a ton more readable.


Honestly the use of * and ** in functions doesn’t bother me a whole lot, though i don’t see much use for it over what’s already available for lists (and I think doing something similarly generic for mapping is a better idea). What really bothers me is these parts:

* making it easy to unpack iterables  into list and set displays and comprehensions, and
* making it easy to unpack mappings into dict displays and comprehensions.

I feel like these are super wrong and if they were put in I’d probably end up writing a linter to disallow them in my own code bases.

I feel like adding a special case for * in list comprehensions breaks the “manually expanded” version of those. Switching from append to extend inside of a list comprehension because of a * doesn’t make any sense to me. I can’t seem to construct any for loop that mimics what this PEP proposes as [*item for item in iterable] without fundamentally changing the operation that happens in each loop of the list comprehension.


---
Donald Stufft
PGP: 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20150209/670805f3/attachment.html>


More information about the Python-Dev mailing list