Raqib
Raqib

Reputation: 1442

Initialize multiple attributes at initialization from a single attribute

I have a class as such:

class Line:
    def __init__(self, text: str):
        self.text = text

A line in my case can have many features. For example,

  1. The line is considered long if it has more than 50 characters.
  2. If the line has non-ascii characters, then a flag is set as non_ascii = True and so on
  3. Find all the urls in the line

I am able to implement them in terms of class methods:

class Line:
    def __init__(self, text: str):
        self.text = text

    def is_ascii(self):
        # check and return
    
    def is_long(self):
        # check and return

    def urls(self):
        # find and return urls

The problem I have is, I need to make the method calls on the Line object multiple times at different stages in the process (some of the method calls are pretty computationally heavy).

What I would like to have is, at initialization, I would like the Line object to have attributes such as is_ascii, is_long, urls to be pre populated so that they can be accessed multiple times without the need to compute them every time they are accessed.

class Line:
    def __init__(self, text: str):
        self.text = text
        self.is_ascii = do some processing on self.text, and assign value
        self.is_long = do some processing on self.text, and assign value
        self.urls = do some processing on self.text, and assign value

I am not sure if having all that logic live inside the init block makes sense. How would I achieve this in a 'pythonic' manner?

Upvotes: 1

Views: 843

Answers (1)

Carcigenicate
Carcigenicate

Reputation: 45750

You could just have the methods that you do, and call them from within __init__:

class Line:
    def __init__(self, text: str):
        self.text = text
        self.is_ascii = self.calc_is_ascii(self.text)
        self.is_long = self.calc_is_long(self.text)
        self.urls = self.calc_urls(self.text)

    def calc_is_ascii(self):
        # check and return

    def calc_is_long(self):
        # check and return

    def calc_urls(self):
        # find and return urls

You could also have it so if a method is called, it checks to see if the value has already been calculated, and uses the cached value, otherwise it calculates and caches it:

class Line:
    def __init__(self, text: str):
        self.text = text
        self.is_ascii = None
        self.is_long = None
        self.urls = None

    def calc_is_ascii(self):
        if self.is_ascii is None:
            # Do expensive calculation of self.is_ascii
            self.is_ascii = expensive_result
        
        # Use the cached, previously calculated value
        return self.is_ascii

    # Then the same pattern for other methods

This has the benefit that if one of the attributes is never needed, the work to calculate it isn't done.

I'm initializing the attributes to None. If they're None later, I know they haven't been calculated yet. If they aren't None, I know they have already been calculated, so I can just return the calculated result. This of course assumes that None is not a valid value.

Upvotes: 2

Related Questions