-3

In Python, I define a class:

class X():
    _CON = 0

    def __init__(self, var):
        self.y = var*self._CON 

Is it possible to change the constant _CON for an instance of X? For example, I attempt to do the following

X._CON = 3
x = X(2)
a = x.y 

I expect _CON to be changed from 0 to 3 and thus self.y = var*_CON = 2*3 = 6. What is the right way to do this short of creating a new class?


Edit:

I have found the answer. My second code works. However, it changes not _CON of the instance x of the class X but that of the class X itself. The following code changes _CON of the instance x not that of the class X.

x = X(2)
x._CON = 3
x.__init__(2)
Mark Mikofski
  • 19,398
  • 2
  • 57
  • 90
Hans
  • 1,269
  • 3
  • 19
  • 38
  • 2
    Can you please clarify your question? What is your expected outcome? What do you expect the value of `a` to be? – Mark Mikofski Aug 16 '17 at 03:12
  • If I execute your example as is I get an exception `NameError: global name '_CON' is not defined`. You will either need to change it to `self.y = var * X._CON` or `self.y = var * self._CON` as [@Mateen Ulhaq](https://stackoverflow.com/users/365102/mateen-ulhaq) states below. – Mark Mikofski Aug 16 '17 at 03:20
  • IMHO if you improve your question with a [minimum complete verifiable example](https://stackoverflow.com/help/mcve) you will get better answers – Mark Mikofski Aug 16 '17 at 03:27
  • @MarkMikofski: I have edited my question. Is it better clearer now? – Hans Aug 16 '17 at 08:10
  • yes it is clearer, but your example code does not even run. Have you tried it? The answer to your question is yes, but you will have to change your code example as indicated in my comments and the upvoted answers below – Mark Mikofski Aug 16 '17 at 08:20
  • @MarkMikofski: I have corrected the code just you suggested. The code now runs. Assigning value to X._CON direct just like my code works. – Hans Aug 16 '17 at 22:32
  • Can the downvoters explain their reasons for downvoting? Aside from needing to change _CON to self._CON in __init__, the spirit of the question is quite clear. What is there not to like the question? – Hans Aug 17 '17 at 07:12

5 Answers5

1

_CON is a class level variable, not an instance level variable. If you change _CON in your class then the value of _CON will change for all instances of your class X. If the value of _CON will not be static then I'd include it as an instance level variable instead of defining it as a class variable.

vielkind
  • 2,840
  • 1
  • 16
  • 16
1

Actually it is. When you assign to an instance attribute you mask the class attribute.

So if you do C()._CON you are referring the instance namespace while if you do C._CON you are referring the class namespace. You can assign to instance and class attribute is not touched.

c = C()
c._CON = random()

You can reach class namespace from instance by self.__class__._CON or type(self)._CON

Nothing of this was tested.

Pay attention that _CON is not globally defined at X.__init__ so you should be getting an NameError: global name _CON is not defined error at x = X(2) line.

And there is another problem, since you use _CON in __init__, changing it after instantiating it will have no effect on the y attribute because this value was already computed. In this case the better thing to do is to extend the X class and customize the _CON attribute.

class Y(X):
    _CON = 3
y = Y(2)
Mark Mikofski
  • 19,398
  • 2
  • 57
  • 90
geckos
  • 5,687
  • 1
  • 41
  • 53
  • I tested my code, with the slight correction _CON -> self._CON. It works as I intended. Also please see my new edit in the question. – Hans Aug 16 '17 at 22:54
  • Calling __init__ explicitly is not very elegant... But yeah it may work. Why not extending? – geckos Aug 16 '17 at 23:47
  • I agree with the inelegance of calling __init__ explicitly. I probably should have used a class method name other than __init__. I just wanted to change the class constant which is used in the class method. Extending class is a good idea too. – Hans Aug 17 '17 at 00:22
  • 1
    But OP specifically says not to create a new class. A way to this so that it updates every time `X._CON` is changed would be to make `y` a [property](https://docs.python.org/2/library/functions.html#property) and set `var` to an instance variable, then `y = property(lambda self: X._CON * self.var)` means you can change `X._CON` even after you instantiate `x = X(2)`. Sorry for bad use of lambda, but comments don't multi-line snippets. – Mark Mikofski Aug 17 '17 at 04:18
  • @MarkMikofski is exactly right about my prohibition of creating a new class. Property is new to me. Good to know that. – Hans Aug 17 '17 at 07:17
  • what about putting `_CON` as a parameter of `__init__` with a default value of 3? The you can change at instantiation time `x = X(2,CON=2)`!? – geckos Sep 06 '17 at 18:44
1

As I said in this comment, which I will expand on here, if OP wants to be able to change X._CON on the fly, even after instantiating x = X(var) and without writing a new class then one way might be to make y a property and var an instance attribute like this:

class X(object):
    _CON = 0

    def __init__(self, var):
        self.var = var

    @property
    def y(self):
        return self.var * X._CON

then ...

>>> x = X(2)
>>> x.y  # returns 0

>>> X._CON = 3
>>> x.y  # returns 6

N.B.: As others have indicated and I think the OP has already discovered, this example uses the class attribute X._CON and changes to a class attribute will affect all instances of that class. For example, executing the following after instantiating x = X(var) from above, the value of X._CON will still be 3; it will not be 0.

Please pay very careful attention to the difference between capital X the class and little x the instance in the examples below:

>>> x._CON = 23  # <-- doesn't affect x.y b/c x.y = x.var * X._CON
>>> x.y  # still returns 6 b/c X._CON = 3 still

>>> x10 = X(10)
>>> x10.y  # returns 30 b/c X._CON = 3

>>> x._CON  # returns 23
>>> x10._CON  # returns 3 since we changed X._CON to 3
>>> X._CON  # returns 3 <-- this is the **class** attribute

Therefore if you want the value of _CON to be particular to each instance then use self._CON instead of X._CON. Starting afresh ...

class X(object):
    _CON = 0

    def __init__(self, var):
        self.var = var

    @property
    def y(self):
        return self.var * self._CON  # <-- use **instance** attribute

then ...

>>> x = X(2)
>>> x.y  # returns 0

>>> x._CON = 3  # <-- use **instance** attribute
>>> x.y  # returns 6

>>> x10 = X(10)
>>> x10.y  # returns 0 b/c x10._CON = X._CON = 0

>>> x10._CON = 7  # <-- use **instance** attribute
>>> x10.y  # returns 70

>>> x._CON  # returns 3
>>> x10._CON  # returns 7
>>> X._CON  # returns 0

There are a few more variations, like what happens to the instance attributes x._CON and x10._CON in the 2nd example using self._CON if you change the class attribute X._CON either before or after setting the instance attributes x._CON or x10._CON. I'll leave those examples for the reader to explore.

Mark Mikofski
  • 19,398
  • 2
  • 57
  • 90
0

Is it possible to get change the constant _CON for an instance of X.

Nope. If you want this, you must use an instance variable like self.con.

In your example, _CON is attached to the class X. To attach it to an instance, you must use self.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
  • 1
    OP could also use the *class* name itself, by that I mean, instead of `self._CON` he could use `X._CON`. If I change OP's example to `self.y = var * X._CON` then I get `a = 6` – Mark Mikofski Aug 16 '17 at 03:24
-1

No, from a instance/object you can't update a class variable. Because it's by design, has to be constant for all the object. Though some tricks exists(create a class method to update the variable or as usual go with init method.)..

class MyClass:
    _CON = 1

    @classmethod
    def update(cls, value):
        cls._CON += value

    def __init__(self,value):
        self.value = value
        self.update(value)

a = MyClass(1)
print (MyClass._CON) #o/p: 2

And you must think 10 times before you you update anything that starts with a underscore(somebody is saying don't touch it...)

Satya
  • 5,470
  • 17
  • 47
  • 72