Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Logically combine class instances

I have a set of filter objects, which inherit the properties of a Filter base class

class Filter():
    def __init__(self):
        self.filterList = []

    def __add__(self,f):
        self.filterList += f.filterList  

    def match(self, entry):
        for f in self.filterList:
            if not f(entry):
               return False
        return True

class thisFilter(Filter):
    def __init__(self, args):
        super().__init__()
        ....
        def thisFilterFunc(entry)->bool:
            return True

        self.filterList.append(thisFilterFunc)

This filter classes are used by various functions to filter entries

def myfunc(myfilter, arg1, ...):
    ...
    for entry in entries:
        if myfilter.match(entry):
            ... do something

Multiple filters can be added (logical and) by adding instances of these filters:

bigFilter = filter1 + filter2 + ...

This is all comming together quite well, but I would love to generalize this in a way to handle more complex logical constraints, e.g.

bigFilter = (filter1 and filter2) or (filter3 and not filter4)

It feels like this should somehow be possible with overwriting __bool__ of the class instead of using __add__ but the boolean value of the class is only known for a given entry and not during assemly of the filter.

Any ideas how to make this possible? Or is there maybe a more pythonic way do do this?

like image 361
user_na Avatar asked Oct 28 '25 13:10

user_na


1 Answers

I would go for something like this:

class Filter:
  def __init__(self, filter: Callable[[Any], bool]):
    self.filter = filter

  def __add__(self, added: Filter):
    return OrFilter(self, added)

  def __mul__(self, mult: Filter):
    return AndFilter(self, mult)

  def __invert__(self):
    return Filter(lambda x: not self.filter(x))

  def __call__(self, entry):
    return self.filter(entry)

class AndFilter(Filter):
  def __init__(self, left: Filter, right: Filter):
    self.left = left
    self.right = right

  def __call__(self, entry):
    return self.left(entry) and self.right(entry)

class OrFilter(Filter):
  def __init__(self, left: Filter, right: Filter):
    self.left = left
    self.right = right

  def __call__(self, entry):
    return self.left(entry) or self.right(entry)

Then you can create filters, and use them as (filterA + ~filterB) * filterC

You'll probably want to replace that Any with a generic type, so that your filter knows what it's dealing with.

like image 138
njzk2 Avatar answered Oct 31 '25 11:10

njzk2



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!