How to combine methods?

Question:

I have three different methods. But the return statement of the three methods is almost identicall.

So I have this three methods:

def filter_verdi_total_fruit_cost(self):
        fruits_groups = (
            f"(?:{fruit})" for fruit in self.extractingText.list_fruit)
        fruits_combined_with_capture = f'(?:{"|".join(fruits_groups)})'
        fruits_pattern = self.regex_fruit_cost(fruits_combined_with_capture)
        return re.findall(fruits_pattern, self.extractingText.text_factuur_verdi[0])

and I have a method that combines the three methods:

def show_extracted_data_from_file(self,  file_name):
        self.extractingText.extract_text_from_image(file_name)
        total_fruit = self.filter_verdi_total_number_fruit()
        fruit_name = self.filter_verdi_fruit_name()
        fruit_total_cost = self.filter_verdi_total_fruit_cost()

        return "n".join("{} t {} t {}".format(a, b, c) for a, b, c in zip(total_fruit, fruit_name, fruit_total_cost))

But as you can see the return statement: self.extractingText.text_factuur_verdi[0]

is the same in all three methods.

Question: how can I improve this?

Asked By: mightycode Newton

||

Answers:

You could create another method in your class, e.g.:

    def findall(self, inputstr):
        return re.findall(inputstr, self.extractingText.text_factuur_verdi[0])

and then use that in each of your other methods, e.g.

    def filter_verdi_total_number_fruit(self):
        regex = r"(d*(?:.d+)*)s*W+(?:" + '|'.join(re.escape(word)
                                                       for word in self.extractingText.list_fruit) + ')'
        return self.findall(regex)

This is a bit neater than your current code, but not by much.

Answered By: Matt Pitkin

For simpler shorter methods, I would have them return the regex rather than the matches. Add methods to join the list and to find the matches.

    def fruit_list(self, format_=re.escape):
        """ Return a string with all the fruit words, 
            escaped or formatted for use in a regex """
        return "|".join(format_(word) for word in self.extractingText.list_fruit)

    def verdi_total_number_fruit_regex(self):
        return rf"(d*(?:.d+)*)s*W+(?:{self.fruit_list()})"
     
    def verdi_fruit_name_regex(self):
        return rf"(?:d*(?:.d+)*)s*W+({self.fruit_list()})"
    
    def verdi_total_fruit_cost_regex(self):
        fruit_list = self.fruit_list(format_="(?:{})".format)
        return self.regex_fruit_cost(f"(?:{fruit_list})")

    def findall(self, regex):
        return re.findall(regex, self.extractingText.text_factuur_verdi[0])

    def show_extracted_data_from_file(self,  file_name):
        self.extractingText.extract_text_from_image(file_name)
        regexes = [
            self.verdi_total_number_fruit_regex(),
            self.verdi_fruit_name_regex(),
            self.verdi_total_fruit_cost_regex()
        ] 
        matches = [self.findall(regex) for regex in regexes]
        return "n".join(" t ".join(items) for items in zip(*matches))

Consider whether you are calling the three regex methods from multiple different places. If you are only calling them from show_extracted_data_from_file, there is no need for them to be separate one-liner methods. You could cut and paste their return values into the show_extracted_data_from_file method. The abstraction you need in order to avoid repetition is provided by fruit_list and findall.

Answered By: Stuart

In a similar occasion I used a decorator.

Something like this should do the trick:

def findall_decorator(func):
    def wrapper(self, *args, **kwargs):
        regular_expression = func(self, *args, **kwargs)
        return re.findall(regular_expression, self.extractingText.text_factur_verdi[0])
    return wrapper
class A:
    @findall_decorator
    def filter_verdi_fruit_name(self):
        return r"(?:d*(?:.d+)*)s*W+(" + '|'.join(re.escape(word)for word in self.extractingText.list_fruit) + ')'

The class is of course just an example, but the idea is you decorate your methods and have them return the regex.

This works since self gets passed around as an argument, specifically the first argument, and your decorator simply outputs a new method which will receive the arguments the decorated method receives.

Disclaimer: even if it works this is bad coding practice that could lead to errors and hard to maintain code

Answered By: gionni
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.