Re: One liners

Discussion in 'Python' started by Michael Torrie, Dec 7, 2013.

  1. 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.
    Michael Torrie, Dec 7, 2013
    #1
    1. Advertising

  2. 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
    Steven D'Aprano, Dec 7, 2013
    #2
    1. Advertising

  3. On Sat, Dec 7, 2013 at 1:28 PM, Steven D'Aprano
    <> 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
    Chris Angelico, Dec 7, 2013
    #3
  4. Michael Torrie

    Roy Smith Guest

    In article <52a287cb$0$30003$c3e8da3$>,
    Steven D'Aprano <> 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.docmap,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?
    Roy Smith, Dec 7, 2013
    #4
  5. On Sat, Dec 7, 2013 at 2:27 PM, Roy Smith <> 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.docmap,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
    Chris Angelico, Dec 7, 2013
    #5
  6. 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
    Steven D'Aprano, Dec 7, 2013
    #6
  7. 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.
    Jussi Piitulainen, Dec 7, 2013
    #7
  8. Michael Torrie

    Rotwang Guest

    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?
    Rotwang, Dec 7, 2013
    #8
  9. 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
    Steven D'Aprano, Dec 7, 2013
    #9
  10. 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
    Jussi, but your examples are just fine for correctness and readability.
    I think Dan's gripes come when cleverness is taken to the extreme.
    Michael Torrie, Dec 7, 2013
    #10
  11. On 12/06/2013 08:27 PM, Roy Smith wrote:
    > In article <52a287cb$0$30003$c3e8da3$>,
    > Steven D'Aprano <> 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 []


    This is a generator expressions, and ternary ifs are common and often
    needed in generator expressions.

    > --------------------------------------------------
    > c2s = compids2songs(set(targets.keys()) |
    > set.union(*map(set,targets.itervalues())),self.docmap,self.logger) if
    > targets else {}


    I suspect the ternary distracted you on this one. The ternary here is
    needed because if targets is None the expression fails. This part
    anyway is a common idiom.

    The rest is basically making a set (list of unique items only) of the
    combined keys and values from the "targets" dictionary. Now I'm not
    sure why the programmer needs do this, but nevertheless that's what it's
    doing. set.union is used because that can iterate over a list of sets,
    which is what the map returns. I suppose they could have done this, but
    it wouldn't be much clearer unless you knew what sets, map and
    itervalues do:

    if targets:
    c2s = compids2songs(
    set(targets.keys()) |
    set.union(*map(set,targets.itervalues())),
    self.docmap,
    self.logger )
    else:
    c2s = {}

    In any case the ternary operator isn't really the part you were
    complaining about. Personally if I needed to do this particular
    operation a lot (combine keys and values into a set), I'd write a
    function that returned the set. Still can't avoid the ternary, though,
    unless you made compids2songs a little smarter (and we don't know what
    compids2songs does with an empty set):

    def dict_keys_and_values_set (some_dict):
    return set(some_dict.keys()) |
    set.union(*map(set,some_dict.itervalues()))

    c2s = compids2songs( dict_keys_and_values_set(targets) ) if targets else {}

    or I suppose you could o this:

    c2s = {}
    if targets: c2s = compids2songs( dict_keys_and_values_set(targets) )

    Just a matter of taste.

    > --------------------------------------------------
    > code = 2 if (pmp3,paac)==(mmp3,maac) else 3 if any(x is None for x in
    > (pmp3,paac,mmp3,maac)) else 4
    > --------------------------------------------------


    This one probably could stand to be reworked for sure! A standard if
    block would be much clearer. Definitely an example of a programmer
    thinking he was clever... maybe a git bisect could identify the author
    and we can shame him.

    > Anybody else have some fun ternary abuse examples?


    Only the last one seems to be problematic to me.
    Michael Torrie, Dec 7, 2013
    #11
  12. On 12/07/2013 09:56 AM, Michael Torrie wrote:
    >> extracols = sorted(set.union(*(set(t.data.keys()) for t in tracks))) if
    >> tracks else []

    >
    > This is a generator expressions, and ternary ifs are common and often
    > needed in generator expressions.


    Oops. This is not a generator expression at all!
    Michael Torrie, Dec 7, 2013
    #12
  13. Michael Torrie

    rusi Guest

    On Saturday, December 7, 2013 10:26:04 PM UTC+5:30, Michael Torrie wrote:
    > On 12/06/2013 08:27 PM, Roy Smith wrote:
    > > Steven D'Aprano 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 []


    > This is a generator expressions, and ternary ifs are common and often
    > needed in generator expressions.


    > > --------------------------------------------------
    > > c2s = compids2songs(set(targets.keys()) |
    > > set.union(*map(set,targets.itervalues())),self.docmap,self.logger) if
    > > targets else {}


    > I suspect the ternary distracted you on this one. The ternary here is
    > needed because if targets is None the expression fails. This part
    > anyway is a common idiom.


    > The rest is basically making a set (list of unique items only) of the
    > combined keys and values from the "targets" dictionary. Now I'm not
    > sure why the programmer needs do this, but nevertheless that's what it's
    > doing. set.union is used because that can iterate over a list of sets,
    > which is what the map returns. I suppose they could have done this, but
    > it wouldn't be much clearer unless you knew what sets, map and
    > itervalues do:


    > if targets:
    > c2s = compids2songs(
    > set(targets.keys()) |
    > set.union(*map(set,targets.itervalues())),
    > self.docmap,
    > self.logger )
    > else:
    > c2s = {}


    > In any case the ternary operator isn't really the part you were
    > complaining about. Personally if I needed to do this particular
    > operation a lot (combine keys and values into a set), I'd write a
    > function that returned the set. Still can't avoid the ternary, though,
    > unless you made compids2songs a little smarter (and we don't know what
    > compids2songs does with an empty set):


    > def dict_keys_and_values_set (some_dict):
    > return set(some_dict.keys()) |
    > set.union(*map(set,some_dict.itervalues()))


    > c2s = compids2songs( dict_keys_and_values_set(targets) ) if targets else {}


    > or I suppose you could o this:


    > c2s = {}
    > if targets: c2s = compids2songs( dict_keys_and_values_set(targets) )


    > Just a matter of taste.


    > > --------------------------------------------------
    > > code = 2 if (pmp3,paac)==(mmp3,maac) else 3 if any(x is None for x in
    > > (pmp3,paac,mmp3,maac)) else 4
    > > --------------------------------------------------


    Just trying to rewrite that in a way which I try to use for long if-exprs

    code = 2 if (pmp3,paac)==(mmp3,maac) else
    3 if any(x is None for x in (pmp3,paac,mmp3,maac)) else
    4


    > This one probably could stand to be reworked for sure! A standard if
    > block would be much clearer. Definitely an example of a programmer
    > thinking he was clever... maybe a git bisect could identify the author
    > and we can shame him.



    The logic for writing (and hopefully reading) it this way is like this:

    In math we often have equations that are defined by cases -- typically typeset
    with a large curly bracket. Those else's hanging at the end are to be read as
    a signal to read this whole expr and though under a big curly brace.
    rusi, Dec 7, 2013
    #13
  14. Rotwang writes:

    > 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?


    My motivation is that the "one-armed if" highlights the condition,
    together with the fact that nothing is changed unless the condition
    holds. That said, I'm also fine with the forms you use.

    On another tangent, I wish people called the conditional expression
    the conditional expression. The number of its slots is about the least
    salient property of the thing. :)
    Jussi Piitulainen, Dec 7, 2013
    #14
  15. Michael Torrie

    Terry Reedy Guest

    On 12/7/2013 11: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?


    The 'extra' line is not necessary, as one can write

    if tracks is None: tracks = [] # or something

    I prefer this because it exactly expresses what one want done. The other
    branch

    else: tracks = tracks

    is superfluous and to me unaesthetic.

    --
    Terry Jan Reedy
    Terry Reedy, Dec 7, 2013
    #15
  16. Michael Torrie

    Rotwang Guest

    On 07/12/2013 16:25, Steven D'Aprano wrote:
    > 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 :)


    Thanks, and likewise to everyone else who replied.
    Rotwang, Dec 8, 2013
    #16
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Curly Joe

    Re: Clever One Liners

    Curly Joe, Jul 3, 2003, in forum: Python
    Replies:
    0
    Views:
    482
    Curly Joe
    Jul 3, 2003
  2. holger krekel

    Re: Clever One Liners

    holger krekel, Jul 3, 2003, in forum: Python
    Replies:
    0
    Views:
    836
    holger krekel
    Jul 3, 2003
  3. vj
    Replies:
    0
    Views:
    291
  4. kj
    Replies:
    2
    Views:
    411
    unayok
    Jun 18, 2009
  5. Larry
    Replies:
    1
    Views:
    99
    Martien Verbruggen
    Feb 3, 2005
Loading...

Share This Page