Frank
Frank

Reputation: 2431

Change Python Class Variable Value

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)

Upvotes: 2

Views: 11850

Answers (6)

plpsanchez
plpsanchez

Reputation: 389

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'}

Upvotes: 0

Seer.The
Seer.The

Reputation: 487

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.

Upvotes: 0

Frank
Frank

Reputation: 2431

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())

Upvotes: 1

shmee
shmee

Reputation: 5101

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

Upvotes: 3

Roushan
Roushan

Reputation: 4420

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

Upvotes: 1

Yasin Yousif
Yasin Yousif

Reputation: 967

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)

Upvotes: 0

Related Questions