swigganicks
swigganicks

Reputation: 1231

Preventing a dict with Classes as values from instantiating unless needed?

I have a list of dicts that looks roughly like this:

input_data = {...}
my_list = [
    {"a": Class1(input_data)},
    {"b": Class2(input_data)},
    {"c": Class3(input_data)}
]

I want to be able to maintain a list like the one above but without all of the classes being instantiated until I need them. Sometimes I'll need to go through all of them, however most times I'll just need to access one of them. As it is now, they all get instantiated and make my runtime longer even if I'm only using one of dicts in the list.

How can I prevent the classes from being instantiated until I need them?

Upvotes: 1

Views: 428

Answers (5)

Nathan
Nathan

Reputation: 10306

It sounds like you need a generator. A generator creates each element as needed when you're iterating over it (the downside is, you can't iterate over the generator again; once you've "used" an element, it's gone). range in Python is a (somewhat special) example of a generator. I'm not sure how you're going to pull out the single/few items you need from your list, though - will you just need the first few, or do you access them by index? Here's a generator example you could use if you're just going to use the first items from your list:

class C1:
    def __init__(*args):
        print("C1 init")

class C2:
    def __init__(*args):
        print("C2 init")

class C3:
    def __init__(*args):
        print("C3 init")

classes = [C1, C2, C3]
keys = 'abc'
data = [3, 5]
gen = ({keys[i]: classes[i](*data)} for i in range(len(keys)))

# each time you want an element out:
new_dict = next(gen)

If you want to be able to get the items out by index instead, other answers will probably work better.

Upvotes: -2

Paul Panzer
Paul Panzer

Reputation: 53029

You could subclass dict along the lines (bare minimum, you may have to override more methods for a smooth experience):

>>> class delayed_dict(dict):
...     def __init__(self, backstore):
...         super().__init__()
...         self._backstore=backstore
...     def __getitem__(self, key):
...         if not key in self:
...             fun, *args = self._backstore[key]
...             self[key] = fun(*args)
...         return super().__getitem__(key)

Applied to your example:

>>> input_data = (1, 2, 3)
>>> Class1 = list
>>> Class2 = tuple
>>> Class3 = dict.fromkeys
>>> 
>>> my_list = [
...     {"a": (Class1, input_data)},
...     {"b": (Class2, input_data)},
...     {"c": (Class3, input_data)}
... ]
>>> 
>>> lazy = list(map(delayed_dict, my_list))
>>> 
>>> lazy
[{}, {}, {}]
>>> 
>>> x = lazy[1]['b']
>>> x is lazy[1]['b']
True
>>> x
(1, 2, 3)
>>> lazy
[{}, {'b': (1, 2, 3)}, {}]

Upvotes: 0

BallpointBen
BallpointBen

Reputation: 13750

You can turn them into functions that return the desired object and which you only evaluate as needed.

my_list = [
    {"a": lambda: Class1(input_data)},
    {"b": lambda: Class2(input_data)},
    {"c": lambda: Class3(input_data)}
]

c1 = my_list[0]["a"]()

Upvotes: 0

DYZ
DYZ

Reputation: 57033

I feel that you are looking for a dictionary with multiple items, not a list of single-item dictionaries.

You can initialize the dictionary with class constructors rather than class objects and create objects later:

input_data = {...}
my_dict = {
    "a": Class1,
    "b": Class2,
    "c": Class3
}

def get_item(d, key, data = input_data):
    if isinstance(d[key], type): 
        d[key] = d[key](data)
    return d[key]

Later:

get_item(my_dict, "a") # Returns a Class1 instance

Perhaps you can even derive a new "smart" dictionary from dict and redefine its __getitem__ to instantiate objects when they are accessed for the first time.

Upvotes: 1

Primusa
Primusa

Reputation: 13498

I would just put the class and its input in a tuple:

input_data = {...}
my_list = [
    {"a": (Class1, input_data)},
    {"b": (Class2, input_data)},
    {"c": (Class3, input_data)}
]

Then when I want to instantiate:

tup = my_list[0]["a"]
instantiated_class = tup[0](tup[1])

Upvotes: 3

Related Questions