Defending the ternary operator

Andrew Dalke adalke at mindspring.com
Sat Feb 8 16:52:57 EST 2003


I'll apologize right now and say that yes, I know I'm looking at the
details and not at the, ahem, larger picture.  Call me a pointalist ;)

But I do like examples, so thanks for doing this!

Andrew Koenig:
> EXAMPLE 1:
>
>         p = <null-terminated string>;
>         q = <fixed-length char array>;
>         for (i = 0; i < sizeof(<fixed-length char array>); i++) {
>             if (*q++ != (*p? *p++: ' ')) {
>                    /* Complain that the two strings aren't equal */
>             }
>         }

For this case in Python,

  if q != p + " " * (q_size - len(p)):
    complain

or if you don't like the !=, use "startswith".  Or if you don't
like the " "*value use a format string

  format_string = "%-" + len(q_size) + "s"
  ...
  if not q.startswith(format_string % p)

or

  if q[:len(p)] != p or q[len(p):q_size].strip() != "":
    complain



> EXAMPLE 2:
>
>         mode = cvlong(param, strlen(param), 8) |
>             (c == 'c'? S_IFCHR: S_IFBLK);

Perhaps  {"c": S_IFCHR}.get(c, S_IFBLK) ?

The dictionary approach has the advantage of being easily
extendable as well.

> EXAMPLE 3:
>
>         fprintf(tf, "%c %#o %d %d ",
>            mode == S_IFBLK? 'b': 'c',
>            buf->st_mode & 07777,
>            major(buf->st_rdev),
>            minor(buf->st_rdev));

  {S_IFBLK: "b"}.get(mode, "c")

> EXAMPLE 4:
>
>        fprintf(stderr, "package %s\n",
>            strcmp(pack->iname, instr)? pack->ename: instr);

This is a place where I would have done
    if pack.iname == instr:
      package_name = instr
   else:
     package_name = pack.ename
   print >> sys.stderr, "package", package_name

I think even with an if/else expression, introducing the
variable name helps describe what's going on.

> EXAMPLE 5:
>
>        fprintf(file,
>            *p >= '0' && *p <= '7'? "\\%.3o": "\\%o",
>            c);

I assume in Python this would have a string 's' and an index 'i'.
What about
escape_codes = dict.from_keys("01234567", \\%.3o)
...
    file.write(escape_codes.get(s[i:i+1], "\\%o") % c)

(The s[i:i+1] is in case i is past the end of string.)

> EXAMPLE 6:
>
>         p = (s == NULL)? str: s;

Hmm.  Let's assume that s can be None or a string.  If it can't
be the empty string, then

    p = s or str

If it can be the empty string, the above fails since "" is a
false value.  In which case I would use

   p = s
   if p is None:
      p is str

and not use the [s, str][s is None] hack.

> EXAMPLE 7:
>
> Here, hrout and mrout are functions:
>
>         r = (*(line[0] == '.'? mrout: hrout)) (line, out);

Again, a table would work.

   r = ( {".": mrout}.get(line[0][:1], hrout) )(line, out)

but I would more likely have done

   if line[0][:1] == ".":
    f = mrout
  else:
    f = hrout
  f(line, out)

If there were more characters to dispatch on then the table
lookup could be extended to

  r = ( {".": mrout,
           "x": xrout,
           "t": trout}.get(line[0][:1], hrout) )(line, out)

but if I did a few of these I would have had

class DefaultLookup:
  def __init__(self, table, default):
    self.table = table
    self.default = default
  def __getitem__(self, item):
    return self.table.get(item, self.default)

and hidden the strangeish get(..., hrout) part behind a

dispatch_table = DefaultLookup({".": .... trout}, hrout)
 ...
r = dispatch_table[line[0][:1]](line, out)

> So... I used ?: 7 times in 2800 lines, or once every 400 lines or so.
>
>
> Looking back on these examples, I find that in the first five of them,
> the use of ?: makes the code easier to understand, even after 20 years.

For #1, I think a more Pythonic solution exists.

For #2, #3, and #7, I used a dict lookup, but in the simple binary
cases used I think tht approch is heavy handed and a ternary operator
would be clearer.

For #4, I think having the intermediate variable helps a bit
with explaining what is going on.

For #5, I think a table lookup is a bit better, again, partially
because it introduces a name.

For #6, depending on the situation, I think the 'x or y' operator
may get the solution.  Otherwise I think the ternary operator is
clearer.

So I would suggest that only 4 of those scan better with a
ternary operator.

                    Andrew
                    dalke at dalkescientific.com






More information about the Python-list mailing list