0

I want to have a class that is used just for accessing read-only data. If the data is requested from the class but does not exist yet, I want the class to handle getting the data. I'm just wondering if the following code makes sense; can you see any potential issues with this?

class Data:

    @property
    def some_data(self):
        if hasattr(Data, "_some_data"):
            return Data._some_data
        else:
            Data._some_data = function_that_gets_data()
            return Data._some_data

    # other definitions for more data go here ...


D = Data()

# access data
print(D.some_data)
martineau
  • 119,623
  • 25
  • 170
  • 301
Spaterman
  • 21
  • 4
  • 1
    You can use a property for lazy initialisation. It's a bit weird that you're using an instance's property to initialise and get a class attribute. – khelwood Sep 27 '21 at 22:10
  • 1
    Are you trying to ask how to have a class attribute be a property? See https://stackoverflow.com/a/64738850/3001761 for the up-to-date answer. – jonrsharpe Sep 27 '21 at 22:12
  • 1
    Do you want the cached data to be scoped to the instance or to the class? – wim Sep 27 '21 at 22:16
  • What do you mean @khelwood? Are you referring to how I'm using e.g. `Data._some_data` in the class, rather than `self._some_data`? – Spaterman Sep 27 '21 at 22:17
  • Yes.[​](https://stackoverflow.com/q/69353719/) – khelwood Sep 27 '21 at 22:18
  • @wim it doesn't actually really matter. I don't require separate instances of this class. Would it be better to use `self._some_data`? – Spaterman Sep 27 '21 at 22:22
  • 2
    You may as well use [`functools.cached_property`](https://docs.python.org/3/library/functools.html#functools.cached_property) then. – wim Sep 27 '21 at 22:24
  • Ok, I'll check it out. But just for my understanding's sake, would my code above work if I replaced all the `Data` names in the class with `self`? – Spaterman Sep 27 '21 at 22:29
  • Yes, the code will work fine as is if you really don't care about class vs instance attributes, and it will work if you use `self`. Ryan's answer is slightly better, `cached_property` is better still - the body would just become `function_that_gets_data()`. Or you could just use `functools.cache` or `lru_cache` on `function_that_gets_data`. and cache it for everything. – Alex Hall Sep 27 '21 at 23:54

1 Answers1

0

I'm doing basically this exact thing in my code base and it's been working great for me, it's been a great way of caching information.

class ItemBase(BaseClass):
  _soup = None

  @property
  def soup(self):
    if not self._soup:
      self._soup = get_info()
    return self._soup
Ryan Nygard
  • 200
  • 8
  • 1
    One thing I'd want to change would be instead of using eg. `if not self._soup:`, use `if self._soup is None:`. That way you avoid issues if `_soup` is a boolean and is set to False. – Spaterman Sep 28 '21 at 23:12
  • @Spaterman - You are a beautiful person, that's a great point. – Ryan Nygard Sep 29 '21 at 16:05