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.
Related
here's a newb Python question that I'm sure is a no-brainer for the pros. I have an object OddStream that is called from the function print_from_stream to create a list of odd numbers starting from 1. It works find the first time but when calling it a second time the new list starts from the last number of the first list + 2.
What should I write to reset OddStream? BTW, this is only a portion of the code, but it's the part that matters. Thanks if you can help.
class OddStream(object):
def __init__(self):
self.current = 1
def get_next(self):
to_return = self.current
self.current += 2
return to_return
def print_from_stream(n):
for _ in range(n):
print(stream.get_next())
I guess the stream instance of the OddStream class is being instantiated like this:
class OddStream(object):
# ...
stream = OddStream() # Breakpoint 1
def print_from_stream(n):
for _ in range(n):
print(stream.get_next()) # Breakpoint 2
At breakpoint 1 stream has been instantiated and stream.current = 1
At breakpoint 2 stream.get_next() has been called and it has increased stream.current
So the behaviour you described should be expected.
To avoid such behaviour, add a reset() method to OddStream and call it from print_from_stream():
class OddStream(object):
# ...
def reset(self):
self.current = 1
stream = OddStream()
def print_from_stream(n):
stream.reset()
for _ in range(n):
print(stream.get_next())
print("First run:")
print_from_stream(10)
print("Second run:")
print_from_stream(10)
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
As I'm new in python maybe I'm missing the obvious.
I've wrote the updateRates method for the class which counts fps in this RDP program.
When I try to access some attributes from inside the call from the timer method there is this weird Error:
"AttributeError: type object 'RDCToGUI' has no attribute '_cnt_framerate'"
it is really obvious this class inherits this attribute, but in the Timer thread it doesn't seem to be there anymore
So the problem is: I can call the inherited methods of the super (e.g. self.rates) but inside some attributes aren't present in the method
class RDCToGUI(clientProtocol.rdc):
def __init__(self):
clientProtocol.rdc.__init__(self)
self.num = 0
self.count = 0
self.framerate_before = 0
def updateRates(self, framerate_label, datarate_label):
divisor = 1
datarate_text = "b/s"
self.rates(self)
framerate = self.framerate
datarate = self.datarate
if self.logged_in == 1 and self.framerate_before == 0 == framerate:
self.framebufferUpdateRequest(
width=800, height=800)
if datarate > 1000000:
divisor = 1000000
rateText = "Mb/s"
elif datarate > 1000:
divisor = 1000
rateText = "Kb/s"
self.framerate_before = framerate
framerate_label.setText(f"Framerate: {framerate}")
datarate_label.setText(
f"Datarate: {round(datarate / divisor, 2)} {datarate_text}")
threading.Timer(1, self.updateRates, args=(self,
framerate_label, datarate_label)).start()
class rdc(Protocol):
def __init__(self):
self._packet = ""
self._expected_len = 0
self._cnt_framerate = 0
self._cnt_datarate = 0
self.framerate = 0
self.datarate = 0
self.logged_in = 0
def rates(self):
self.setRates(self)
self.resetCounter(self)
def setRates(self):
self.framerate = self._cnt_framerate
self.datarate = self._cnt_datarate
def resetCounter(self):
self._cnt_datarate = 0
self._cnt_framerate = 0
def incFramerate(self):
self._cnt_framerate += 1
def addDataSize(self, size):
self._cnt_datarate += size
The classes have more methods and members than what I showed, but wanted to cut it to the most important stuff.
Tried to put the method in different classes and played around with the names of the attributes
class rdc is in another file which is imported as clientProtocol.
This seems like an inheritance issue. Maybe try replacing
clientProtocol.rdc.__init__(self)
with
super(RDCToGUI, self).__init__()
I'm not an expert on inheritance either and haven't really used it, but there is a ton to read on the super() function and you could maybe start at Understanding Python super() with __init__() methods and just follow it down the rabbit hole :)
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'}
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!