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