2

I am extremely new to Python (coming from JavaScript) and I am battling with understanding how to change a value of a class variable. All I am trying to do is to change the age value, and then display the new msg. But when I add Tester.age = 20 it doesn't change my msg output. My msg output still uses age = 10.

How do I change a class variable externally?

class ClassTester:
    age = 10
    seconds = age * 365 * 24 * 60 * 60
    msg = "You have lived for " + str(seconds) + " seconds."

Tester = ClassTester()

Tester.age = 20
print(Tester.msg)
Xantium
  • 11,201
  • 10
  • 62
  • 89
Frank
  • 2,109
  • 7
  • 25
  • 48

6 Answers6

3

I recommend using properties, so your interface would not change.

class ClassTester:
    def __init__(self):
        self._age = 10
        self._msg = "You have lived for {} seconds."
        self._secs = self._seconds(self._age)


    @staticmethod
    def _seconds(age):
        return age * 365 * 24 * 60 * 60


    @property
    def age(self):
        return self._age


    @age.setter
    def age(self, v):
        self._age = v
        self._secs = self._seconds(v)


    @property
    def msg(self):
        return self._msg.format(self._secs)

Tester = ClassTester()

print(Tester.msg) # You have lived for 315360000 seconds.
Tester.age = 20
print(Tester.msg) # You have lived for 630720000 seconds.

EDIT: I added a static method to recalculate the seconds value and call that from the __init__ and the age setter to reduce recalculations as per the OP's concern about those in the comment to another answer

shmee
  • 4,721
  • 2
  • 18
  • 27
  • The only thing necessary to be a property is `msg`. `age` can be a regular attribute, but there's nothing wrong with keeping it as a property as well. – Taku Apr 13 '18 at 08:36
  • @abccd That's basically right. I had the recalc of the seconds value inside the age.setter however, and I will bring it back since the OP was concerned about recalculating with every call to msg in a comment to the other answer. – shmee Apr 13 '18 at 08:38
1

age is the static class variable You can directly access the age variable using Class name

>>ClassTester.age=20

>>ClassTester.age
>>20

Static class variables in Python

Roushan
  • 4,074
  • 3
  • 21
  • 38
  • I would like to have the age variable non-static though. Which is what I need to learn of course, sorry I am very new to this – Frank Apr 13 '18 at 08:46
  • but there is no key word for that you can have than self.age initialized – Roushan Apr 13 '18 at 08:52
1

The solution is to set the variables in an object-level (not class-level). You do this by creating the variables inside the init area like below. You can then change the values externally.

Then you need to calculate whatever you want in a different function, using that variable. Then just call that function where you need it.

VERY IMPORTANT (the mistake I made): The init is written with TWO underscores _ x 2 on each side

class ClassTester:
    def __init__(self):
        self.age = 10

    def msg(self):
        self.seconds = self.age * 365 * 24 * 60 * 60
        self.txt = "You have lived for " + str(self.seconds) + " seconds."
        return self.txt

Tester = ClassTester()

Tester.age = 20
print(Tester.msg())
Tester.age = 30
print(Tester.msg())
Frank
  • 2,109
  • 7
  • 25
  • 48
0

You are doing it wrong, you define msg var as an attribute to the class, and it has been evaluated once, (when you instance the class) and no more change,

so when you call:

print(Tester.msg)

you are getting the first value,

what actully is nearest to your work is just make msg into method, simply:

class ClassTester:
    age = 10
    seconds = self.age * 365 * 24 * 60 * 60
    msg = "You have lived for " + str(seconds) + " seconds."
    def get_msg(self):
        seconds = self.age * 365 * 24 * 60 * 60
        self.msg = "You have lived for " + str(seconds) + " seconds."

Tester = ClassTester()

Tester.age = 20
Tester.get_msg()
print(Tester.msg)
Yasin Yousif
  • 969
  • 7
  • 23
  • I understand the answer but I don't feel like this is efficient. Now I have to recalculate before using msg again. In javascript the recalculation wouldn't be necessary – Frank Apr 13 '18 at 08:34
  • @Frank How would you get an updated value without re-calculating ? – jadsq Apr 13 '18 at 08:37
  • @Frank , the thing that there's no **Closure** in python , – Yasin Yousif Apr 13 '18 at 08:40
0

First, as was pointed out in comments, you should declare variables in __init__ constructor. As to your example, I'd suggest to make some more advanced steps, so consider following: you actually have only one important variable, which is self.age. Everything else is derived from it, and so you can lose consistency: for example, when changing self.age another variables will not be changed. Also, you can later on in your project change self.seconds variable, thus completely messing up your class instance. To solve that, have a look at property decorator:

class ClassTester:
    def __init__(self, age=10):
        self.age = age

    @property
    def seconds(self):
        return self.age * 365 * 24 * 60 * 60

    @property
    def msg(self):
        return "You have lived for {} seconds.".format(self.seconds)

There, @property is a handy decorator, which allows you to do address seconds and msg as attributes, but which will be always synced and consistent: first, you cannot set seconds attribute, thus it is protected. Second, changing age will affect all subsequent calls to msg and seconds.

Example usage:

>>> tester = ClassTester()
>>> tester.seconds
315360000
>>> tester.msg
'You have lived for 315360000 seconds.'
>>> tester.age = 23
>>> tester.seconds
725328000
>>> tester.msg

There is a lot more about @property, feel free to explore! For example, see this answer for some in-depth @property analysis.

Seer.The
  • 475
  • 5
  • 15
0

Old question, but still deserves a new answer.

It seems that what the OP wanted was to somehow change the value of a class variable in a way that the change could be felt by all objects of the class.

You can´t do it directly changing the value of the class variable, but if the value of the class variable is a mutable object then you can change the content of that object instead and the change will be seem by all instances of the class.

class A:
    my_magic = dict()

a = A()
b = A()
c = A()

a.my_magic['x'] = "something"
b.my_magic['y'] = "other thing"

print(c.my_magic)

results

{'x': 'something', 'y': 'other thing'}
plpsanchez
  • 339
  • 4
  • 10