
Asterisks in Python - ingve
http://treyhunner.com/2018/10/asterisks-in-python-what-they-are-and-how-to-use-them/
======
zmmmmm
This is one of those areas where I find Python a little contradictory. About
50% of the time it's "explicit is better than implicit" and "there should be
one and only one good way" and then the other half of the time it's "here's
this cool feature for doing something you could do a different way that looks
like hieroglyphics and nobody understands but you should totally use it
because it's awesome!

~~~
pavlov
This is one of the things that gets me about Python.

It makes this big noise about being a super-friendly form of executable
pseudocode, but then you open any code example and the first thing you see is
two asterisks and the mysterious word "kwargs" (a Swedish dessert perhaps?).

I wouldn't mind except for all the haughty pretense about Python being a
language that doesn't do this sort of thing. Dear Python, get a grip, you're
just another ASCII-abusing interpreted monstrosity like Perl, you just decided
to make programmer life suck with whitespace instead.

~~~
yxhuvud
Aside: Swedish does have the word 'kvarg', which is a product made out of sour
milk. Some people eat it for breakfast but personally I can't stand it.

~~~
nickelcitymario
This is legitimately my favourite comment of the day.

------
th0ma5
I've been programming with Python for over a decade. I mostly understand, but
I do try to avoid when possible for maximum clarity. Expanding function
variables is fine and clear enough, but multiple levels deep in a
comprehension and it can get pretty thick to try and keep it all straight.
This article is nice that it covers all the patterns I've seen.

~~~
soVeryTired
Honest question: why would anyone make a list comprehension that's multiple
levels deep? Isn't the purpose of comprehensions to provide quick-and-dirty
inline for loops, where a full for loop is too verbose?

~~~
bjourne
People who come from math backgrounds often becomes infatuated with
complicated list comprehensions when they realize that they can be used as
set-builder notation. {(x,y) for x in range(10) for y in range(10) if y ==
2*x}

~~~
analog31
I use stuff like that. But my rule of thumb is that I try and make it readable
by breaking it up into multiple lines and using indentation. If I can't make
it readable, then I will consider another way of writing it. This is mainly
for self preservation, since I'm likely to be the person who has to read it
later.

------
bpchaps
Unrelated to the article: the 'x' on the newsletter popup doesn't work on
mobile. Makes the article very difficult to read. If the author is reading
this, you should fix that!

~~~
th
Thanks for reporting this issue!

------
cpsempek
> print(*more_numbers, sep=', ')

This alone makes me appreciate print as a function in py3.

~~~
sooheon
Now how much nicer would it be if even more things were functions, rather than
special syntax like here? This can be done with an `apply`.

~~~
nemetroid
I think the star syntax is a lot nicer than `apply`. Python actually had an
`apply` builtin, but it was deprecated in Python 2.3.

~~~
sooheon
Apply, and first class / higher order functions in general are good in
languages where they compose well. Python's * won't compose well.

~~~
quietbritishjim
To add a small footnote to the existing replies to this comment: A related,
but slightly different, function in the `functools` module (part of the Python
standard library) is the `partial` function. It is like apply() but returns a
function instead of calling the function and return its return value:

    
    
        f1 = partial(f, 1, 2)
        f2 = partial(f1, 3, 4)
        f2(5, 6)  # equivalent to f(1, 2, 3, 4, 5, 6)
    

It also supports kwargs.

------
reachtarunhere
The single * way to specify keyword only arguments is a total monstrosity.

~~~
masklinn
It's a logical and consistent outgrowth of the existence of *args, and having
(finally) allowed parameters after that (which would be keyword-only). I'm
sure you could make up other ways to do it, but not that they'd be any better.

------
tln
Whats with the / in the help for sorted?

    
    
        Help on built-in function sorted in module builtins:
    
        sorted(iterable, /, *, key=None, reverse=False)

~~~
dec0dedab0de
That is curious, it looks like it might just be a type-o in the documentation.
Since it is written in C and not python, it is not automatically generated.
But I don't know enough C to say for sure, maybe someone else can.

[https://github.com/python/cpython/blob/9dfa0fe587eae3626ffc9...](https://github.com/python/cpython/blob/9dfa0fe587eae3626ffc973680c6a17f35de3864/Python/bltinmodule.c#L2203)

~~~
jnwatson
Nope. It represents positional-only arguments. You can have positional-only
arguments for C function but not Python functions. Just a weird Python wart.

------
dec0dedab0de
my favorite cheat is using __locals() with string formatting something like
this:

    
    
      def example_cheater(color, flavor, age):
        
          template = 'I am {age} years old and I like to wear {color} hats and eat {flavor} icecream'
          return template.format(**locals())
    
    

Obviously a contrived example, and it can be argued that using locals() is not
very pythonic, but I think it makes the code look much nicer.

~~~
avbor
In python 3.6 and up, that becomes

    
    
        def example_cheater(color, flavor, age):
          return f'I am {age} years old and I like to wear {color} hats and eat {flavor} icecream'
    

Even nicer!

~~~
sys_64738
Does this trip up pylint?

~~~
aportnoy
Trips up Vim's syntax highlighter.

------
runarberg
So this is like the rest and spread operator in javascript (`...`).

An interesting observation is that while python has separate operators for
lists ( * ) and dictionaries ( * * )—javascript has only `...`.

> You need to be careful when using __multiple times though. Functions in
> Python can’t have the same keyword argument specified multiple times, so the
> keys in each dictionary used with __must be distinct or an exception will be
> raised.

This is not an issue in javascript, the unpacked object with repeated keys
takes the value of the last one.

    
    
        {...{a: 1}, ...{a: 2}}
        // => {a: 2}
    

edit: formatting

~~~
kroltan
I suppose both observations are reminiscent of the fact that Python has
language-level support for named parameters. Given that named and sequential
parameters are treated differently everywhere in Python (sequential params
must be provided, keyword args are optional, amongst other things), it would
be extremely surprising behaviour if the run-time type of the value being
spread determined what kind of spread happened.

In JavaScript, the surrounding syntatical context solely decides if it's an
object or array splat. But since Python has 2 kinds of parameters, to preserve
obviousness when reading code using splats, they went with 2 operators for
each parameter kind, and then separated list and dictionary splat analogously.

~~~
int_19h
> it would be extremely surprising behaviour if the run-time type of the value
> being spread determined what kind of spread happened.

The other reason to distinguish * and double-* is because * works with any
iterable, and dicts are themselves iterables (of their keys). So you can
actually use * on dicts, it just does something different:

    
    
       >>> d = {'a': 1, 'b' : 2}
       >>> [*d]
       ['a', 'b']
    

You could arguably say that only * makes sense here since it's a list context.
But then, as you say, function calls would still be ambiguous because they
support both positional and named arguments. This keeps it all unambiguously
consistent in all contexts.

------
keyle
Side note... Why does everything needs to be bold on that page? It made
reading it very difficult.

~~~
th
I'm curious about what browser you're using.

Side note: I typically bold many sentences in my articles, but this is the
first article in a while where I actually bolded nearly no words at all. I
wonder what my other articles look like in your browser.

