Advanced Python
In-Class Exercise Solutions, Session 6

Notes if you are using Jupyter Notebook: to call exit() from a notebook, please use sys.exit() (requires import sys). If a strange error occurs, it may be because Jupyter retains variables from all executed cells. To reset the notebook, click 'Restart Kernel' (the circular arrow) -- this will not undo any changes made.

CLASSES - INSTANCES AND METHODS

Ex. 6.1 Define a class.

Use the class statement with name MyClass (or you may substitute a name of your own choice). To fill the otherwise empty block, use the pass statement. Initialize an instance of the class and print its type to show that it is an instance of the class.

Suggested Solution:
class MyClass:
    pass

x = MyClass()        # 'MyClass' instance / object
print(type(x))       # <class 'MyClass'>
 
Ex. 6.2 Create an 'instance' method.

Add to the class Hola below a new method, saludar(), that prints "bienvenidos a todos" (or whatever greeting you'd prefer) when it is called.

Suggested Solution:
class Hola:
    """ una clase que is amigable """
    def saludar(self):                 # self: 'Hola' instance / object
        print('bienvenidos a todos')

yo = Hola()                            # 'Hola' instance / object

yo.saludar()                           # (prints the greeting)
 
Ex. 6.3 Add argument and return value to method.

Add the method doubleit() below, that doubles the value passed to it.

Suggested Solution:
class Numbers:
    """ various number methods """
    def doubleit(self, val):        # self: 'Numbers' instance; val: int, 5 (1st call)
        return val * 2              # return 10

num = Numbers()                     # 'Numbers' instacne
val = num.doubleit(5)               # int, 10
print(val)

val2 = num.doubleit(100)            # int, 200
print(val2)
 

LAB 1

Ex. 6.4 Create a class Time that has a method get_time() that returns the current time. Call as shown.

(A string showing the current time can be obtained from the time module using time.ctime().)

Suggested Solution:
import time

class Time:
    def get_time(self):            # 'Time' instance / object
        return time.ctime()        # (returns string of current time)


obj = Time()                       # 'Time' instance / object
print(obj.get_time())              # (prints current time)
 
Ex. 6.5 Create a class Random that has a method get_rand(val) that returns a random number from 1 to the specified value. Call as shown.

(A random number can be generated with random.randint(x, y) where x and y are the minimum and maximum values.)

Suggested Solution:
import random

class Random:
    def get_rand(self, val):            # 'Random' instance / object
        return random.randint(1, val)   # returns randomly selected int

obj = Random()                          # 'Random' instance

val = obj.get_rand(5)                   # int, randomly selected between 1 and 5
print(val)

val2 = obj.get_rand(18)                 # int, randomly selected between 1 and 18
print(val2)
 
Ex. 6.6 Create a class Math with method add() that takes two integer arguments and returns the values summed.
Suggested Solution:
class Math:
    def add(self, val, val2):        # self: Math instance; val: int, 5; val2: int, 10
        return val + val2            # return 15

obj = Math()                         # 'Math' instance

mysum = obj.add(5, 10)               # int, 15
print(mysum)

mysum2 = obj.add(100, 150)           # int, 250
print(mysum2)
 

CLASSES - CONSTRUCTOR, CLASS ATTRIBUTES AND METHODS

Ex. 6.7 Demonstration: note the unique identifier of self and an instance from a class.

At the bottom of the code below, print obj, then call obj.something(), noting that this method prints self. Compare the hex codes that identify the instance. Next, print obj2 and call obj2.something(), and note the output, particularly the hex codes.

Suggested Solution:
class Do:
    def something(self):        # self:  'Do' instance / object
        print(self)


obj = Do()                      # 'Do' instance / object
obj2 = Do()                     # 'Do' instance / object

print(obj)                      # prints this instance
obj.something()                 # also prints this instance
print()

print(obj2)
obj2.something()
 
Ex. 6.8 Create an __init__() method.

Add a method to the below class, __init__(self) that inside the function announces and prints the argument self, i.e. print(f'self: {self}'). Construct 2 new instances, and then print each instance. Put a blank line between each instance.

Suggested Solution:
class Be:
    """ this class is something! """
    def __init__(self):                # 'Be' instance / object
        print(f'self:    {self}')


obj1 = Be()                            # 'Be' instance / object
print(f'object:  {obj1}')

print()

obj2 = Be()                            # 'Be' instance / object
print(f'object:  {obj2})
 
Ex. 6.9 Set an instance attribute in __init__().

Create a method __init__(self, num) that sets numin self as a .value attribute. At bottom, print obj.value to see the value.

Suggested Solution:
class Live:
    """ a class that just wants to live """
    def __init__(self, num):        # self:  'Live' instance; num: int, 5
        self.value = num            # (set .value attribute of self to 5)


obj = Live(5)                       # 'Live' instance / object

print(obj.value)
 
Ex. 6.10 Create a "getter" method.

Create a method get_value() that returns the .value attribute from the instance.

Suggested Solution:
class Say:
    def __init__(self, val):        # self: 'Say' instance; val: int, 100
        self.thisval = val          # (set .value attribute of self to 100)

    def get_value(self):            # self:  'Say' object / instance
        return self.thisval         # return 100


obj = Say(100)                      # 'Say' object / instance

vl = obj.get_value()                # int, 100
print(vl)
 
Ex. 6.11 Demonstrate class attributes.

In the class below, set a class variable cvar to value 1000. Print the value of cvar in three places: 1) instance a (a.cvar); 2) instance b (b.cvar); 3) the class itself (Something.cvar) Also print the .attr attribute from each of the two instances.

Suggested Solution:
class Something:
    cvar = 1000                    # int, 1000

    def __init__(self, xx):        # self: 'Something' instance; xx: str, 'hi'
        self.attr = xx             # (set .attr instance of self to 'hi')

a = Something('hi')                # 'Something' instance / object
b = Something('there')             # 'Something' instance / object


print(a.cvar)                      # 1000
print(b.cvar)                      # 1000
print(Something.cvar)              # 1000

print(a.attr)                      # hi
print(b.attr)                      # there
 
Ex. 6.12 Create a class method.

Add a class method classincrement(cls) that uses its cls argument to increment the cattr class variable (cattr will be found to be an attribute of cls. Call classincrement() through the instance obj as well as through the class MyClass. The values printed below should both be 1. Before this can work as shown, however, you must decorate classincrement() with @classmethod.

Suggested Solution:
class MyClass:

    cattr = 0                            # int, 0

    @classmethod                         #
    def classincrement(cls):             # cls:  'MyClass' class object
        cls.cattr = cls.cattr + 1        # (increment .cattr attribute of class object)

obj = MyClass()                          # 'MyClass' instance / object


obj.classincrement()

print(obj.cattr)                         # 1
print(MyClass.cattr)                     # 1
 
Ex. 6.13 Create a static method.

To the below class add the static method ftoc(temp) which converts a temperature in Fahrenheit to Celcius. The formula is (temp - 32) * 5 / 9 To be a static method, the method must not take self as an argument, and must be decorated with @staticmethod.

Suggested Solution:
class Forecast:

    def __init__(self, forecast, high=0, low=0):    # self: 'Forecast' instance;
                                                    # forecast: str, 'Light rain';
                                                    # high: int, 62
        self.text = forecast                        # sets .text attribute
        self.hightemp = high                        # sets .hightemp attribute
        self.lowtemp = low                          # sets .lowtemp attribute

    @staticmethod
    def ftoc(temp):                                 # temp: int, 32
        return (temp - 32) * 5 / 9                  # returs 0


t = Forecast('Light rain', high=62, low=48)         # 'Forecast' instance/object

print(t.ftoc(32))                                   # 0
print(t.ftoc(212))                                  # 100
 

LAB 2

Ex. 6.14 Create a class Name that allows you to store a person's first and last name.
Suggested Solution:
class Name:
    def __init__(self, fname, lname):
        self.first = fname
        self.last = lname
 
Ex. 6.15 Continuing the Name class, add a method .get_name() that returns the full name as 'first name last name'. Finally, add another method .get_rname() that returns the name in reverse order (last, first).
Suggested Solution:
class Name:
    def __init__(self, fname, lname):
        self.first = fname
        self.last = lname

    def get_name(self):
        return f'{self.first} {self.last}'

    def get_rname(self):
        return f'{self.last}, {self.first}'
 
Ex. 6.16 Continuing the Name class, set a class variable 'label' that refers to a string 'Name: ' -- this will serve as the label to be printed along with a person's name.
Suggested Solution:
class Name:

    label = 'Name:'

    def __init__(self, fname, lname):
        self.first = fname
        self.last = lname

    def get_name(self):
        return f'{Name.label}  {self.first} {self.last}'

    def get_rname(self):
        return f'{Name.label}  {self.last}, {self.first}'
 

MODULES

Ex. 6.17 Create a module that holds a function.

Create a new file, temputils.py, that has the below functions def ctof() and def ftoc(). Place this file in the same folder as this exercise file (inclass_exercises if this is a .py file; notebooks_inclass_challege if this is a Jupyter notebook).

Suggested Solution:
import temputils as tu

val = tu.ftoc(212)        # float, 100.0
print(val)

val2 = tu.ctof(0)         # float, 32.0
print(val2)
 
Ex. 6.18 Set PYTHONPATH environment variable to make utils.py importable from any directory.

Move (don't copy) temputils.py to your Desktop. This can be done through the PyCharm file view or your Finder or Windows Explorer windows. If you are using Jupyter Notebook, for this exercise you must first restart the kernel. Click the circular arrow in the menu bar, or Kernel -> Restart (no outputs will be cleared from your notebook). Run the below exercise and note the ModuleNotFoundError, which indicates that the module can no longer be located. This is because it is not in the same directory as this exercise file.

Suggested Solution:
import temputils as tu

val = tu.ftoc(212)        # float, 100.0
print(val)

val2 = tu.ctof(0)         # float, 32.0
print(val2)
 
Ex. 6.19 Create a module that holds a class.

Add the below class to temputils.py.

class TempConvert:

    def __init__(self, temp, scale='F'):     # self: 'TempConvert' instance; temp: 32; scale: 'F'
        self.itemp = temp                    # set .itemp attribute to 32
        self.scale = scale                   # set .scale attribute to 'F'

    def as_fahrenheit(self):                 # self: 'TempConvert' instance
        """ function to convert celsius to fahrenheit """
        if self.scale == 'F':                # bool, True
            return self.itemp                # return 32
        return (self.itemp * 9 / 5) + 32     #

    def as_celsius(self):                    #
        """ function to convert fahrenheit to celsius """
        if self.scale == 'C':                # bool, False
            return self.itemp                #
        return (self.itemp - 32) * 5 / 9     # return 0.0
Suggested Solution:
import temputils as tu

# construct a TempConvert object as 32 degrees Fahrenheit
ftemp = tu.TempConvert(32)                       # 'TempConvert' instance

# call .as_celsius() to see the value 0
print(ftemp.as_celsius())                        # float, 0.0


# construct a Tempconvert object as 100 degrees Celsius
ctemp = tu.TempConvert(100, scale='C')           # 'TempConvert' instance

# call .as_fahrenheit() to see the value 212
print(ctemp.as_fahrenheit())                     # float, 212.0
 
[pr]