Alexander Zhak
Alexander Zhak

Reputation: 9272

Init class instances with a constant set of properties which is initialized once

I want to have some instances of a class, MyClass, which share some constant properties read from file, but also have properties with values unique to the instance. However, I want to read file only once for all instances.

Something like

config.json

{
  "a": 1,
  "b": 2,
}

Then classes definition:

class MyBaseClass:

    def __new__(cls):
        with open('config.json', 'r') as f:
            for key, value in json.load(f).items():
                setattr(cls, key, value)

class MyClass(MyBaseClass):

    def __init__(self, param):
        self.c = param

And when I initialize instances of MyClass, I want

inst1 = MyClass("One")
inst2 = MyClass("Two")

to have inst1.c is One and inst2.c is Two. But I want to achieve that in a way so that MyBaseClass reads data from config.json only once. Like in a singleton. How can I do it?

Upvotes: 3

Views: 52

Answers (2)

Mad Physicist
Mad Physicist

Reputation: 114230

You have most of the framework already there. There are a number of pieces of code that run when a class is first created. The simplest one to use is the class body itself. You could use it to create a class variable containing the attributes you wanted with a snippet like the one you show here:

class MyBaseClass:
    with open('config.json', 'r') as f:
        attributes = json.load(f)

    def __new__(cls):
        self = super().__new__(cls)
        self.__dict__.update(cls.attributes)

Any name in the class namespace that remains after the class is run becomes a class variable, not just methods. Updating a dictionary will only work if you have a regular class that doesn't use slots or have conflicting properties. In those cases, use your setattr loop in __new__.

Upvotes: 1

cs95
cs95

Reputation: 402263

You can define MyBaseClass to be a metaclass, so this is done just once.

>>> class MyBaseClass(type):
...    def __new__(cls, name, bases, dct):
...        x = super().__new__(cls, name, bases, dct)
...        print('In metaclass')
...        with open('config.json', 'r') as f:
...            for key, value in json.load(f).items():
...                setattr(x, key, value)
...        return  x
...
>>>

And now, specify MyBaseClass as a metaclass of MyClass.

>>> class MyClass(metaclass=MyBaseClass):    
...    def __init__(self, param):
...        self.c = param
...
In metaclass
>>>

When MyClass is defined, that invokes MyBaseClass.__new__ method just once. Now,

>>> inst1 = MyClass("One")
>>> inst2 = MyClass("Two")

Will result in the two instances being created without any further reads for config.json.

Upvotes: 1

Related Questions