Nadia Alramli
Nadia Alramli

Reputation: 114943

A neat way of extending a class attribute in subclasses

Let's say I have the following class

class Parent(object):
    Options = {
        'option1': 'value1',
        'option2': 'value2'
    }

And a subclass called Child

class Child(Parent):
   Options = Parent.Options.copy()
   Options.update({
        'option2': 'value2',
        'option3': 'value3'
   })

I want to be able to override or add options in the child class. The solution I'm using works. But I'm sure there is a better way of doing it.


EDIT

I don't want to add options as class attributes because I have other class attributes that aren't options and I prefer to keep all options in one place. This is just a simple example, the actual code is more complicated than that.

Upvotes: 16

Views: 3542

Answers (6)

John La Rooy
John La Rooy

Reputation: 304147

Here is a way using a metaclass

class OptionMeta(type):
    @property
    def options(self):
        result = {}
        for d in self.__mro__[::-1]:
            result.update(getattr(d,'_options',{})) 
        return result

class Parent(object):
    __metaclass__ = OptionMeta
    _options = dict(
        option1='value1',
        option2='value2',
        )

class Child(Parent):
    _options = dict(
        option2='value2a',
        option3='value3',
        )

print Parent.options
print Child.options

Upvotes: 2

Nadia Alramli
Nadia Alramli

Reputation: 114943

After thinking more about it, and thanks to @SpliFF suggestion this is what I came up with:

class Parent(object):
    class Options:
        option1 = 'value1'
        option2 = 'value2'


class Child(Parent):
    class Options(Parent.Options):
        option2 = 'value2'
        option3 = 'value3'

I'm still open to better solutions though.

Upvotes: 6

Alex Martelli
Alex Martelli

Reputation: 881595

Semantically equivalent to your code but arguably neater:

class Child(Parent):
   Options = dict(Parent.Options,
      option2='value2',
      option3='value3')

Remember, "life is better without braces", and by calling dict explicitly you can often avoid braces (and extra quotes around keys that are constant identifier-like strings).

See http://docs.python.org/library/stdtypes.html#dict for more details -- the key bit is "If a key is specified both in the positional argument and as a keyword argument, the value associated with the keyword is retained", i.e. keyword args override key-value associations in the positional arg, just like the update method lets you override them).

Upvotes: 21

Ants Aasma
Ants Aasma

Reputation: 54882

One way would be to use keyword arguments to dict to specify additional keys:

Parent.options = dict(
    option1='value1',
    option2='value2',
)

Child.options = dict(Parent.options,
    option2='value2a',
    option3='value3',
)

If you want to get fancier, then using the descriptor protocol you can create a proxy object that will encapsulate the lookup. (just walk the owner.__mro__ from the owner attribute to the __get__(self, instance, owner) method). Or even fancier, crossing into the probably-not-a-good-idea territory, metaclasses/class decorators.

Upvotes: 8

SpliFF
SpliFF

Reputation: 38966

class ParentOptions:
  option1 = 'value1'
  option2 = 'value2'

class ChildOptions(ParentOptions):
  option2 = 'value2'
  option3 = 'value3'

class Parent(object):
  options = ParentOptions()

class Child(Parent):
  options = ChildOptions()

Upvotes: 2

Jason Baker
Jason Baker

Reputation: 198557

Why not just use class attributes?

class Parent(object):
    option1 = 'value1'
    option2 = 'value2'

class Child(Parent):
    option2 = 'value2'
    option3 = 'value3'

Upvotes: 4

Related Questions