tverrbjelke
tverrbjelke

Reputation: 1294

Pythonic way of Refactoring longer "One line" if-condition-assignment ( ternary If )

My current code uses tenary assignments One line if-condition-assignment, but with more verbose identifyers it is easily passing line length limits.

Since I am not yet into pythonic coding, I would be glad to see some refactoring suggestions.

for label in range(num_labels):
    d_tresh = drop_treshold[label] if type(drop_treshold) == numpy.ndarray) else drop_treshold     
    r_tresh1 = relabel_treshold1[label] if type(relabel_treshold1) == numpy.ndarray else relabel_treshold1
    r_tresh2 = relabel_treshold2[label] if type(relabel_treshold2) == numpy.ndarray else relabel_treshold2

Using locally short variable names seems part of solution, but I like to have more explanatory function arguments. Hm. So dramatically shortening function argument names results in (for me) unreadable code.

for l in range(n_labels):
    t0 = d_t[l] if type(d_t) == numpy.ndarray) else d_t     
    t1 = r_t1[l] if type(r_t1) == numpy.ndarray else r_t1
    t2 = r_t2[l] if type(r_t2) == numpy.ndarray else r_t1

So shall I resort to multi line if - else assignments? It will stretch and bloat the simple logic dramatically.

for label in range(num_labels):
    if type(drop_treshold) == numpy.ndarray):
        d_tresh = drop_treshold[label]
    else:
        d_tresh = drop_treshold     

    if type(relabel_treshold1) == numpy.ndarray:
        r_tresh1 = relabel_treshold1[label] 
    else:
        d_tresh = relabel_treshold1

    if type(relabel_treshold2) == numpy.ndarray:
        r_tresh2 = relabel_treshold2[label] 
    else:
        d_tresh = relabel_treshold2

(Surely I (sh/)could also refactor the whole code around the shown example... This example snippet comes from function with arguments, which could be scalar float/int or 1D numpy.array. If it is an array it will apply each item to each label, just being plain scalar it will apply it globally to all labels) But here again how is the pythonic way? When to start to refactor more exhaustively and when to stay put - because it works?

Upvotes: 1

Views: 135

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1121834

The issue here is that you are repeating yourself for each variable; indexing if the object is an array, otherwise using the object directly. I'd use a utility function here:

def threshold_label(ob, i):
    # the threshold label can be a scalar or an array
    return ob[i] if isinstance(ob, numpy.ndarray) else ob

for label in range(num_labels):
    d_tresh = threshold(drop_treshold, label)
    r_tresh1 = threshold(relabel_treshold1, label)
    r_tresh2 = threshold(relabel_threshold2, label)

Note that I used isinstance() to test for the type of each object, see What are the differences between type() and isinstance()?

Another option is to not test for arrays each iteration. Test once, and if it is not an array, turn the scalar float or int value into a sequence of the expected length:

# turn scalar labels into sequences for easy iteration
if not isinstance(drop_threshold, numpy.ndarray):
    drop_threshold = [drop_threshold] * num_labels
if not isinstance(relabel_treshold1, numpy.ndarray):
    relabel_treshold1 = [relabel_treshold1] * num_labels
if not isinstance(drop_threshold, numpy.ndarray):
    relabel_treshold2 = [relabel_treshold2] * num_labels

or, again, with a helper function:

def ensure_sequence(ob, cnt):
    # turn a scalar label value into a sequence if needed
    return [ob] * cnt if not isinstance(ob, numpy.ndarray) else ob

drop_threshold = ensure_sequence(drop_threshold, num_labels)
relabel_treshold1 = ensure_sequence(relabel_treshold1, num_labels)
relabel_treshold2 = ensure_sequence(relabel_treshold2, num_labels)

at which point you can use zip() to iterate:

labels = zip(drop_threshold, relabel_threshold1, relabel_threshold2)
for d_thresh, r_thresh1, r_thresh2 in labels:
    # ...

Upvotes: 2

Related Questions