Can you set parent class attributes using super()
in python 2.7? I would expect the following code to work, but it does not:
class A(object):
val_a = 1
class B(A):
val_b = 2
def set_a(self, val):
super(B,self).__class__.val_a = val
b = B()
b.set_a(3)
It gives the following error:
TypeError: can't set attributes of built-in/extension type 'super'
Is there a correct way to do this?
Can you set static class attributes using
super()
in python 2.7?
No.
I would expect the following code to work, but it does not
First, __class__
actually gets special-cased by super
, to return the super
instance's class, rather than performing the MRO search super
usually does.
Second, there's no B.__dict__['__class__']
entry for super
to bypass, so even if __class__
wasn't special-cased, it'd still be B
instead of A
.
Third, if you want to refer specifically to class A
, rather than whatever happens to come next in self
's MRO, you should just refer to A
directly.
Long story short: use A
, not super
.
Reference A.val_a
directly. val_a
in this case is a "static" variable, not an "attribute" of an object instance.
The super
call tries to get the object of self instance, and thus access to attribute super(B, self).val_a
will fail.
class A(object):
val_a = 1
class B(A):
val_b = 2
def set_a(self, val):
A.val_a = val
b = B()
b.set_a(3)
print b.val_a
You can't do it with super()
, but you can search through base classes of the subclass and do it that way.
Here's what I mean:
class Other(object):
val_a = 42
class A(Other):
val_x = 1
def __str__(self):
return '{}(val_a={})'.format(self.__class__.__name__, self.val_a)
class B(A):
val_b = 2
def set_a(self, val):
for cls in self.__class__.__mro__[1:]:
try:
object.__getattribute__(cls, 'val_a')
except AttributeError:
continue
print('Updating class: {}'.format(cls.__name__))
cls.val_a = val
break
else:
raise RuntimeError("Can't find a base class with attribute 'val_a'")
b = B()
print(b) # -> B(val_a=1) # B(val_a=42)
print('Other.val_a: {}'.format(Other.val_a)) # Other.val_a: 42
print('A.val_a: {}'.format(A.val_a)) # A.val_a: 42
print('B.val_a: {}'.format(B.val_a)) # B.val_a: 42
print('')
b.set_a(3) # Updating class: Other
print('')
print(b) # B(val_a=3)
print('Other.val_a: {}'.format(Other.val_a)) # Other.val_a: 3
print('A.val_a: {}'.format(A.val_a)) # A.val_a: 3
print('B.val_a: {}'.format(B.val_a)) # B.val_a: 3
According to your code you are trying to make the change for the instance. Then, the function set_a
needs to be as below.
class B(A):
val_b = 2
def set_a(self, val):
self.val_a = val
But if you need the change to be applicable for the class level, you can use a class method and you can't use super()
class B(A):
val_b = 2
@classmethod
def set_a_class(cls, val):
cls.val_a = val
B.set_a_class(5)
print B.val_a
b = B()
print b.val_a
will return
5
5
See Static variable inheritance in Python
class A(object):
val_a = 1
class B(A):
val_b = 2
def set_a(self, val):
type(self).val_a = val
b1 = B()
b1.set_a(3)
b2 = B()
b2.set_a(4)
print type(b1).val_a
print type(b2).val_a
print A.val_a
print B.val_a
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.