Search code examples
pythonconditional-statementscode-readability

Readability in nested conditional statements


I'm currently writing a program in Python to model projective geometry, and the congruence function for a projective point is looking rather nasty.

(for anyone interested, two projective points are congruent if they both lie on a single line passing through the origin.)

class Point(object):
    def __init__(self, a, b, c):
        self.coords = [ a, b, c ]

    def congruent(self, other):
        ratio = 0
        for i in range(3):
            if self.coords[i] != 0 and other.coords[i] != 0:
                if ratio is 0:
                    ratio = other.coords[i] / self.coords[i]
                elif ratio != other.coords[i] / self.coords[i]:
                    return False
            elif self.coords[i] != 0 or other.coords[i] != 0:
                return False
        return True

I'm new to Python, but I know that there's generally a "Pythonic" way to do everything. With that in mind, how would I go about making this more readable?


Solution

  • How about this:

    def congruent(self, other, eps=0.001):
        ratios = (c1 / c2 for c1, c2 in zip(self.coords, other.coords) if c1 or c2)
        try:
            first = next(ratios)
            return all(abs(ratio - first) < eps for ratio in ratios)
        except ZeroDivisionError:
            return False
    
    1. Prefer operating directly over elements instead of on indices if possible (zip is handy).
    2. The list comprehension gets all coord ratios for cases where either coordinate is non-zero. If both are, it's fine, and it gets excluded.
    3. The ZDE only happens if c1 is nonzero and c2 is zero, so that's a fail.
    4. At the end, we pass if all ratios are equal.

    Note: If you're not using Python 3, you should add from __future__ import division to the top of your file so that you don't get incorrect results for integer coordinate values.

    Edit: added short-circuiting and epsilon-comparison for float ratios per @JoranBeasley.