Velocity Reviews > Re: One liners

# Re: One liners

Michael Torrie
Guest
Posts: n/a

 12-07-2013
On 12/06/2013 05:14 PM, Dan Stromberg wrote:
> I'm thinking mostly of stackoverflow, but here's an example I ran into (a
> lot of) on a job:
>
> somevar = some_complicated_thing(somevar) if
> some_other_complicated_thing(somevar) else somevar
>
> Would it really be so bad to just use an if statement? Why are we
> assigning somevar to itself? This sort of thing was strewn across 3 or 4
> physical lines at a time.

You're right that a conventional "if" block is not only more readable,
but also faster and more efficient code. Sorry you have to deal with
code written like that! That'd frustrate any sane programmer. It might
bother me enough to write code to reformat the program to convert that
style to something sane! There are times when the ternary (did I get
that right?) operator is useful and clear.

Steven D'Aprano
Guest
Posts: n/a

 12-07-2013
On Fri, 06 Dec 2013 17:20:27 -0700, Michael Torrie wrote:

> On 12/06/2013 05:14 PM, Dan Stromberg wrote:
>> I'm thinking mostly of stackoverflow, but here's an example I ran into
>> (a lot of) on a job:
>>
>> somevar = some_complicated_thing(somevar) if
>> some_other_complicated_thing(somevar) else somevar
>>
>> Would it really be so bad to just use an if statement? Why are we
>> assigning somevar to itself? This sort of thing was strewn across 3 or
>> 4 physical lines at a time.

Unless you're embedding it in another statement, there's no advantage to
using the ternary if operator if the clauses are so large you have to
split the line over two or more lines in the first place. I agree that:

result = (spam(x) + eggs(x) + toast(x)
if x and condition(x) or another_condition(x)
else foo(x) + bar(x) + foobar(x))

is probably better written as:

if x and condition(x) or another_condition(x):
result = spam(x) + eggs(x) + toast(x)
else:
result = foo(x) + bar(x) + foobar(x)

The ternary if is slightly unusual and unfamiliar, and is best left for
when you need an expression:

ingredients = [spam, eggs, cheese, toast if flag else bread, tomato]

As for your second complaint, "why are we assigning somevar to itself", I
see nothing wrong with that. Better that than a plethora of variables
used only once:

# Screw this for a game of soldiers.
def function(arg, param_as_list_or_string):
if isinstance(param_as_list_or_string, str):
param = param_as_list_or_string.split()
else:
param = param_as_list_or_string

# Better.
def function(arg, param):
if isinstance(param, str):
param = param.split()

"Replace x with a transformed version of x" is a perfectly legitimate
technique, and not one which ought to be too hard to follow.

> You're right that a conventional "if" block is not only more readable,
> but also faster and more efficient code.

Really? I don't think so. This is using Python 2.7:

[steve@ando ~]\$ python -m timeit --setup="flag = 0" \
> "if flag: y=1
> else: y=2"

10000000 loops, best of 3: 0.0836 usec per loop

[steve@ando ~]\$ python -m timeit --setup="flag = 0" "y = 1 if flag else 2"
10000000 loops, best of 3: 0.0813 usec per loop

There's practically nothing between the two, but the ternary if operator
is marginally faster.

As for readability, I accept that ternary if is unusual compared to other
languages, but it's still quite readable in small doses. If you start
chaining them:

result = a if condition else b if flag else c if predicate else d

you probably shouldn't.

--
Steven

Chris Angelico
Guest
Posts: n/a

 12-07-2013
On Sat, Dec 7, 2013 at 1:28 PM, Steven D'Aprano
<(E-Mail Removed)> wrote:
> As for readability, I accept that ternary if is unusual compared to other
> languages...

All the C-derived ternary operators put the condition first, but
Python puts the condition in the middle. What that does for
readability I don't really know. Which is more important?

ChrisA

Roy Smith
Guest
Posts: n/a

 12-07-2013
In article <52a287cb\$0\$30003\$c3e8da3\$(E-Mail Removed) om>,
Steven D'Aprano <(E-Mail Removed)> wrote:

> The ternary if is slightly unusual and unfamiliar

It's only unusual an unfamiliar if you're not used to using it
Coming from a C/C++ background, I always found the lack of a ternary
expression rather limiting. There was much rejoicing in these parts
when it was added to the language relatively recently. I use them a lot.

On the other hand, I found list comprehensions to be mind-bogglingly
confusing when I first saw them (read: slightly unusual and unfamiliar).
It took me a long time to warm up to the concept. Now I love them.

> As for readability, I accept that ternary if is unusual compared to other
> languages, but it's still quite readable in small doses. If you start
> chaining them:
>
> result = a if condition else b if flag else c if predicate else d
>
> you probably shouldn't.

That I agree with (and it's just as true in C as it is in Python).

Just for fun, I took a look through the Songza code base. 66 kloc of
non-whitespace Python. I found 192 ternary expressions. Here's a few
of the more bizarre ones (none of which I consider remotely readable):

--------------------------------------------------
extracols = sorted(set.union(*(set(t.data.keys()) for t in tracks))) if
tracks else []
--------------------------------------------------
c2s = compids2songs(set(targets.keys()) |
set.union(*map(set,targets.itervalues())),self.doc map,self.logger) if
targets else {}
--------------------------------------------------
code = 2 if (pmp3,paac)==(mmp3,maac) else 3 if any(x is None for x in
(pmp3,paac,mmp3,maac)) else 4
--------------------------------------------------

Anybody else have some fun ternary abuse examples?

Chris Angelico
Guest
Posts: n/a

 12-07-2013
On Sat, Dec 7, 2013 at 2:27 PM, Roy Smith <(E-Mail Removed)> wrote:
> --------------------------------------------------
> extracols = sorted(set.union(*(set(t.data.keys()) for t in tracks))) if
> tracks else []
> --------------------------------------------------
> c2s = compids2songs(set(targets.keys()) |
> set.union(*map(set,targets.itervalues())),self.doc map,self.logger) if
> targets else {}

Easy rewrites:

extracols = tracks and sorted(set.union(*(set(t.data.keys()) for t in tracks)))

Assumes that tracks is a list, which it most likely is given the
context. Parallel with the other.

ChrisA

Steven D'Aprano
Guest
Posts: n/a

 12-07-2013
On Fri, 06 Dec 2013 22:27:00 -0500, Roy Smith wrote:

> Just for fun, I took a look through the Songza code base. 66 kloc of
> non-whitespace Python. I found 192 ternary expressions. Here's a few
> of the more bizarre ones (none of which I consider remotely readable):
>
> --------------------------------------------------
> extracols = ( sorted(set.union(*(set(t.data.keys()) for t in tracks)))
> if tracks else [] )

[extra parentheses added so I can split the line over two]

I actually don't find that too bad, readability-wise. Not ideal, but I
can follow it. However, I wonder why t.data.keys() is converted to a set
before being unpacked? Even assuming that data.keys are not necessarily
unique, wouldn't building the union make them so?

extracols = ( sorted(set.union(*(t.data.keys()) for t in tracks)))
if tracks else [] )

Also, you can get rid of the `if tracks` check altogether by using a
bound method instead of an unbound method:

extracols = sorted(set().union(*(t.data.keys()) for t in tracks))

ought to work even if tracks is empty.

--
Steven

Jussi Piitulainen
Guest
Posts: n/a

 12-07-2013
Steven D'Aprano writes:

> On Fri, 06 Dec 2013 22:27:00 -0500, Roy Smith wrote:
>
> > Just for fun, I took a look through the Songza code base. 66 kloc of
> > non-whitespace Python. I found 192 ternary expressions. Here's a few
> > of the more bizarre ones (none of which I consider remotely readable):
> >
> > --------------------------------------------------
> > extracols = ( sorted(set.union(*(set(t.data.keys()) for t in tracks)))
> > if tracks else [] )

>
> [extra parentheses added so I can split the line over two]
>
> I actually don't find that too bad, readability-wise. Not ideal, but I
> can follow it. However, I wonder why t.data.keys() is converted to a set
> before being unpacked? Even assuming that data.keys are not necessarily
> unique, wouldn't building the union make them so?
>
> extracols = ( sorted(set.union(*(t.data.keys()) for t in tracks)))
> if tracks else [] )
>
>
> Also, you can get rid of the `if tracks` check altogether by using a
> bound method instead of an unbound method:
>
> extracols = sorted(set().union(*(t.data.keys()) for t in tracks))
>
> ought to work even if tracks is empty.

I suspect in the original code tracks could be None (or False) and
then the part of the code that bombs is 'for t in tracks' because
tracks is not iterable.

One could write [f(t) for t in tracks or []] and it wouldn't be blamed
on a binary operator - or maybe it would - but I suspect the cleaner
design would be to first make sure that an empty tracks is an empty
list (or an empty whatever-it-needs-to-be):

if tracks is None:
tracks = []
...
extracols = sorted(set.union(*(t.data.keys()) for t in tracks))

Still, is t.data.keys() a collection of sets? Maybe frozensets? And
then their unions are being sorted by set inclusion, which is not a
total order. Does sorted work correctly with a partial order? I don't
think it does.

Rotwang
Guest
Posts: n/a

 12-07-2013
On 07/12/2013 12:41, Jussi Piitulainen wrote:
> [...]
>
> if tracks is None:
> tracks = []

Sorry to go off on a tangent, but in my code I often have stuff like
this at the start of functions:

tracks = something if tracks is None else tracks

or, in the case where I don't intend for the function to be passed
non-default Falsey values:

tracks = tracks or something

Is there any reason why the two-line version that avoids the ternary
operator should be preferred to the above?

Steven D'Aprano
Guest
Posts: n/a

 12-07-2013
On Sat, 07 Dec 2013 16:13:09 +0000, Rotwang wrote:

> On 07/12/2013 12:41, Jussi Piitulainen wrote:
>> [...]
>>
>> if tracks is None:
>> tracks = []

>
> Sorry to go off on a tangent, but in my code I often have stuff like
> this at the start of functions:
>
> tracks = something if tracks is None else tracks
>
> or, in the case where I don't intend for the function to be passed
> non-default Falsey values:
>
> tracks = tracks or something
>
> Is there any reason why the two-line version that avoids the ternary
> operator should be preferred to the above?

Only if you need to support Python 2.4, which doesn't have the ternary if
operator

--
Steven

Michael Torrie
Guest
Posts: n/a

 12-07-2013
On 12/07/2013 09:13 AM, Rotwang wrote:
> On 07/12/2013 12:41, Jussi Piitulainen wrote:
>> [...]
>>
>> if tracks is None:
>> tracks = []

>
> Sorry to go off on a tangent, but in my code I often have stuff like
> this at the start of functions:
>
> tracks = something if tracks is None else tracks
>
> or, in the case where I don't intend for the function to be passed
> non-default Falsey values:
>
> tracks = tracks or something
>
> Is there any reason why the two-line version that avoids the ternary
> operator should be preferred to the above?

I think for such a short operation, and for a common need like this,
what you do is fine. Personally I prefer the part you quoted from
I think Dan's gripes come when cleverness is taken to the extreme.