I am working on the exorcism.io clock exercise and I can not figure out why this test is failing. The results look identical and even have the same type.
Here is my code:
class Clock:
def __init__(self, h, m):
self.h = h
self.m = m
self.adl = 0
def make_time(self):
s = self.h * 3600
s += self.m * 60
if self.adl: s += self.adl
while s > 86400:
s -= 86400
if s == 0:
return '00:00'
h = s // 3600
if h:
s -= h * 3600
m = s // 60
return '{:02d}:{:02d}'.format(h, m)
def add(self, more):
self.adl = more * 60
return self.make_time()
def __str__(self):
return str(self.make_time()) # i don't think I need to do this
if __name__ == '__main__':
cl1 = Clock(34, 37) #10:37
cl2 = Clock(10, 37) #10:37
print(type(cl2))
print(cl2, cl1)
print(cl2 == cl1) #false
A custom class without an __eq__ method defaults to testing for identity. That is to say, two references to an instance of such a class are only equal if the reference they exact same object.
You'll need to define a custom __eq__ method that returns True when two instances contain the same time:
def __eq__(self, other):
if not isinstance(other, Clock):
return NotImplemented
return (self.h, self.m, self.adl) == (other.h, other.m, other.adl)
By returning the NotImplemented singleton for something that is not a Clock instance (or a subclass), you let Python know that the other object could also be asked to test for equality.
However, your code accepts values greater than the normal hour and minute ranges; rather than store hours and minutes, store seconds and normalise that value:
class Clock:
def __init__(self, h, m):
# store seconds, but only within the range of a day
self.seconds = (h * 3600 + m * 60) % 86400
self.adl = 0
def make_time(self):
s = self.esconds
if self.adl: s += self.adl
s %= 86400
if s == 0:
return '00:00'
s, h = s % 3600, s // 3600
m = s // 60
return '{:02d}:{:02d}'.format(h, m)
def __eq__(self, other):
if not isinstance(other, Clock):
return NotImplemented
return (self.seconds, self.adl) == (other.seconds, other.adl)
Now your two clock instances will test equal because internally they store the exact same time in a day. Note that I used the % modulus operator rather than a while loop and subtracting.
Related
Im trying to subtract with prefixes as objects.
Here is my code
class Prefix:
def __init__(self, m=0, cm=0):
self.m = m
self.cm = cm
def __sub__(self, other):
centim = self.cm - other.cm
meter = (self.m - other.m) - abs(centim/100)
if meter < 1:
centim = m*100
meter = 0
return Prefix(meter, cm)
Im trying to subtract in a way which creates a negative centimeter value and take 1m from the meter object such that this is fulfilled
Prefix(2, 20) - Prefix(1, 30) == Prefix(0, 90)
First, keep in mind that for a given length, everything to the right of the hundreds place goes into cm, and everything at it or to its left gets divided by 100, and then goes into m.
Given this, we can recast the problem as converting both Prefix objects into their full lengths, performing calculations there, and then creating a new Prefix from the result:
class Prefix:
def __init__(self, m=0, cm=0):
self.m = m
self.cm = cm
def __sub__(self, other):
self_length = self.m * 100 + self.cm
other_length = other.m * 100 + other.cm
result_length = self_length - other_length
result_m, result_cm = divmod(result_length, 100)
return Prefix(result_m, result_cm)
result = Prefix(2, 20) - Prefix(1, 30)
print(result.m, result.cm)
Output:
0 90
Since we've come this far, we might as well store a "length" and overload __repr__ to make the result prettier:
class Prefix:
def __init__(self, length):
self.length = length
def __sub__(self, other):
result_length = self.length - other.length
return Prefix(result_length)
def __repr__(self):
result_m, result_cm = self.split_up(self.length)
if result_m > 0:
return f'{result_m}m {result_cm}cm'
else:
return f'{result_cm}cm'
#staticmethod
def split_up(length):
return divmod(length, 100)
Prefix(220) - Prefix(130)
Output:
90cm
So I defined a function such that it returns a sum of two Time objects as a new Time object, but the sum of the minutes in that resulting Time object cannot exceed 59 and I don't know how to implement that.
Here's my code:
from timer import Time
sum_of_time = Time(0, 0)
sum_of_time.hours= time1.hours + time2.hours
sum_of_time.minutes = time1.minutes + time2.minutes
return sum_of_time
if sum_of_time.minutes >= 60: # where I try to make minutes within 0 to 59
sum_of_time.minutes = sum_of_time.minutes - 60
sum_of_time.hours = sum_of_time.hours + 1
return sum_of_time
This is what datetime.timedelta is used for.
from datetime import timedelta
delta1 = timedelta(hours=1, minutes=30)
delta2 = timedelta(minutes=31)
sum_time = delta1 + delta2
print(sum_time) #2:01:00
You want to get the quotient and remainder when dividing by 60; the quotient gets added to the hour, and the remainder is used as the number of minutes.
sum_of_time = Time(0,0)
hours = time1.hours + time2.hours
minutes = time1.minutes + time2.minutes
q, r = divmod(minutes, 60) # e.g. divmod(117, 60) == (1, 57)
hours += q
minutes = r
sum_of_time.hours = hours
sum_of_time.minutes = minutes
This is logic that should probably be implemented by methods of the Time class. For example:
class Time:
def __init__(self, h, m):
self.hours = 0
self.minutes = 0
# For simplicity here, assume other is an instance of Time
def __add__(self, other):
hours = self.hours + other.hours
minutes = self.minutes + other.minutes
q, r = divmod(minutes, 60)
return Time(hours + q, r)
sum_of_time = time1 + time2
class Time:
def __init__(self,h,m):
self.h = h
self.m = m
q, r = divmod(m, 60)
self.h += q
self.m = r
def __add__(self, o):
return Time(self.h+o.h,self.m+o.m)
def __str__(self):
return "{0}:{1}".format(self.h,self.m)
t1 = Time(1,60)
t2 = Time(1,60)
print (t1+t2)
I am trying to use the .split method within my "clean" method definition in my "Time" class. Every time I execute the function I am prompted with the error 'Time' object has no attribute 'split'. Here is what I have so far:
def __init__(self, hours=0, minutes=0, seconds=0):
'''hours represents time in hours, minutes represents time in
minutes, and seconds represents time in seconds'''
self.H = hours
self.M = minutes
self.S = seconds
self.clean()
def clean(self):
'''Adjust Time object so that the number of seconds and minutes
is between 0 and 59'''
if isinstance(self.H,str) == True:
self.H = self.split(':')
self.H = self[0]
self.M = self[1]
self.S = self[2]
return self.__str__()
else:
self.S = int(self.H * 3600 + self.M * 60 + self.S)
self.M = int(self.S / 60)
self.H = int(self.M / 60)
self.M = self.M % 60
self.S = self.S % 60
return self.__str__()
So Time("2:33:04") returns a Time object: 02:33:04
Is there any way around this or do I have to define .split within my Time class?
It looks like what you are trying to do is get the class members H from your Time object and split it. I'm not sure this is a great OO design, but if you must do that, it looks like the problem is you are calling split on the class instead of the class member H.
So, in the end you should call self.H.split(':') if you are storing the time as an encoded string in H.
So the code would have to change to:
time_attrs = self.split(':')
self.H = time_attrs[0]
self.M = time_attrs[1]
self.S = time_attrs[2]
return self.__str__()
I am writing a "clean" method for a "Time" class. The clean method would take a Time object and would make sure that the number of seconds and minutes is between 0 and 59. I am running into an error with recursion. Here is what I have so far:
def clean(self):
'''Adjust Time object so that the number of seconds and minutes
is between 0 and 59'''
if self.S < 60:
self.S
else:
self.M = int(self.S/60)+self.M
self.S = self.S%60
if self.M < 60:
self.M
else:
self.H = int(self.M/60)+ self.H
self.M = self.M%60
if isinstance(self.H,float) == True:
self.S = self.H * 3600
self.clean()
else:
self.H
return self.__str__()
In your code:
if isinstance(self.H,float) == True:
self.S = self.H * 3600
self.clean()
else:
You don't change self.H if this is True, so it will always be true. I'm guessing you want to set self.H = 0 or something like that (since you converted it all to seconds).
Also, in your if statements, having 'self.S' doesn't actually do anything, so you should write:
if self.S < 60:
self.S
else:
self.M = int(self.S/60)+self.M
self.S = self.S%60
as
if self.S >= 60:
self.M = int(self.S/60)+self.M
self.S = self.S%60
Also, in your recursive check, you destroy your previous value of self.S, did you mean to add that?
EDIT:
You don't specify in your question, but from your code it looks like you're assuming the time values (S, M, H) are valid and you're just shifting the quantities around to get the same total time with S < 60 and M < 60. If this is the case, then you can easily eliminate the recursion by converting everything to seconds at the beginning and then go through your S and M steps:
self.S = self.H * 3600 + self.M * 60 + self.S
self.M = int(self.S / 60)
self.H = int(self.M / 60) # or = int(self.S / 3600)
self.M = self.M % 60
self.S = self.S % 60
It looks like the only place where recursion happens is here:
if isinstance(self.H,float) == True:
self.S = self.H * 3600
self.clean()
If you make self.H not a float then it won't repeat:
if isinstance(self.H,float) == True:
self.S = self.H * 3600
self.H = int(self.H) # add this line
self.clean()
Better yet, adjust the values at the top so you don't need recursion:
# move this to the first lines of your method
if isinstance(self.H,float) == True:
self.S = self.H * 3600
I am working with a custom number type which is best thought of as YearQuarter, (i.e. 20141, 20142, 20143, 20144, 20151, 20152, ...), or as I label it, quarter_code, q_code for short. Its incrementing function would be something like:
def code_sum(q_code, n):
q_code_year, q_code_quarter = q_code // 10, q_code % 10
n_year, n_quarter = (n // 4), (n % 4 - 1)
quarters = q_code_quarter + n_quarter
years = q_code_year + n_year + quarters // 4
return years * 10 + quarters % 4 + 1
# code_sum(20141, 1) = 20142, code_sum(20144, 1) = 20151
# code_sum(20144, -1) = 20143, code_sum(20151, -1) = 20144
# NOTE: code_sum(20147, 0) = 20153
I want to warn or raise exceptions for numbers which don't conform to the form year*10 + number_of_quarters. It easy to write and call a check function, but I'm wondering if constantly calling check is the best approach when using quarter_codes in many different functions. E.g.
def foo(qc1, qc2, qc3):
qc1, qc2, qc3 = check(qc1, qc2, qc3)
# do something
return bar
def foo2(qc, arg1, arg2) ...
qc = check(qc)
return 42
def fooN(qc1, qc2, arg1):
qc1, qc2 = check(qc1, qc2)
And so on. Here is a short check function as an example.
def check(*args):
checked = tuple()
for q_code in args:
if q_code % 10 > 4:
while q_code % 10 > 4:
q_code += 6
print('Number of quarters > 4. Using {}'.format(q_code))
checked += (q_code, )
else:
checked += (q_code, )
return checked[0] if len(checked) == 1 else checked
It seems a little laborious to create class YearQtr although maybe I am missing something here altogether. My question really boils to: how far should I go to creating a custom number class or type? And how would I do that?
I hope this can help you
class QuarterCode(object):
"""docstring for QuarterCode"""
#property
def value(self):
return self.__value;
#value.setter
def value(self, value):
assert 1 <= value%10 <=4, "Number of quarters differs from {1,2,3,4}"
self.__value = value
def __init__(self, value):
self.value = value
def code_sum(self, n):
q_code_year, q_code_quarter = self.value // 10, self.value % 10
n_year, n_quarter = (n // 4), (n % 4 - 1)
quarters = q_code_quarter + n_quarter
years = q_code_year + n_year + quarters // 4
self.value = years * 10 + quarters % 4 + 1
You can use this class as follows:
>>>q1 = QuarterCode(20142)
>>>q1.value
20142
>>>q1.code_sum(10)
20164
>>>q1.value = 20145
AssertionError: Number of quarters differs from {1,2,3,4}