49

I have this

bc = 'off'

if c.page == 'blog':
    bc = 'on'

print(bc)

Is there a more pythonic (and/or shorter) way of writing this in Python?

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
32423hjh32423
  • 3,048
  • 7
  • 44
  • 59
  • 9
    Do you *have* to use 'on' and 'off'? Could this info be repesented with just a boolean, I mean bc = (c.page == 'blog') is much better. – u0b34a0f6ae Aug 23 '09 at 21:19

6 Answers6

103

Shortest one should be:

bc = 'on' if c.page=='blog' else 'off'

Generally this might look a bit confusing, so you should only use it when it is clear what it means. Don't use it for big boolean clauses, since it begins to look ugly fast.

freiksenet
  • 3,569
  • 3
  • 28
  • 28
65

This is:

  1. definitely shorter
  2. arguably Pythonic (pre-Python 2.5, which introduced the controversial X if Z else Y syntax)
  3. questionably readable. With those caveats in mind, here it goes:

    bc = ("off","on")[c.page=="blog"]
    

EDIT: As per request, the generalized form is:

   result = (on_false, on_true)[condition]

Explanation: condition can be anything that evaluates to a Boolean. It is then treated as an integer since it is used to index the tuple: False == 0, True == 1, which then selects the right item from the tuple.

Arkady
  • 14,305
  • 8
  • 42
  • 46
  • Fascinating. As with another one above, could you generalize that a bit more and explain it? (As in ... you weren't kidding about #3). – lilbyrdie Aug 23 '09 at 20:46
  • `("off", "on")` is a tuple and `c.page=="blog"` evaluates to the index of the element that is being accessed – Otto Allmendinger Aug 23 '09 at 20:58
  • 3
    I wanted to vote down because it's really ugly, then I remembered that I used it myself. – u0b34a0f6ae Aug 23 '09 at 21:16
  • 4
    This is what I use when 2.5 isn't available; I find ‘and...or’ unacceptable as it reads weirdly and fails if the and-value is something that isn't truthy. The drawback of ‘(a, b)[cond]’ is that both a and b are evaluated, so you can't rely on shortcutting, which means you can't convert a construct like “'nothing' if item is None else item.name”. – bobince Aug 23 '09 at 22:20
  • I find this quite unreadable. I think if-else conditional expression is more readable. – Manish Mar 03 '11 at 12:03
  • 4
    I like it but compared to `X if Z else Y` is that `if Z` is false we only evaluate Y not X so it fails for example if key not in dictionary: `adict[ k ] if adict.__contains( k ) else k` – stefanB Aug 24 '12 at 02:22
  • @stefanB's observation does not matter in this particular case, but in the general case an important point. – Paul Draper Dec 07 '13 at 18:11
32

Well, not being a python guy please take this with a huge grain of salt, but having written (and, with more difficulty, read) a lot of clever code over the years, I find myself with a strong preference now for readable code. I got the gist of what your original code was doing even though I'm a nobody as a Python guy. To be sure, you could hide it and maybe impress a Python wonk or two, but why?

John Lockwood
  • 3,787
  • 29
  • 27
  • C people use i++. Pythonic if can be used as shorthand for obvious conditionals as i++ or i+= can be used as shorthand for obvious operations. – freiksenet Aug 23 '09 at 19:22
  • 1
    @freiksenet: I think a better comparison is using the ?: statements in other languages for short, simple -- inline -- conditionals. And, arguably, those can make the code more readable rather than cluttering it up with extra lines that add no functionality. – lilbyrdie Aug 23 '09 at 20:50
  • 4
    Just FYI, "Pythonic" is a term that promotes readability over terseness. – TM. Sep 21 '09 at 05:53
  • 2
    In general however, shorter code is more readable. You can overdo it, but all else being equal, I'd prefer fewer tokens in my code. In particular, if statements with side-effecting bodies are a pain to reason about because it's hard to skim: you can't know what they're touching without reading the entire thing. C's ternary or the inline `if` freiksenet mentions doesn't have these problems: it takes some getting used to, but it's a style much more amenable to maintenance than raw branches. – Eamon Nerbonne Jan 28 '11 at 10:19
  • I think the new conditional expression is quite readable. However, I completely agree that one must write for readability. – Manish Mar 03 '11 at 12:05
  • I tend to prefer the shorter code as it's easier to maintain as long as you don't abuse the construct. I just used this one today: instead of `if verbose: for value in list: process(value)"`, I used `for value in list if verbose else []: process(value)`. It takes a fraction of a second longer to parse if you're unfamiliar with the inline-if-else, but after that, it's shorter and uses one less indent. – Harvey Nov 25 '13 at 18:07
15

You could use an inline if statement:

>>> cpage = 'blog'
>>> bc = 'on' if cpage == 'blog' else 'off'
>>> bc
'on'
>>> cpage = 'asdf'
>>> bc = 'on' if cpage == 'blog' else 'off'
>>> bc
'off'

There's a bit of a writeup on that feature at this blog, and the relevant PEP is PEP308. The inline if statement was introduced in Python 2.5.

This one is less pythonic, but you can use and/or in this fashion:

>>> cpage = 'asdf'
>>> bc = (cpage == 'blog') and 'on' or 'off'
>>> bc
'off'
>>> cpage = 'blog'
>>> bc = (cpage == 'blog') and 'on' or 'off'
>>> bc
'on'

This one is used more often in lambda statements than on a line by itself, but the form

 A and B or C

is similar to

   if A:
       return B
   else:
       return C

The major caveat to this method (as PEP 308 mentions) is that it returns C when B is false.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Mark Rushakoff
  • 249,864
  • 45
  • 407
  • 398
  • 1
    Second one looks *really* confusing. Can you please expand answer to explain why it works like this? – freiksenet Aug 23 '09 at 18:43
  • 1
    Thanks for explanation. It still is very unreadable for me. I think first option is better. – freiksenet Aug 23 '09 at 19:21
  • The second option is what people used before we got the first one, on those occasions when cramming things io one line was more important than readability.. – John Fouhy Aug 23 '09 at 22:21
  • 1
    That second one surely gets a low score for readability. Not Pythonic. – Craig McQueen Aug 24 '09 at 00:38
  • 3
    The and/or approach *used* to be the Pythonic way of doing a conditional in a lambda, until PEP308. See the first two paragraphs of the PEP: http://www.python.org/dev/peps/pep-0308/ – Mark Rushakoff Aug 24 '09 at 02:14
4

Another possibility is to use a dict if you can compute the values outside of the function that accesses them (i.e. the values are static, which also addresses the evaluation issue in scrible's answer's comments).

want_bc = {True: "on", False: "off"}
# ...
bc = want_bc[c.page == "blog"]

I prefer this and/or the tuple indexing solutions under the general rubric of preferring computation to testing.

elp
  • 8,021
  • 7
  • 61
  • 120
user135331
  • 229
  • 1
  • 5
3

You can use,

a = b if c else d 

but if you are using a python version prior to 2.5,

bc = c.page == "blog" and "on" or "off"

can do the trick also.

Rapnar
  • 137
  • 11
M. Utku ALTINKAYA
  • 2,254
  • 23
  • 29