Ternary Operations - KeithChamberlain/PythonJourney GitHub Wiki
Ternary Expressions, combined with boolean flags, are how Logical Algebraic Recoding (LAR) works, only LAR is vectorized where vectorization is supported. When used in control flow rather than in recoding data, we simply have ternary expressions.
Ternary expressions are a combination of boolean flags either multiplied by numeric expressions to generate super fast if/else logic, or a ternary one-line if/else
statement that can be used with numeric data and text. One benefit of the ternary if/else
is that it is more readable than the combined numeric ternary expressions. The benefit of the numeric ternary expressions is that they work in any programming environment, including python, R, Excel, VBA, SAS, ArcGIS Pro and Tableau to name a few.
The boolean flags work in combination to isolate which numeric expression(s) need to be ran. For example, in order to turn the following if: else:
statement into a ternary expression...
def isPrime(number):
if number <= 1:
return False
elif number == 2:
return True
elif number >= 3:
for i in range(3, int(n**(1/2)+1), 2):
if not (n % i != 0):
return False
return False
Exhibit 1 has 10 lines of code, four if branches and four levels of indenting. Sure the code is readable, but it starts to take up vertical real estate with the conditions. Consider the replacement below.
def is_prime(n):
primeFlag = 1 * (n >= 3 and n % 2 !=0) + 1 * (n == 2)
if n > 2:
for i in range(3, int(n**(1/2)+1), 2):
primeFlag = 1 * (n % i != 0)
if primeFlag == 0:
break
return bool(primeFlag)
Exhibit 2 has the same number of indenting, 8 lines of code and has two fewer if
s replaced by the conditional numeric ternary expressions that are coded to return 1 or 0. The first numeric ternary expression replaces 3 lines of code embellished in the if: else:
clauses. A single additional if statement evaluates to break the loop if needed. At the end of the function, the 1 or 0 is cast to boolean to match the datatype in isPrime() from Exhibit 1. I find the function in Exhibit 2 to be more compact and readable.
A note of caution with numeric ternary expressions... All of the boolean flags run. There are no boolean flags that are isolated from the rest, or run at the exclusion of the rest. Also, if a return
or a break
is needed, a traditional if: else:
will be needed to isolate the keywords because python doesn't know what to do with break * (condition)
, or "Hello World" * (Condition)
.
LAR is a special case of Boolean Flag Ternary Logic. For LAR, the expressions to isolate are the actual contrast code or dummy code values, and the boolean flags are the (data == "value") comparison.
The one-line if/else
ternary expressions do not just have to evaluate a numeric expressions. They can return any object, which gives this type of expression advantages over numeric ternary expressions. However, one disadvantage is that these expressions are less compact.
Expression_if_Condition1 if Condition1 else Expression_if_Condition2 if Condition2 else Expression_if_False
def cInt(arg):
"""
cInt() attempts to coerce 'arg' to <type: int>.
arg:
Base python objects have been tested.
Objects that can be coerced to integer are.
Empty objects, type: None, and '' are set to 0.
Non-empty objects that cannot be coerced to integer
are set to 1.
return:
arg coerced to <type: int> or tmp as the original object type.
"""
tmp = arg
try:
arg = int(float(arg))
if arg == float(tmp):
return arg
else:
return tmp
except:
return tmp
When using return
, break
, try
, except
, etc., those keywords need to be isolated by traditional if: else:
statements.
def xor(*args, parity = True):
"""
xor() attempts to return the exclusive or operation on *args.
args:
Arbitrary list of arguments to be coerced to <type: int>, 0 or 1.
parity:
Switch to turn off parity behavior such that 1^1^1 = 0 if 3 bits
are supplied, 1^1^1^1 = 0 if 4 bits are supplied, etc.
return:
<type: int>; the exclusive or operation on args.
"""
exOr = False
checker = list()
for arg in args:
arg = cInt(arg)
# Ternary if/else in setting exOr
exOr ^= arg if type(arg) == int else bool(arg)
checker.append(bool(arg))
# Ternary if/else in setting exOr
exOr = 0 if all(checker) and not parity else exOr
return int(exOr)
The ternary operation first used is arg if type(arg) == int else bool(arg)
. If this expression were written in traditional if: else:
logic, it would look like the following, which looses an additional 3 lines of code!
if type(arg) == int:
exOr ^= arg
else:
exOr ^= bool(arg)
Ternary expressions are faster than conditional if/then logic. If you're not convinced, replace your if/elif/else combinations with a ternary expression and clock the difference.