First, it should be noted that this is the part of Python that describes operational expressions, not Python expressions.

You can refer to the Python documentation 10.3.1. Mapping Operators to Functions section for information on what an operational expression is in Python, and this is what you need to pass in.

A simple question

The title is as follows:

Given a list of real numbers and an interval, find the interval part.

There are two variables in this problem, one is a list of real numbers, and one is an interval. The interval includes several cases:

  • Left open right away
  • Left on the right off
  • Left closed right away
  • Left open right away

Due to the existence of various situations in the interval, it is impossible to describe this interval in a fixed form.

If the left boundary is A, the right boundary is B, and a variable in the list is X, then the interval relationship is converted to:

  • (a, b) : a < x < b
  • (a, b) : a < x <= b
  • [a, b] : a <= x < b
  • [a, b] : a <= x <=b

So how to use an elegant way to obtain this kind of operation relation, is a problem to be solved.

Typical applications

The most typical use of transitive expressions in Python is in ORM.

Python calls to relational databases are basically implemented through the Database API. Querying data relies on SQL, and one of ORM’s greatest conveniences is the ability to generate SQL for the query.

Some query statements in non-relational databases also support conditional queries, such as AWS Dynamodb. How to generate query statements through ORM is always important.

You can see in the Query Operators of the Peewee documentation that this ORM supports common operators to represent fields and relationships between fields.

The documentation also uses functions to express relationships, which are essentially the same, but are beyond the scope of the discussion

# Find the user whose username is "charlie".
User.select().where(User.username == 'charlie')

# Find the users whose username is in [charlie, huey, mickey]
User.select().where(User.username << ['charlie'.'huey'.'mickey'])
Copy the code

You can see from the code above that equality is represented by == and IN is represented by <<.

The solution

The central idea is simple: store restore operators and parameters

All of the operators Python supports can be reimplemented by overwriting magic methods, so operators and arguments are already available in magic methods.

This is true for both unary and binary operators.

So, the original problem can be done in two steps.

The first step, storing the operators and arguments, can be done with a class override of the related operators.

class Expression:
    def __eq__(self, other):
        return Operator('= =', other)

    def __lt__(self, other):
        return Operator('<', other)

    def __le__(self, other):
        return Operator('< =', other)

    def __gt__(self, other):
        return Operator('>', other)

    def __ge__(self, other):
        return Operator('> =', other)
Copy the code

The second step is to restore the operators and parameters. Complete the conversion from an Operator to a function in the Operator class.

import operator

class Operator:
    def __init__(self, operator_, rhs):
        self._operator = operator_
        self._rhs = rhs 
        self._operator_map = {
            '= =': operator.eq,
            '<': operator.lt,
            '< =': operator.le,
            '>': operator.gt,
            '> =': operator.ge
        }

    @property
    def value(self):
        return self._rhs 

    @property
    def operator(self):
        return self._operator_map[self._operator]
Copy the code

An instance of an Operator is an expression that defines the relationship between the Operator and the function to perform special operations.

So, with Expression and Operator, you can elegantly solve the original problem

def pick_range(data, left_exp, right_exp):
    lvalue = left_exp.value
    rvalue = right_exp.value
    
    loperator = left_exp.operator
    roperator = right_exp.operator
    
    return [item for item in data if loperator(item, lvalue) and roperator(item, rvalue)]
Copy the code

Finally, a couple of test cases

>>> exp = Expression()
>>> data = [1.3.4.5.6.8.9]
>>> pick_range(data, 1 < exp, exp < 6)
[3.4.5]
>>> pick_range(data, 1 <= exp, exp < 6)
[1.3.4.5]
>>> pick_range(data, 1 < exp, exp <= 6)
[3.4.5.6]
>>> pick_range(data, 1 <= exp, exp <= 6)
[1.3.4.5.6] > > >Copy the code

conclusion

Transitive expressions are easy for those who know them, but confusing for those who don’t.

Python is powerful and mysterious, and simplicity of logic is always backed by complexity. Go deep into Python to see the beauty of Python.