I have a Class B inheriting Class A with a class attribute cls_attr. And I would like to set dynamically cls_attr in class B. Something like that:
class A():
cls_attr= 'value'
class B(A):
def get_cls_val(self):
if xxx:
return cls_attr = 'this_value'
return cls_attr = 'that_value'
cls_attr = get_cls_val()
I tried several things. I know i might not be looking in the right place but i am out of solutions.
EDIT: Classes are django admin classes
Thanks.
4 Answers 4
class attributes can be read on the class or an instance, but you can only set them on the class (trying to set them on an instance will only create an instance attribute that will shadow the class attribute).
If the condition is known at import time, you can just test it in the class body:
xxx = True
class A(object):
cls_attr = 'value'
class B(A):
if xxx:
cls_attr = 'this_value'
else
cls_attr = 'that_value'
Now if you want to change it during the program's execution, you either have to use a classmethod:
class B(A):
@classmethod
def set_cls_attr(cls, xxx):
if xxx:
cls.cls_attr = 'this_value'
else:
cls.cls_attr = 'that_value'
or if you need to access your instance during the test:
class B(A):
def set_cls_attr(self, xxx):
cls = type(self)
if xxx:
cls.cls_attr = 'this_value'
else:
cls.cls_attr = 'that_value'
7 Comments
cls_attr = get_cls_val() in my example, even though it is not the right way to do it.What about using classmethod and polymorphically overriding it in subclass?
class A:
@classmethod
def cls_attr(cls):
return 'value'
class B(A):
@classmethod
def cls_attr(cls):
if cond():
return 'this'
else:
return 'that'
assert A.cls_attr() == 'value'
cond = lambda: True
assert B.cls_attr() == 'this'
cond = lambda: False
assert B.cls_attr() == 'that'
6 Comments
@property with @classmethod. Is it a thing? For instance attributes, sure, but will it work for class attributes / methods?B.cls_attr, which is kind of the point of having a class attribute at first...The easiest solution for me is with property decorator:
class B:
@property
def attr_name(self):
""" do your stuff to define attr_name dynamically """
return attr_name
Comments
This seems to do what you want:
>>> class B(A):
@classmethod
def set_cls_val(cls, x):
if x == 1:
cls.cls_attr = "new"
>>> c = B()
>>> c.cls_attr
'value'
>>> c.set_cls_val(B, 1)
>>> c.cls_attr
'new'
>>> B.cls_attr
'new'
Just set it within the function.
EDIT: Updated to set the class attribute and not the instance attribute, thanks @bruno-desthuilliers.
EDIT: Updates once again, thanks @bruno-desthuilliers. I should think my answers through more clearly. But what you want is answered below.
4 Comments
classmethod if you hard-code the class name ? (hint: the convention for classmethods is to name the first param 'cls' - can you guess why ?)
A.cls_attrto change after callingget_cls_val(), or should the change only be seen onBand instances ofB.