0

just looking for opinions or thoughts about how you choose between using a static class vs a memoized class.

For example, consider these 2 python classes:

@cached
class A:

    def __init__(self):
        #expensive computation

    def a(self):
        #do something

this class would be called like:

A().a()

now consider this second class:

class B:

    is_init = False

    @classmethod
    def __init(cls):
        #expensive computation
        cls.is_init=True

    @classmethod
    def b(cls):
        if not cls.is_init: 
             cls.__init()
        #do stuff

called like:

B.b()

they both only do the expensive computation once - so, which one is the better approach? what are the tradeoffs?

not needed for this question:)

  • 1
    So, there is no concept of a "static class" in Python. Neither of these class definitions make a lot of sense, and are highly irregular. So, I wouldn't really recommend either of them. Both approaches here could be termed "memoization". – juanpa.arrivillaga Mar 10 '23 at 21:12
  • thank you @juanpa.arrivillaga for the feedback! how would you declare a class with some static methods that needs an expensive first-run setup? – Jean Robatto Mar 10 '23 at 21:13
  • 1
    Why are you creating a class with static methods to begin with? Just don't. Just create functions. A class with only static methods *should not be a class*, since it defeats the entire purpose of a class, which is to encapsulate state. In which case, you should just cache/memoize that function which does the expensive calculation. – juanpa.arrivillaga Mar 10 '23 at 21:17
  • But note, here you are using *class methods*, not static methods. And to be specific about what I was saying above, decorating `__init__` with `classmethod` is highly irregular, and I wouldn't use whatever that approach is for that reason alone – juanpa.arrivillaga Mar 10 '23 at 21:18
  • @juanpa.arrivillaga sorry, i should have specified - the expensive computation is needed in order for the other methods to work - its an initialization function that initialized state in the class in order for the other methods to be called correctly – Jean Robatto Mar 10 '23 at 21:19
  • 1
    Again, this just seems like a terminology issue - you aren't working with "static" methods. You have *class methods*. I'm not trying to be difficult here, but we need to be able to communicate using a common set of terminology. – juanpa.arrivillaga Mar 10 '23 at 21:20
  • ok i think the issue @juanpa.arrivillaga is that i left the question too open, please allow me to specify:) my goal is to have a class that exposes a get() method to retrieve information from some server. the initial connection to the server is super slow, but subsequent calls are really fast. so the init method basically sets up the connection, and the get method would use said connection - im not an expert in python so im wondering - how would you approach this code design? the goal is to have one single connection per app – Jean Robatto Mar 10 '23 at 21:26
  • The context is helpful! I would use dependency injection and provide the connection to the class as an argument. But if you don't want to go that route, then just create a function which initializes that connection, memoize that function using `functools.cache` or `functools.lru_cache`, and then call that function in your `__init__` to get the connection. – juanpa.arrivillaga Mar 10 '23 at 21:29
  • awesome @juanpa.arrivillaga! so this can open up my original question - which is: “why use caching and instanced classes as opposed to saving the connection in a class variable”? as in, why would you opt for the cache as opposed to just cls.connection ? – Jean Robatto Mar 10 '23 at 21:34

1 Answers1

0

Here is the most basic approach I can think of, and it's a perfectly reasonable approach:

import functools

@functools.cache
def initialize_connection():
    # expensive initialization
    return connection

Then, you can just do:

def A:
    def __init__(self):
        self.connection = initialize_connection()
    def get(self):
        # use self.connection to do stuff
        return stuff

Another alternative, which is essentially equivalent:

class A:
    connection = initialize_connection()
    ...
    def get(self):
        # use self.connection to do stuff
        return stuff
    ...

The key issue isn't really whether you should make it a class variable or an instance variable, IMO, it's that you have a function that initializes your connection that is cached. Now you can utilize that function anywhere you might need a connection. When I said I would just use dependency injection, I mean I would have:

class A:
    def __init__(self, connection):
        self.connection = connection
    def get(self):
        # use self.connection to do stuff
        return stuff

a = A(initialize_connection())
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
  • this is great! im just wondering at the advantage of this approach versus saving the connection in a class variable and using classmethods instead! – Jean Robatto Mar 10 '23 at 21:37
  • @JeanRobatto well, you wouldn't have to use classmethods. Instances have access to class variables. – juanpa.arrivillaga Mar 10 '23 at 21:38
  • @JeanRobatto the flexibility of making it an instance method, though, is that you *can* have different connections if you want to, if the design changes in the future. The advantages of dependency injection are the advantages of dependency injection in general (better testability, loose coupling, more modularity, explicit dependencies, etc etc) – juanpa.arrivillaga Mar 10 '23 at 21:40