views:

516

answers:

6

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.

+1  A: 

Why not just use class attributes?

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

class Child(Parent):
    option2 = 'value2'
    option3 = 'value3'
Jason Baker
you, win. beat me by 20 seconds :)
SpliFF
I prefer to keep options in one attribute as I have other class attributes that aren't options.
Nadia Alramli
+1  A: 
class ParentOptions:
  option1 = 'value1'
  option2 = 'value2'

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

class Parent(object):
  options = ParentOptions()

class Child(Parent):
  options = ChildOptions()
SpliFF
Hmmm, I don't know. I prefer an automated way of doing it. I have tens of subclasses. Adding a separate class for each options is going to be a mess.
Nadia Alramli
+6  A: 

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.

Ants Aasma
+11  A: 

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).

Alex Martelli
A: 

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.

Nadia Alramli
The downside to this approach is that it's much uglier to do dynamic setting of options. For example to read the options of one class from a configuration file.
Ants Aasma
Well this solution fits more in my special case. But as a general solution your solution is better.
Nadia Alramli
A: 

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
gnibbler