John
John

Reputation: 551

Call a class method only once

I created the following class:

import loader
import pandas

class SavTool(pd.DataFrame):

    def __init__(self, path):
         pd.DataFrame.__init__(self, data=loader.Loader(path).data)

    @property
    def path(self):
        return path
    @property
    def meta_dict(self):
        return loader.Loader(path).dict

If the class is instantiated the instance becomes a pandas DataFrame which I wanted to extend by other attributes like the path to the file and a dictionary containing meta information (called 'meta_dict').

What I want is the following: the dictionary 'meta_dict' shall be mutable. Namely, the following should work:

df = SavTool("somepath")
df.meta_dict["new_key"] = "new_value"
print df.meta_dict["new_key"]

But what happens is that every time I use the syntax 'df.meta_dict' the method 'meta_dict' is called and the original 'meta_dict' from loader.Loader is returned such that 'df.meta_dict' cannot be changed. Therefore, the syntax leads to "KeyError: 'new_key'". 'meta_dict' shall be called only once and then never again if it is used/called a second/third... time. The second/third... time 'meta_dict' should just be an attribute, in this case a dictionary.

How can I fix this? Maybe the whole design of the class is bad and should be changed (I'm new to using classes)? Thanks for your answers!

Upvotes: 0

Views: 1902

Answers (1)

Sam Dolan
Sam Dolan

Reputation: 32542

When you call loader.Loader you'll create a new instance of the dictionary each time. The @property doesn't cache anything for you, just provides a convenience for wrapping complicated getters for a clean interface for the caller.

Something like this should work. I also updated the path variable so it's bound correctly on the class and returned in the path property correctly.

import loader
import pandas

class SavTool(pd.DataFrame):

    def __init__(self, path):
         pd.DataFrame.__init__(self, data=loader.Loader(path).data)
         self._path = path
         self._meta_dict = loader.Loader(path).dict

    @property
    def path(self):
        return self._path

    @property
    def meta_dict(self):
        return self._meta_dict

    def update_meta_dict(self, **kwargs):
        self._meta_dict.update(kwargs)

Another way to just cache the variable is by using hasattr:

@property
def meta_dict(self):
    if not hasattr(self, "_meta_dict"):
       self._meta_dict = loader.Loader(path).dict
    return self._meta_dict

Upvotes: 1

Related Questions