Search code examples
pythonparsingtokenizelogical-operators

How to store and read my logical operation on python


I am making a program to filter my data using some parameters and logical operation.

I have a lot of classrooms data that has its characteristics, so each classroom will have a different filter.

    if classrooms == 1:
       if data[A] > data[B] & data[C] != data [D]:
         print("matched")
    elif classrooms == 2:
       if data[A] < data[B] & data[C] == data [D]:
         print("matched")
    elif classrooms == 3:
       if data[B] < data[D] & data[A] == data [C]:
         print("matched")
...
...
    elif classrooms == 5000:
       if data[R] < data[A] & data[W] == data [H]:
         print("matched")

since the operator is similar, is there any method to read my logical filter from my stored file to the python program?

"(A<B)&(C!=D)"
"(A>B)&(C==D)"
..
..
"(R<A)&(W==H)"

So, I don't have to write all my logical filters for each classroom in python that causing a huge line in python. I just read from my stored text data, and my python program will interpret

"(A<B)&(C!=D)"

to this program

if data[A] > data[B] & data[C] != data [D]:

Solution

  • You could parse the filters in your file using a regular expression, and then construct a chain of functions from the operator module to execute the filter.

    This expression

    import re
    rx = re.compile(r"""^                         # Beginning of string
                        \(                        # Opening outer parenthesis
                        (?P<operand0>[A-Z])       # First operand
                        (?P<operator0>[^A-Z]+)    # First operator
                        (?P<operand1>[A-Z])       # Second operand
                        \)                        # Closing outer parenthesis
                        (?P<operator1>[^A-Z]+)    # Second operator
                        \(                        # Opening oute parenthesis
                        (?P<operand2>[A-Z])       # Third operand
                        (?P<operator2>[^A-Z]+)    # Third operator
                        (?P<operand3>[A-Z])       # Fourth operand
                        \)                        # Closing outer parenthesis
                        $                         # End of string
                        """,
                    re.VERBOSE)
    

    matches the structure of your filters.

    They can be matched like this:

    m = rx.match("(A<B)&(C!=D)")
    

    Parts of the filter may be accessed using the names assigned inside the (?P<name>) sub-expressions

    m['operand0']
    'A'
    m['operator0']
    '<'
    

    Using a dictionary to map operators to functions from the operator module

    import operator
    lookup = {
        '<': operator.lt,
        '&': operator.and_,
        '!=': operator.ne,
    }
    

    you can build an expression and evaluate it

    op0 = lookup[m['operator0']]
    op1 = lookup[m['operator1']]
    op2 = lookup[m['operator2']]
    
    result = op1(op0(a, b), op2(c, d))
    

    How you derive a, b, c, d from the operands is something you will need to work out.

    The regular expression above assumes operands are single uppercase characters and operators are one or more non-uppercase characters. This can made more precise, for example you could use the regex alternation operator | to make an expression that matches only the operators that are used

    r'&|<|>|!=|=='
    

    instead of the overly-general

    r'[^A-Z]+'