0

Is it possible to dynamically create/set variables within a subclass within the base class across multiple subclasses without affecting the other subclasses?

For example, take this code here:

class Base:
    @classmethod
    def __init__(cls,v):
        cls.v=v

class sub1(Base):
    Base.__init__(1)

class sub2(Base):
    Base.__init__(5)

In that code, when sub1 is created, its v attribute is equal to 1. But when sub2 is created, the v attribute of both sub1 and sub2 becomes 5. I think I might know why this is. I assume the @classmethod of the base class is not actually setting the attribute of the subclass, but instead its own attribute. Then that attribute is inherited in the subclasses. My question is: how can I use this kind of inheritance to set attributes of subclasses, not attributes of the base class which are inherited.

In other words, I would like to be able to use a similar structure, if possible (or at least a simple one) in order to accomplish setting attributes in subclasses that are specific to the subclasses and are not simply globally inherited from the base class.

Is this even possible?

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
Clayton Geist
  • 422
  • 6
  • 14
  • Is there some reason you can't just do: `v = 1` in `sub1` and `v = 5` in `sub2`? Assignments within the body of the class statement, outside any method, define class attributes already; you don't need a special method to do it. Making `__init__` a `classmethod` makes it sound like you really want a metaclass, but that's massive overkill if you just want to set a class attribute... – ShadowRanger Jan 24 '18 at 04:04

1 Answers1

0

I don't know if it can be easily accomplished any other way, but I've come up with a solution myself using a metaclass.

Solution:

class MetaBase(type):
    def __init__(cls, name, bases, namespace):
        super(MetaBase, cls).__init__(name, bases, namespace)
        if '_superclass' in namespace:  # A special attribute to distinguish between superclasses and subclasses
            if '_attrnames' not in namespace:
                raise AttributeError('"_attrnames" needs to be defined as a class attribute in a superclass.')
        else:
            if 'classvars' in namespace:  # Allow for define all required class attributes in one iterable attribute
                for attrname, attr in zip(getattr(cls.__mro__[1], '_attrnames'), getattr(cls, 'classvars')):  # Get all the varnames in the superclass's "varnames", as well as the values
                    setattr(cls, attrname, attr)
                    namespace[attrname] = attr
                delattr(cls, 'classvars')
            else:
                for attrname in getattr(cls.mro()[1], '_attrnames'):
                    if attrname not in namespace:
                        raise AttributeError('"%s" not defined, but is required.' % attrname)

class Base(metaclass=MetaBase):
    _superclass = True  # The value of this attribute doesn't matter
    _attrnames = ('a','b','c')

class Sub1(Base):
    a = 1
    b = 2
    c = 3

class Sub2(Base):
    classvars = (1, 2, 3)
Clayton Geist
  • 422
  • 6
  • 14