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

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.

Numeric Ternary Expressions

( Numeric Expression 1 ) * ( Boolean Flag 1 ) + ( Numeric Expression 2 ) * ( Boolean Flag 2 ) + ...

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

Exhibit 1: Function with many if/else branches

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.

Exhibit 2: Function with two Numeric Ternary expressions

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 ifs 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).

A Note on LAR

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.

If/else One-Line Ternary Expression in Python

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_True if Condition else Expression_if_False

Expression_if_Condition1 if Condition1 else Expression_if_Condition2 if Condition2 else Expression_if_False

Exhibit 3: Where Ternary expressions wont work

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.

Exhibit 4: Ternary if/else

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)

Speed Benefits in Ternary Logic

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.

⚠️ **GitHub.com Fallback** ⚠️