Jossy
Jossy

Reputation: 989

Can I set a class variable from within a class?

Consider the following simple class:

class ExampleClass():

    def __init__(self, foo):
        self.foo = foo

    def calc_bar(self):
        baz = self.get_baz()
        return baz * self.foo

    @staticmethod
    def get_baz():
        return <result of unchanging database query>

I then use it with something like the following:

from module1 import ExampleClass

foo = 10
myobj = ExampleClass(foo)
bar = myobj.calc_bar()

The way this is currently constructed the database is queried every time I call the calc_bar method.

How would I turn the output of the get_baz method into a class attribute that is set only once for the class?

I can do it manually via:

class ExampleClass():

    baz = None

    def __init__(self, foo):
        self.foo = foo

    def calc_bar(self):
        return self.baz * self.foo

---

from module1 import ExampleClass

ExampleClass.foo = <result of unchanging database query>
foo = 10
myobj = ExampleClass(foo)
bar = myobj.calc_bar()

Is there a way it can be done automatically from within the class?

Upvotes: 1

Views: 48

Answers (3)

Marat
Marat

Reputation: 15738

class ExampleClass():

    def __new__(cls, *args, **kwargs):  # Singleton
        if cls.foo is None:
            cls.foo = ...
        return super(ExampleClass, cls).__new__(cls)
    # the rest is as usual

The advantage of this method is that foo is accessible at __init__, otherwise it is similar to other answers

Upvotes: 1

pho
pho

Reputation: 25489

One option is to define baz as None, and then make get_baz() update baz only if it's None.

class ExampleClass:
    baz = None
    def calc_bar(self):
        return self.get_baz() * 2
        
    @classmethod
    def get_baz(cls):
        if cls.baz is None:
           cls.baz = 'hello' # <result of query>
        return cls.baz

ec = ExampleClass()

print(ec.baz)
# Output: None

print(ec.calc_bar())
# Output: hellohello

print(ec.baz)
# Output: hello

Upvotes: 1

chepner
chepner

Reputation: 530960

You can simply cache the value the first time get_baz is called.

class ExampleClass:
    _baz = None

    def __init__(self, foo):
        self.foo = foo

    def calc_bar(self):
        baz = self.get_baz()
        return baz * self.foo

    @classmethod
    def get_baz(cls):
        if cls._baz is None:
            cls._baz = <result of query>
        return cls._baz

Upvotes: 4

Related Questions