2

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.

jcs
  • 426
  • 3
  • 14
  • Have you seen this answer http://stackoverflow.com/a/2584050/471899 ? – Konstantin Jun 09 '15 at 15:12
  • 1
    I think you need to explain the behaviour you want to see more clearly. That is, do you intend for `A.cls_attr` to change after calling `get_cls_val()`, or should the change only be seen on `B` and instances of `B`. – Dunes Jun 09 '15 at 15:28
  • Hi @Dunes, the latter, i want the change only in B. – jcs Jun 09 '15 at 15:47

4 Answers4

6

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'
Jeyekomon
  • 2,878
  • 2
  • 27
  • 37
bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
  • This is the correct answer, and thanks for the suggestions. I need to remember to drink coffee before answering. – Alex Huszagh Jun 09 '15 at 15:28
  • Hi I tried your @classmethod solution. It seems that function set_cls_attr in not called... – jcs Jun 09 '15 at 15:51
  • @jcs Why would it be called if _you_ don't call it ? If you know the value at import time, please check the first part of the answer. Else please try to clarify your question. – bruno desthuilliers Jun 09 '15 at 15:56
  • @brunodesthuilliers The if part depends on user profile so i need to access 'request' (Django), can't use first part. Yes, i know i have to call the function, that is why i added the `cls_attr = get_cls_val()` in my example, even though it is not the right way to do it. – jcs Jun 09 '15 at 16:10
  • @brunodesthuilliers How can i get the function to be executed within the class itself? – jcs Jun 09 '15 at 16:31
  • @brunodesthuilliers Ok i called the function without decorator, randomly somewhere else and it works! Thanks a lot! – jcs Jun 09 '15 at 16:42
  • "i called the function without decorator" : if you remove the `@classmethod` decorator it's not a classmethod no more, the first argument will be an instance, and it will set an instance attribute, not a class attribute. This being said, given your use case, you definitly _DONT_ want a class attribute. You really should post about your _real_ use case and not about what _you_ think is the solution, because given your questions you obviously don't have a clear enough understanding of Python's object model to assert that "it works" (even if it _might_ seem to work with a single user). – bruno desthuilliers Jun 09 '15 at 16:53
2

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'
Łukasz Rogalski
  • 22,092
  • 8
  • 59
  • 93
0

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
Nikolay Shindarov
  • 1,616
  • 2
  • 18
  • 25
-2

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.

Alex Huszagh
  • 13,272
  • 3
  • 39
  • 67