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)
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
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
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())
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)
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.
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'}
Related
With my limited understanding of #property,#setter, and #getter, I came up with following code.
class BitCounts:
sign_bit = 0
exponent_bits = 0
mantissa_bits = 0
_total_bits = 0
#property
def total_bits(self):
return self._total_bits
#total_bits.setter
def total_bits(self):
self._total_bits = self.sign_bit + self.exponent_bits + self.mantissa_bits
class Single(BitCounts):
sign_bit = 1
offset = 0x7F
exponent_bits = 8
mantissa_bits = 23
_total_bits = BitCounts.total_bits
class Double(BitCounts):
sign_bit = 1
offset = 0x400
exponent_bits = 11
mantissa_bits = 52
_total_bits = BitCounts.total_bits
My intention is to use the subclass Single and Double in other functions as a set of options like so, for example:
def some_function(option=Single):
print("exponent bit counts are: %d", option.exponent_bits)
print("mantissa bit counts are: %d", option.mantissa_bits)
print("total bit counts are: %d", option.total_bits)
I would like total_bits to be automatically recalculated using values from subclass Single or Double.
I am trying to avoid extra functions to perform the calculation at subclass level.
With above codes, by calling Single.total_bits, or Double.total_bits, I am only getting a message saying <property object at 0x000002258DF7CB30>, what did I do wrong, and how can I fix it?
The way you are using subclasses with hard-coded static values suggests these should be instances not subclasses. This is also suggested by your temptation to use self even though you haven't made any instances. self refers to a particular instance.
Also, setters typically take a value as an argument. You don't have that in your setter because total_bits is completely dependent on other values. As such you should just move your setter calculation to the getter and return the result of the calculation.
Consider:
class BitCounts:
def __init__(self, sign,offset, exponent, mantissa):
self.sign_bit = sign
self.offset = offset
self.exponent_bits = exponent
self.mantissa_bits = mantissa
#property
def total_bits(self):
return self.sign_bit + self.exponent_bits + self.mantissa_bits
# now make two instances:
single = BitCounts(1, 0x7F, 8, 23 )
double = BitCounts(1, 0x400, 11, 52)
print(single.total_bits)
# 32
print(double.total_bits)
# 64
You can use:
class Single(BitCounts):
sign_bit = 1
offset = 0x7F
exponent_bits = 8
mantissa_bits = 23
_total_bits = BitCounts.total_bits
def get_total_bits(self):
# Update values here, example below
self._total_bits = self._total_bits + 1
return self._total_bits
Then call:
option = Single()
option.get_total_bits()
The problem here is that you are trying to call an instance method from a class.
class A:
#property
def foo(self):
return 1
print(A.foo) # prints an object of function "foo"
print(A().foo) # prints "1"
To accomplish this, at least from my knowledge you need to use a metaclass similar to what they do here: Per-class #property decorator in Python
I'm really struggling trying to understand functions and how they can be used to create attributes or properties (in this case i'm tasked with a person)
The following is my code to declare the person in a dictionary,
def format(person):
return "Name:\t" + person['name']
def display(person):
print(format(person))
person = {'name':"Bilbo Baggins"}
I then can call the display to produce;
Name = Bilbo Baggins
I then have to add to the dictionary a property storing the weight and height (say both of which are 0 for now) of my person, which i have done by;
person['height'] = 0
person['weight'] = 0
I now need to create a function (called create_person) that has the 3 parameters (name, height and weight), and modify my earlier code to use this function and alongside printing Name: Bilbo Baggins also prints the weight(in kg) and height(in m).
The overall aim of this is to find out the BMI of a person, BMI is calculated by weight/height2. I also need to add a function that takes a single person object from the previous dictionary/function as a parameter and returns the BMI of the person. Is that possible through linking the two?
class Person:
def __init__(self, name, height, weight):
self.name = name
self.height = height
self.weight = weight
# This is called when you print(PERSON OBJECT)
def __repr__(self):
return self.name + " " + self.height + " " + self.weight
def BMI(self):
return (self.weight/self.height)/self.height
This allows you to create a person like so:
person_one = Person("Bilbo", 177, 72.7)
print(person_one)
bmi = person_one.BMI()
class Time:
def __init__(self,x,y,z):
self.hour=x
self.minute=y
self.second=z
def __str__(self):
return "({:02d}:{:02d}:{:02d})".format(self.hour, self.minute, self.second)
def time_to_int(time):
minutes=time.hour*60+time.minute
seconds=minutes*60+time.second
return seconds
def int_to_time(seconds):
time=Time()
minutes,time.second=divmod(seconds,60)
time.hour,time.minute=divmod(minutes,60)
return time
def add_time(t1,t2):
seconds=time_to_int(t1)+time_to_int(t2)
return int_to_time(seconds)
start=Time(9,45,00)
running=Time(1,35,00)
done=add_time(start,running)
print(done)
I am new to python and i've been doing some practice lately.I came across a question and i've written the code for the same.But I am repeatedly getting an error: "add_time is not defined". I tried defining a main() method but then it doesn't print anything.Please help.
You haven't created an object to the above class.
Any function/method inside a class can only be accessed by an object of that class .For more information on the fundamentals of Object Oriented Programming, please check this page.
Meanwhile for this to work, define your class in the following way :
class Time:
def __init__(self,x=None,y=None,z=None):
self.hour=x
self.minute=y
self.second=z
def __str__(self):
return "({:02d}:{:02d}:{:02d})".format(self.hour, self.minute, self.second)
def time_to_int(time):
minutes=time.hour*60+time.minute
seconds=minutes*60+time.second
return seconds
def int_to_time(seconds):
time=Time()
minutes,time.second=divmod(seconds,60)
time.hour,time.minute=divmod(minutes,60)
return time
def add_time(t1,t2):
seconds=time_to_int(t1)+time_to_int(t2)
return int_to_time(seconds)
and outside the class block, write the following lines :
TimeObject = Time()
start=Time(9,45,00)
running=Time(1,35,00)
TimeObject.add_time(start,running)
print "done"
I however suggest you to write the add_time function outside the class because you are passing the objects to the class as the parameters to the function within the same class and it is considered as a bad design in object oriented programming.
Hope it helps. Cheers!
This works fine for me as long as you specified 3 args in your constructor
def int_to_time(seconds):
time=Time(0,0,0) # just set your 3 positionals args here
minutes,time.second=divmod(seconds,60)
time.hour,time.minute=divmod(minutes,60)
return time
Another way to avoid it could be:
class Time:
def __init__(self,x=0,y=0,z=0):
self.hour=x
self.minute=y
self.second=z
If you want to add your functions to your class (such as time_to_int, int_to_time or even add_time) then you will need to indent with one more level of 4 spaces and add self to your method parameters
Hii Mathers25,
I solve your problem try this below code to get the best output,
class TimeClass:
def __init__(self,x,y,z):
self.hour = x
self.minute = y
self.second = z
def __str__(self):
return "({:02d}:{:02d}:{:02d})".format(self.hour, self.minute, self.second)
def time_to_int(self,time):
minutes = (time.hour * 60) + time.minute
seconds = (minutes * 60) + time.second
return seconds
def int_to_time(self,seconds):
time = TimeClass(0,0,0)
minutes,time.second=divmod(seconds,60)
time.hour,time.minute=divmod(minutes,60)
return time
def add_time(self,t1,t2):
seconds = self.time_to_int(t1) + self.time_to_int(t2)
# Call method int_to_time() using self keyword.
return self.int_to_time(seconds)
# First time object create that time set value is 0 of hour,minute and second
TimeObject = TimeClass(0,0,0)
# After create second object
start=TimeClass(9,45,00)
# After create thired Object
running=TimeClass(1,35,00)
# Store the value which return by add_time()
done = TimeObject.add_time(start,running)
# Display the value of done variable
print(done)
class Employee:
def __init__(self):
self.wage = 0
self.hours_worked = 0
def calculate_pay(self):
return self.wage * self.hours_worked
alice = Employee()
alice.wage = 9.25
alice.hours_worked = 35
print('Alice:\n Net pay: {:.2f}'.format(alice.calculate_pay()))
barbara = Employee()
barbara.wage = 11.50
barbara.hours_worked = 20
print('Barbara:\n Net pay: {:.2f}'.format(barbara.calculate_pay()))
Works for me:
class C:
def f(a, b):
return a + b
x = f(1,2)
print(C.x)
but you should not do such things. Code in class-level is executing when class is "creating", usually you want static methods or class methods (decorated with #staticmethod or #classmethod) and execute code in some function/instantiated class. Also you can execute it on top (module) level if this is the simple script. Your snippet is "bad practice": class level (i'm talking about indentation) is for declarations, not for execution of something. On class-level is normal to execute code which is analogue of C macros: for example, to call decorator, to transform some method/attribute/etc - static things which are "pure" functions!
I'm trying to understand when you would want to have an instance of a class, and what exactly the difference is between these two variations of code:
Class A takes a time and and assigns it to a new variable, and then returns that new variable.
class A:
def B(time):
seconds = time
return seconds
seconds = A.B(int)
Class C takes a time in as well, but also creates an instance of function D (using self) and then returns self.seconds.
class C:
def D(self, time):
self.seconds = time
return self.seconds
seconds = C().D(int)
They end up returning the same values. I'm have difficulty understanding how these two pieces of code are different. Is one superior in certain situations vs. the other?
Thank you!
EDIT: Added calls to both functions.
Perhaps the following (very simplified) example helps to see where it can be useful to have an instance of a class:
class A:
minutes = 2
def time(seconds):
return 60*A.minutes + seconds
class B:
minutes = 2
def time(self, seconds):
return 60*self.minutes + seconds
print("Class:")
print(A.time(30))
a1 = A
a2 = A
a1.minutes = 3
a2.minutes = 4
print(a1.time(30)) # 60*4 + 30
print(a2.time(30)) # 60*4 + 30
print("Instance:")
print(B().time(30))
b1 = B()
b2 = B()
b1.minutes = 3
b2.minutes = 4
print(b1.time(30)) # 60*3 + 30
print(b2.time(30)) # 60*4 + 30
This results in:
Class:
150
270
270
Instance:
150
210
270
At the core, what you are asking for is understanding the difference between a Static Method and a normal Method, and what the advantages of OOP are.
A Static method can be called without instantiating the class, such as in your first example. This allows you to group related functions under a single header (the class), but it is not, technically, OOP.
However, a Static method has no instance data to act upon, as there is no instance.
A class instance allows you to keep track of Object specific data, and act upon them within your methods.
For example:
import time
class Stopwatch:
def __init__(self, name, start):
self.name = name
self.time = start
self.active = False
def start(self):
self.active = True
def stop(self):
self.active = False
def reset(self, time):
self.time = time
def isComplete(self):
return (self.time == 0)
def tick(self):
if self.active:
self.time -= 1
print(self.name+": ",self.time)
if self.time == 0:
print("Timer of " + self.name + " has reached zero!")
self.stop()
watchA = Stopwatch("A", 10)
watchB = Stopwatch("B", 5)
watchA.start()
watchB.start()
while (not watchA.isComplete()) or (not watchB.isComplete()):
time.sleep(1)
watchA.tick()
watchB.tick()
Running this outputs:
A: 9
B: 4
A: 8
B: 3
A: 7
B: 2
A: 6
B: 1
A: 5
B: 0
Timer of B has reached zero!
A: 4
A: 3
A: 2
A: 1
A: 0
Timer of A has reached zero!
Notice how the time of each watch is tracked separately, despite using the same code. This would not be possible using Static methods, as the data is attached not to the Class, but to the Instanced objects themselves.
I'm quite confused as to how I can pass variables which are set within a function to a method, which is within a class.
Here is the code in question:
class monster():
def __init__(self, health, name):
print(name + " has been born!")
self.name = name
self.health = health
self.stats(name, health)
def stats(self, name, health):
self.name = name
self.health = health
print(self.name + " - " + str(self.health) + " HP")
I create an instance of monster() with this line at the start of the program:
gameMonster = monster(20, name)
However, you can see that the function stats() is supposed to display the name and health of the monster, which is set earlier. The game is in a loop, so the stats() function would be called after each "turn". However, I need to pass the variables name and health to stats() to be able to display them, but can't seem to figure out how.
My question is:
How do I pass the variables, which are declared on initialisation of the method, to another method within the same class?
Any help appreciated.
You don't need to set them again, they already live on the instance:
class Monster(object):
def __init__(self, health, name):
print(name + " has been born!")
self.name = name
self.health = health
self.stats()
def stats(self):
print(self.name + " - " + str(self.health) + " HP")
And usage:
>>> monster = Monster(20, 'Boris')
Boris has been born!
Boris - 20 HP
>>> monster.stats()
Boris - 20 HP
>>> monster.health -= 5
>>> monster.stats()
Boris - 15 HP