28

I have a class Klass with a class attribute my_list. I have a subclass of it SubKlass, in which I want to have a class attribute my_list which is a modified version of the same attribute from parent class:

class Klass():
    my_list = [1, 2, 3]
    

class SubKlass(Klass):
    # this works, but I must specify parent class explicitly
    my_list = Klass.my_list + [4, 5]

    # SystemError: super(): __class__ cell not found
    #my_list = super().my_list + [4, 5]

    # NameError: name 'my_list' is not defined 
    #my_list = my_list + [4, 5] 
    

print(Klass.my_list)
print(SubKlass.my_list)

So, is there a way to access parent class attribute without specifying its name?


There is a bug on Python issue tracker: http://bugs.python.org/issue11339. Let's hope it will be solved at some point.

1
  • I'm not quite sure what the mentioned Python issue (now github.com/python/cpython/issues/55548) has to do with it. It's about being able to use the own class name in annotations of methods. Commented Aug 1 at 14:28

5 Answers 5

16

You can't.

A class definition works in Python works as follows.

  1. The interpreter sees a class statement followed by a block of code.

  2. It creates a new namespace and executes that code in the namespace.

  3. It calls the type builtin with the resulting namespace, the class name, the base classes, and the metaclass (if applicable).

  4. It assigns the result to the name of the class.

While running the code inside the class definition, you don't know what the base classes are, so you can't get their attributes.

What you can do is modify the class immediately after defining it.


Here's a little class decorator that you can use to update the attribute. The idea is that you give it a name and a function. It looks through all the base classes of your class, and gets their attributes with that name. Then it calls the function with the list of values inherited from the base class and the value you defined in the subclass. The result of this call is bound to the name.

Code might make more sense:

>>> def inherit_attribute(name, f):
...     def decorator(cls):
...         old_value = getattr(cls, name)
...         new_value = f([getattr(base, name) for base in cls.__bases__], old_value)
...         setattr(cls, name, new_value)
...         return cls
...     return decorator
... 
>>> def update_x(base_values, my_value):
...    return sum(base_values + [my_value], tuple())
... 
>>> class Foo: x = (1,)
... 
>>> @inherit_attribute('x', update_x)
... class Bar(Foo): x = (2,)
... 
>>> Bar.x
(1, 2)

The idea is that you define x to be (2,) in Bar. The decorator will then go and look through the subclasses of Bar, find all their xs, and call update_x with them. So it will call

update_x([(1,)], (2,))

It combines them by concatenating them, then binds that back to x again. Does that make sense?

Sign up to request clarification or add additional context in comments.

4 Comments

Note that metaclasses hijack #3, and can even modify bases before passing it on to type. So yeah, you can't.
Yes, that makes sense. I thought of that myself (via metaclasses), but extending an inherited list was just an example. One might want to delete some items from the list instead of extending it. Or have not a list but other type... Thanks for the extensive answer!
@warwaruk well the idea is that you can write your own update_x to do any of those things. Oh, and Ruby does let you reference classes within the class definition. But then it looks like Perl, so it's not all good! =p
@warwaruk for example, def update_x(base_values, _): out = base_values[0]; out.pop(0); return out. Or, I don't know, set intersection: def update_x(base_values, my_value): return set.union(*base_values).intersection(my_value). Or anything.
3

As answered by @katrielalex, my_list is not in the namespace by default before the class has been created. What you could do however in Python 3, if you want to dive into metaclasses, is to add my_list to the namespace manually:

class Meta(type):
    def __prepare__(name, bases, **kwds):
        print("preparing {}".format(name), kwds)
        my_list = []
        for b in bases:
            if hasattr(b, 'my_list'):
                my_list.extend(b.my_list)
        return {'my_list': my_list}

class A(metaclass=Meta):
    my_list = ["a"]

class B(A):
    my_list.append(2)

print(A().my_list)
print(B().my_list)

Note that this example probably does not yet handle diamond-shaped inheritance structures well.

The new __prepare__ attribute is defined in PEP 3115.

Comments

1

You could use a metaclass or a class decorator. Here's how you would use a metaclass in Python 2.x:

class Klass(object):
    my_list = [1, 2, 3]

class KlassMeta(type):

    def __new__(cls, name, bases, attrs):
        # grab last base class, which in this case is Klass
        attrs['my_list'] = bases[-1].my_list + [4, 5]
        return super(KlassMeta, cls).__new__(cls, name, bases, attrs)

class SubKlass(Klass):
    __metaclass__ = KlassMeta

With Python 3 you'd declare the metaclass using the newer syntax:

class SubKlass(Klass, metaclass=KlassMeta):
    pass

1 Comment

Please do not encourage people to needlessly use metaclasses.They are complicated and should be avoided 9 out of 10 times anyone thinks they need one. Most people do not need to know how metaclasses work.
0

There is no way from the body of the class; from a context in which self is available (or the class itself), you can examine __bases__.

1 Comment

No, self is not available. Otherwise i could have used super()
-1

As you've seen from the other responses such as katriealex's: No, you can't. At least not easily.

Q: What are you actually trying to do?

Q: Why do you want to do this?

2 Comments

Python has a nice feature which Alex Martelli calls 'data inheritance'. In __init__ i can write self.setting1 = self.setting1 + 5, where setting1 is a class attribute. It might be even parent class attribute. In the latter example i haven't used any special code, the lookup by name in the class attributes is done automatically. I want the same feature within class body.
@warvariuc that is going to create an instance attribute called self.setting1, which is then going to accessed in front of any class attribute. So although that's going to help in some circumstances, it's not going to let people set class attributes...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.