Which part of the loop is it going through in this class frame?

Cameron Simpson cs at cskk.id.au
Wed Mar 7 17:44:02 EST 2018


On 07Mar2018 16:57, C W <tmrsg11 at gmail.com> wrote:
>I am new to OOP. I'm a bit confused about the following code.
>
>class Clock(object):
>    def __init__(self, time):
>        self.time = time
>    def print_time(self):
>        time = '6:30'
>        print(self.time)
>
>clock = Clock('5:30')
>clock.print_time()
>5:30
>
>I set time to 6:30, but it's coming out to 5:30. I guess it's because I
>passed in 5:30, so, it's replaced?

No, it is because the scope isn't what you think. In the print_time() method, 
the variable "time" is just a local variable. It is unrelated the the instance 
("self")'s "self.time" value. So you're printing self.time, which is unchanged 
from when the object was initialised. Try adding:

    print(time)

to the method and compare the values.

>How does line-by-line execution run inside a frame? How does __init__ work?
>I understand you must have __init__. Is it run before print_time(), if so,
>why don't I just set self.time = '6:30' instead of self.time = time?

A "frame" is normally what we refer to as a "stack frame": if it a little 
record which the language implementation uses to keep track of nested function 
calls. When you call a function, a new stack frame is pushed, which amongst 
other things contains a reference to the local variables alive during the call.  
When you return from a function the frame is popped and discarded.

The __init__ method is called as the initial setup of an object, just after it 
is created. When you program went:

  clock = Clock('5:30')

a new "Clock" instance was made. Then that instance's __init__ method was 
called with the '5:30' which you passed to the class name. That is how the 
string '5:30' gets stored as the .time attribute of the instance.

The reason you say "self.time = time" is that you want to store the value 
passed to the constructor. It might be any time.

  clock1 = Clock('5:30')
  clock2 = Clock('5:45')

Now you have to distinct Clock instances, one holding the time '5:30' and one 
holding the time '5:45'.

Your print_time method is a little odd. Normally such a function would not take 
an argument, just print the internal value from the Clock instance. So 
typically it would look like this:

  def print_time(self):
    print(self.time)

See that is doesn't take a time parameter?

Regarding setting self.time directly to '6:30', that depends what you want to 
do with it. A "Clock" might have different purposes, and you should decide what 
the purpose of yours actually is.

As an example, Python's "datetime" module contains classes for dates and times 
and "datetimes" containing a date and a clocklike time. Normally these are made 
once and NOT CHANGED. So they're like a special kind of number. They have 
various methods for computing, for you, things like the day of the week or 
writing the date or time out in particular formats, or comparing two dates.

But a sometimes you don't want a fixed timestamp. You might be modelling a 
clock, whose value changes as time passes. In that case you may well want to 
adjust the values inside it.

You're print_time method seems odd because it does 2 things: it prints a time, 
but it also seems to want to change the internal time. It is usually good to 
separate such things.

Finally, setting the time attrubute directly is a reasonable thing to do, but 
it varies with (a) your needs and (b) the OOP paradigm you're following and how 
strict you're being.

In a "pure" OOP environment one never sets the internal state of an object 
directly, so one never goes:

  clock1.time = '5:55'

Instead you would always go:

  clock1.set_time('5:45')

and be _unaware_ of how the object may be storing that vaue internally. Some 
languages will enforce this, and provide no way to modify an object except via 
"setter" methods. This has some advantages:

You can switch out objects implementations provided they provide the same 
setter methods - your calling code doesn't know or care how the clock manages 
the internal values.

In principle you can make objects "remote" i.e. not local im-memory things at 
all, if that is sensible. This is why some OOP descriptions treat methods as 
messages: you send a "set_time" request to the clock with a new time, or you 
send a "get_time" request to a clock the retrive the current time.

Also, because everything is done through setter or getter methods, you can't 
accidentally damage the object internal state by setting to nonsense. The 
setter methods should sanity check the incoming values, and the getters will 
always return sensible values. If the caller expects to directly modify the 
internals then they might set the minutes to negative values, or values >= 60, 
or some other nonsensical value. By using methods all the responsibility is 
inside the class where it is easy to keep correct.

Python lets you use that paradigm, but it doesn't prevent you directly 
accessing or changing the internals. The philosophy is that you should exercise 
restraint. So you _can_ directly set the clock.time value; whether that is a 
good idea depends on the clock implementation and your program.

In Python there is a naming convention: values which may be directy accessed 
are given ordinary names (like ".time") and values which are part of the 
internals, subject to change, or tricky to keep correct are given names 
starting with underscores (like "._time"). If you're accessing a _name that is 
a clue that you're probably doing the wrong thing (if your outside the class - 
obviously the class itself must work on these values).

Cheers,
Cameron Simpson <cs at cskk.id.au> (formerly cs at zip.com.au)



More information about the Python-list mailing list