pythonfilterfunction

python filter function with arguments


Lets say I've a list of ScriptMetadata object

class ScriptMetadata:
  id = 1
  script_name = "test"
  script_run_start_time = 1
  script_run_end_time = 3
  ..
  ..

I want to provide an API like below

class ScriptInfoParser:
    def get_filtered_data(filter_func):
      [I've logic here to obtain a uber list of ScriptMetadata]
      # apply the user supplied filter func on this uber list and return result
      result = filter(filter_func, all_metadata_list)
      return list(result)

If I want to provide a filter func such that give me the ScriptMetadata objects for which script_name is "foo" I can have

    def get_foo_runs(script_metadata):
        if script_metadata.script_name == "foo":
            return script_metadata
print(ScriptInfoParser.get_filtered_data(get_foo_runs))

Question is how can I parameterize the filter func so that I can have one function that can take parameters dynamically and apply filtering

get_script_runs(script_name):
  <?? magic filter func>
  return ScriptInfoParser.get_filtered_data(magic_filter_func) # returns scriptmetadata list that matches script_name

Also I wanna go complicated i.e more than one param e.g give me scriptmetadata items for script_name="foo" and script_run_end_time < 2 etc etc

Any help appreciated


Solution

  • This is very open ended question, and there are many ways of accomplishing this. One of the most readable ways to filter on various attributes might be to pass lambdas into a combining function.

    filter_funcs = [
    lambda script_metadata: script_metadata.script_name == "foo",
    lambda script_metadata: script_metadata.script_run_end_time < 2
    ]
    def apply_filters(script_metadata, filter_funcs):
        for filter_func in filter_funcs:
            if not filter_func(script_metadata):
                return False
        return True
    

    You could also keep the inputs shorter by using eval.

    filter_strs = [
    "script_name == 'foo'",
    "script_run_end_time < 2",
    ]
    def apply_filters(script_metadata, *args):
        for filter_str in args:
            if not eval(f"script_metadata.{filter_str}"):
                return False
        return True
    
    apply_filters(script_metadata,
                  "script_name =='foo'",
                  "script_run_end_time < 2")
    apply_filters(script_metadata, *filter_strs)