10

I'm trying to find the best way to extend a class variable. Hopefully an example of the method I've come up with so far will make this clear.

class A(object):
    foo = ['thing', 'another thing']

class B(A):
    foo = A.foo + ['stuff', 'more stuff']

So I'm trying to make the subclass inherit and extend the parent's class variable. The method above works, but seems a bit kludgey. I'm open to any suggestion, including accomplishing something similar using a completely different approach.

Obviously I can continue to use this method if need be, but if there's a better way I'd like to find it.

gbutler
  • 407
  • 2
  • 6
  • 10
  • 6
    What is it about `foo` that it needs to be a [class property](http://stackoverflow.com/questions/128573/) and not just an instance property? (For that matter, given that lists are mutable, you can *change* the class property at `__init__` time.) – kojiro Jul 26 '12 at 18:41
  • 7
    IMHO, what you have looks fine to me (and I can't think of a more clean way to do it). By the way, I would call these "class attributes", not "class properties" as a property is something different (usually created by the `property` builtin function/decorator). – mgilson Jul 26 '12 at 18:41
  • @kojiro He's not referring to those kind of properties, he just means attributes. – Julian Jul 26 '12 at 18:52
  • The main reason I am avoiding initializing it as an instance variable in __init__ is code cleanliness. If I don't need __init__ for any other purpose, I prefer to avoid writing one, along with the (IMO) ugly super()__init__() call just to initialize one variable. – gbutler Jul 26 '12 at 19:05
  • 1
    IMO, the way shown above is completely clean. A static variable is bound to a class. When inheriting, a new static variable would be bound to the most child class. Redefining it makes it clear that you also have a static foo in B. Besides that, this mostly looks like a design issue to me since this "pattern" it is not just very uncommon but also not a real inheritance pattern. – Sebastian Hoffmann Jul 26 '12 at 19:50
  • I would also say that the way you have it is good. It is practiced everywhere and makes the purpose very obvious. – User Jul 26 '12 at 20:01

2 Answers2

8

Could use a metaclass:

class AutoExtendingFoo(type):

    def __new__(cls, name, bases, attrs):
        foo = []
        for base in bases:
           try:
               foo.extend(getattr(base, 'foo'))
           except AttributeError:
               pass
        try:
            foo.extend(attrs.pop('foo_additions'))
        except KeyError:
            pass
        attrs['foo'] = foo
        return type.__new__(cls, name, bases, attrs)

class A(object):
    __metaclass__ = AutoExtendingFoo
    foo_additions = ['thing1', 'thing2']
    # will have A.foo = ['thing1', 'thing2']

class B(A):
    foo_additions = ['thing3', 'thing4']
    # will have B.foo = ['thing1', 'thing2', 'thing3', 'thing4']

class C(A):
    pass
    # will have C.foo = ['thing1', 'thing2']

class D(B):
    pass
    # will have D.foo = ['thing1', 'thing2', 'thing3', 'thing4']
Silas Ray
  • 25,682
  • 5
  • 48
  • 63
  • 2
    Nice, but violates principle of least surprise. Maybe assign to `__add_to_foo__`, instead, pretending that we inherit an empty `foo` from `object`. – chepner Jul 26 '12 at 20:38
1

I definitively would go for instance-properties. (if I got it right, they are not bound to be static for your case?!)

>>> class A:
...     @property
...     def foo(self):
...         return ['thin', 'another thing']
...
>>> class B(A):
...     @property
...     def foo(self):
...         return super().foo + ['stuff', 'thing 3']
...
>>> B().foo
['thin', 'another thing', 'stuff', 'thing 3']
gecco
  • 17,969
  • 11
  • 51
  • 68
  • That's not particularly efficient if you access this data frequently. You have to do an attribute lookup on `A` then create a new list and append to it every time you access `B.foo`. Plus you still have the problem that every subclass has to define the property such that it retrieves `super().foo`. And if any future classes inherit from multiple foo-having classes, you start getting lots of hard to follow problems fast. – Silas Ray Jul 26 '12 at 22:12