1

Hello everyone I have a class that works with time, for example you enter two times and you can sum them (for now it doesn't matter that it goes beyond 23:59:59) or multiply them etc. The default input are zeros. There is also a function that returns current time. I can call time = Time() which returns 0:00:00, time = Time(12) returns 12:00:00.

The problem that I have is, I want to call time = Time('now') and it should store there current time using function now(), edits should be done in __init__() (if you don't like now() you can change it). But if i put time_now = '' as first parameter it doesn't work, same when i put it as a last, because when I write time = Time('now') it takes string 'now' and wants to put it to hours.

I know there are time modules which can do this, but this is for learning purposes.

My code:

import random, time

class Time:

    def __init__(self, hours=0, minutes=0, seconds=0, time_now=''):
#       if time_now == 'now':
#           now()
        time = abs(3600*hours + 60*minutes + seconds)
        self.hour = time//3600
        self.min = time//60%60
        self.sec = time%60

    def __repr__(self):
        return '{}:{:02}:{:02}'.format(self.hour,self.min,self.sec)

    def __add__(self, other):
        if isinstance(other, Time):
            return Time(self.hour+other.hour, self.min+other.min, self.sec+other.sec)
        if isinstance(other, int):
            return Time(self.hour, self.min, self.sec+other)

    __radd__ = __add__

    def __sub__(self, other):
        return Time(seconds = self.__int__() - other.__int__())

    def __mul__(self, other):
        return Time(self.hour*other.hour, self.min*other.min, self.sec*other.sec)

    def __int__(self):
        return 3600*self.hour + 60*self.min + self.sec

    def __eq__(self, other):
        return self.__int__() == other.__int__()

    def __lt__(self, other):
        return self.__int__() < other.__int__()

    #writing out hour/min/sec what we want
    def __getitem__(self, key):
        if key == 0:
            return self.hour
        if key == 1:
            return self.min
        if key == 2:
            return self.sec



#time now

def now():
    a=(time.localtime()[3:6])
    return Time(a[0],a[1],a[2])
ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
Matis
  • 611
  • 2
  • 11
  • 27
  • 1
    [Good video](https://www.youtube.com/watch?v=-5wpm-gesOY) on that :) – wenzul Dec 02 '14 at 20:19
  • What sense does it make to multiply times? The class looks a lot inconsistent to me also for learning. May there exists a better example to overwrite the \_\_ methods than time... – wenzul Dec 02 '14 at 20:24
  • @wenzul Will watch that video later, thanks ! About the multiplying time, it is silly, but that's what I had to do :D – Matis Dec 02 '14 at 20:45
  • 1
    Ok, sometimes we all have silly things to do... – wenzul Dec 02 '14 at 20:47

4 Answers4

1

You can use a call with named parameters: Time(time_now='now'), but that looks too reiterative.
If I was using named parameters, I'd instead change it to a boolean and use something like Time(now=True), which looks a bit cleaner.

But I think a better alternative is to make your "now" objects be constructed from your now() function, which I'd move to a static method:

class Time(object):
    #...
    @classmethod
    def now(cls):
        a=(time.localtime()[3:6])
        return cls(a[0],a[1],a[2])

So you'd construct your now objects like x = Time.now() instead of x = Time('now')

outlyer
  • 3,933
  • 2
  • 14
  • 18
  • works great ! thanks. Will wait if someone can come up with another solution closer to what I wanted if no, I flag this as an answer. – Matis Dec 02 '14 at 20:25
1

Python doesn't have method overloading, so your only option is to "play" with the arguments:

You can do something that IMHO is very bad (downgraded very bad to meeeeh... so, so after reading @ivan-pozdeev's comments in this answer)

class Time:
    def __init__(self, hours=0, minutes=0, seconds=0, time_now=''):
        if hours == 'now':
            tmp_t = now()
            self.hour = tmp_t.hour
            self.min = tmp_t.min
            self.sec = tmp_t.sec
        else:
            t = abs(3600*hours + 60*minutes + seconds)
            self.hour = t//3600
            self.min = t//60%60
            self.sec = t%60

That... well, that works:

>>> a = Time('now')
>>> print vars(a)
{'sec': 20, 'hour': 15, 'min': 18}
>>>
>>> a = Time(hours=19, minutes=4, seconds=5)
>>> print vars(a)
{'sec': 5, 'hour': 19, 'min': 4}

But that leaves the code in a very weird state. Is very difficult to read. I certainly would try to come with a different approach altogether...

I also changed the time variable within the __init__ to t because that conflicted with the time name from import time

Community
  • 1
  • 1
Savir
  • 17,568
  • 15
  • 82
  • 136
  • Hi, thank you for your answer ! Sadly I am getting an error, have you used mine function now() or did you edit it ? `AttributeError: 'Time' object has no attribute 'localtime'` – Matis Dec 02 '14 at 20:39
  • I (kinda) used yours: I started my answer using `datetime`. Then I realized about your `now` function. I took what it did and adapted the code accordingly. No editing done. *:-)* – Savir Dec 02 '14 at 20:41
  • It sounds like somewhere in your code you're making a variable `time` that overwrites the `time` module (the `time` that came from `import time`). Did you do something like `time = Time(...)` ? – Savir Dec 02 '14 at 20:44
  • Yeah i did that, I am sorry :D Now I know I can't do that, will remember it. Thank you ! – Matis Dec 02 '14 at 20:46
  • No prob. Yeah, I just tested fully, and it does work. But yeah... use this for *learning purposes* only :-) – Savir Dec 02 '14 at 20:47
  • Just one more question "tmp_t" is just a regular variable, nothing special right ? Because it is highlighted in green/blue here so it looks like as if it was "something more". – Matis Dec 02 '14 at 20:48
  • 1
    Nothing special, no... Just a variable. It has to be there because you can't swap `self` with a new instance (the instance that would be returned from the `now()` function) within the `__init__` (you can't simply do `self = now()` inside the `__init__`, that's what I mean). You need to put the instance created by `now()` in a temporary variable and copy its relevant fields into the relevant fields of `self` – Savir Dec 02 '14 at 20:52
  • I don't find it very bad. Eliminate a little duplication and that'll be completely fine. – ivan_pozdeev Dec 02 '14 at 20:53
  • Hehe. Thanks, @ivan_pozdeev. Well... what I mean by *bad* is that if I see that code, and see `Time(hours=0...)`, I'm not gonna expect that I can use it doing `Time('now')` (I'll expect a class that only accepts integers). It's... weird – Savir Dec 02 '14 at 20:56
  • 1
    @BorrajaX That's what docstrings are for... and that's why they're just besides the argument list! – ivan_pozdeev Dec 02 '14 at 21:09
1

For flexible argument handling beyond what's available with static arguments, you may use *args and/or **kwargs:

def __init__(self, *args, **kwargs):
    a_seq=('hours','minutes','seconds')
    if len(args)>0 and args[0]=='now':
         now_=now()
         self.__dict__.update(a,getattr(now_,a) for a in a_seq)
    else:
        t=0
        for i,a in enumerate(a_seq):
            try: v=args[i]
            except IndexError: v=kwargs.get(a,0)
            else: if a in kwargs: raise TypeError("multiple values given for argument `%s'"%a) 
            t+=v
            t*=60
        for a in a_seq[-1:0:-1]:
            setattr(self,a,t%60)
            t//=60
        setattr(self,a_seq[0],t)

(I renamed the fields to the same names as arguments to make it easier to use reflection. Reflection is used here to eliminate code duplication (without it, it'd be about 1.5 times longer).)

In this particular case, though, BorrajaX's solution is much simpler because the deviation from a normal static argument list here is only minor (in fact, the try-except-else in the 2nd half of my code do nothing but emulate it).

Community
  • 1
  • 1
ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
  • Yep, going with `*args` and `**kwargs` is what I'd done to avoid considering what I did in my answer *bad code* I usually try not to deviate too much from the original OP's code (that's why my answer looks the way it does), but this way does offer better control of the arguments. – Savir Dec 05 '14 at 13:28
0

Almost never try to implement your own time module. It's time consuming and defective. Use time or datetime. All your needs are already implemented somehow.

wenzul
  • 3,948
  • 2
  • 21
  • 33