Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make subclass have its own class attribute

Tags:

python

I have an class Generic

class Generic:
    raw_data = []
    objects = dict()

and concrete classes

class A(Generic):
    raw_data = module.somethingA

class B(Generic):
    raw_data = module.somethingB

I want to populate each raw_data onto each objects dict of the class. To that, I'm running:

for object_type in (A, B):
     for data in object_type.raw_data:
         new_object = object_type(*data)
         object_type.objects[id(new_object)] = new_object

However, this does not work because objects is shared between A and B, and I wanted each subclass of Generic to have their own objects.

How do I achieve this without having to type objects = dict() on every subclass?

I'm inclined to say that this is a traditional case where a meta class is required (that adds objects to each new class); is this the case, or is there a simpler option?

like image 300
Jorge Leitao Avatar asked Oct 19 '22 18:10

Jorge Leitao


2 Answers

Either use a metaclass or use a class decorator.

A class decorator could simply create the attribute:

def add_objects(cls):
    cls.objects = {}
    return cls

@add_objects
class A(generic):
    raw_data = module.somethingA

This doesn't really add anything however; you just replace one line (objects = {}) with another (@add_objects).

You could just add the object in your loop:

for object_type in (A, B):
     if 'objects' not in vars(object_type):
         object_type.objects = {}
     for data in object_type.raw_data:
         new_object = object_type(*data)
         object_type.objects[id(new_object)] = new_object

or copy it (reading the attribute may retrieve the parent class attribute or a direct attribute, it doesn't matter here):

for object_type in (A, B):
     object_type.objects = object_type.objects.copy()
     for data in object_type.raw_data:
         new_object = object_type(*data)
         object_type.objects[id(new_object)] = new_object

or create the dictionary from scratch:

for object_type in (A, B):
     object_type.object = {
         id(new_object): new_object
         for data in object_type.raw_data
         for new_object in (object_type(*data),)}
like image 70
Martijn Pieters Avatar answered Oct 21 '22 11:10

Martijn Pieters


I don't think a metaclass is necessary here. Why not just copy the parent class objects before populating each subclass in your loop?

for object_type in (A, B):
     # copy Generic.objects onto object_type.objects here
     object_type.objects = Generic.objects.copy()
     for data in object_type.raw_data:
         new_object = object_type(*data)
         object_type.objects[id(new_object)] = new_object

Additionally you can modify to use super and/or deepcopy if you want.

like image 28
wim Avatar answered Oct 21 '22 09:10

wim