0% found this document useful (0 votes)
7 views

Topic_5_Classes

The document provides homework instructions for a Python programming course focused on classes, including examples of creating and experimenting with classes such as Person, Student, and Rectangle. It emphasizes understanding class attributes, methods, and inheritance, along with practical exercises to solidify these concepts. Additionally, it includes code snippets and solutions for various class-related tasks and challenges.

Uploaded by

James SI
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views

Topic_5_Classes

The document provides homework instructions for a Python programming course focused on classes, including examples of creating and experimenting with classes such as Person, Student, and Rectangle. It emphasizes understanding class attributes, methods, and inheritance, along with practical exercises to solidify these concepts. Additionally, it includes code snippets and solutions for various class-related tasks and challenges.

Uploaded by

James SI
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

Topic 5: Classes

Homework
Python Programming for Economics
Dr. Marcus Roel
Spring 2024, UNNC
Notes on Solution
Slides are becoming rather tricky to format in case of classes
See the notebook or html file
Standard Homework
go over our examples from the lecture and (1) understand them, and (2) vary them
slightly to see if you really understand them.

Classes
Experiment with classes by creating any object you find interesting. Afterwards,
create a parent class.
potential examples: a person, a student; a motor-vehicle, a car, a bus; a video
game weapon, a video game sword (or something less violent); an economic
agent, a consumer (solving your typical micro 1/2 problems); animals; flowers;
etc : )
think about potential things (verbs) these objects should be able to do.
Solution
My solution is a very simple person/student class, with minimal details
These are very much data-containers.
They have very little methods and thus are very uninteresting objects (they can't do
much without methods)
Do experiment with classes by creating any object you find interesting
If you haven't, try to search your particular example online.
Unless you were very creative, you can surely find it.
In [ ]: # My preferred module for working with time/dates. Check out doc!
from datetime import datetime

class Person:
""" A simple description of a person

Attributes:
first_name: first name
second_name: second name/family name
year_of_birth: year born
occupation: occupation
"""

def __init__(self, first_name, second_name, year_of_birth, occupation):


self.first_name = first_name
self.second_name = second_name

# In case people enter YOB as string:


# What would you need to do to have the full birthday?
self.year_of_birth = int(year_of_birth)

self.occupation = occupation

def age(self):
# Not quite correct obviously. Would need full date of birth
return datetime.now().year - self.year_of_birth
# Feel free to implement this correctly.
# Hint: use a year, month, day day of birth attribute
# Or: Read datetime documentation to use datetime

def __str__(self):
return f"{self.first_name} {self.second_name}. Age: {self.age()}. Wo

In [ ]: class Student(Person):
""" A simple description of a student

Attributes:
first_name: first name
second_name: second name/family name
year_of_birth: year born
occupation: student
university: where student studies
major: student major
"""

def __init__(self, first_name, second_name, year_of_birth, university, m


# Notice how much typing we save by calling super()!
super().__init__(first_name, second_name, year_of_birth, "student")
self.university = university
self.major = major

def __str__(self):
return f"{self.first_name} {self.second_name}. Age: {self.age()}. Wo
In [ ]: # Testing Person Class
me = Person("Marcus", "Roel", 1986, "Feels like a CS Professor these days")
print(me)

In [ ]: # Testing Student Class


my_dog = Student("Rhuby", "von Rhubarb", 2023, "Homeschooled", "Puppy 101")
print(my_dog)

Playing with Rectangles


Create a class rectangle with a method that allows the rectangle to be cut in half
(along the largest side)
next level version: have the class return the cut-away part of the rectangle as an
object iself
next next level: write a function (using the above class) to calculate the sum of
the area of the following objects: (the area of) an initial square, (the area of) the
initial square cut in half, (the area) of the cut-in-half square cut in half, ... up to
n-cuts!
Solution
I've chosen to create the Rectangle class anew. Of course, you could (should?)
inherit from the original Rectangle Class.
In [ ]: class RectangleCutable:
""" A Rectangle that can be cut in half along the longest edge.

Attributes:
width: the width of the rectangle
height: the height of the rectangle
"""

def __init__(self, width, height):


self.width = width
self.height = height

def get_area(self):
return self.width * self.height

def cut_in_half(self):
""" Cut the current rectangle in half
return: cut-away part of rectangle as a new Object"""

if self.width >= self.height:


self.width = self.width / 2
else:
self.height = self.height / 2

return RectangleCutable(self.width, self.height)


def __str__(self):
return f"Rectangle of width = {self.width} and height = {self.height

In [9]: # Testing RectangleCutable


rectangle_0 = RectangleCutable(6, 8)
print(f"Original rectangle: {rectangle_0}")
rectangle_1 = rectangle_0.cut_in_half()
print(f"New Rectangle, after one round of cutting:\n {rectangle_1}")

Original rectangle: Rectangle of width = 6 and height = 8.


New Rectangle, after one round of cutting:
Rectangle of width = 6 and height = 4.0.

Solution to:
Calculate the area of the series of n rectangles where the next rectangle is half of
the previous.
In [ ]: def find_area_series_cut_rectangles(initial_height, initial_width, num_cuts)
rectangle = RectangleCutable(initial_height, initial_width)
area = rectangle.get_area()
for i in range(num_cuts):
rectangle = rectangle.cut_in_half()
area += rectangle.get_area()
return area

In [10]: # Test find_area_series_cut_rectangles


height = 1
width = 1
print(f"Area of a series of rectangles, with the first being a 1x1 square, t
print(find_area_series_cut_rectangles(height, width, 1))

print(f"For: 1x1, 0.5x1, and 0.5x0.5, we have:")


print(find_area_series_cut_rectangles(height, width, 3))

Area of a series of rectangles, with the first being a 1x1 square, the scond
a 0.5x1 rectangle is:
1.5
For: 1x1, 0.5x1, and 0.5x0.5, we have:
1.875

You could manually check whether these are right or use the classic formula that
calculates 1 + 1/2 + 1/4+. . . +(1/2)
n

Class Attributes
Create a class with a class attribute. Create an instance (a) of a class and check the
value of said attribute. Create another instance (b) of the class, and change the
class attribute for this instance. Check the value of the class attribute again for
instance a.
Class Attributes: Solution
In [28]: class class_attributes_test:
x = 10

a = class_attributes_test()
b = class_attributes_test()
print(f"a.x = {a.x}")
b.x = 20

print("\nAfter setting b.x = 20, we have")


print(f"a.x = {a.x}, b.x = {b.x}")

print(f"Instance attributes of a: {a.__dict__}; of b: {b.__dict__}")

a.x = 10

After setting b.x = 20, we have


a.x = 10, b.x = 20
Instance attributes of a: {}; of b: {'x': 20}

Essentially, changing the 'class attribute' has created an instance attribute at b.


This is true for immutable (numbers, strings, etc)
Feel free to revisit what happens with mutable types after lecture 7
Staticmethod
Write a class with a staticmethod. Next write the same staticmethod also a normal
method - passing in the self tag and never use it inside.
Try calling both methods after instantializing a version of the class. Try also
calling both methods without creating an instance.
In [ ]: # Staticmethod homework

class StaticMethodExample:
@staticmethod
def add_two_numbers_static(a, b):
return a + b

def add_two_numbers(self, a, b):


return a + b

# What happens if we also add a method without self but don't call it st
def add_two_numbers_no_self(a, b):
return a + b

In [ ]: class_static = StaticMethodExample()
a = 5
b = 6
In [ ]: # Calling a method via the instance of the class works for both
print(f"Add via staticmethod with instance. a = {a}, b = {b}")
print(f"a + b = {class_static.add_two_numbers_static(a, b)}")

print(f"Add via normal method with instance. a = {a}, b = {b}")


print(f"a + b = {class_static.add_two_numbers(a, b)}")

# Calling a method via the class only works for the static method
print(f"Add via staticmethod with class. a = {a}, b = {b}")
print(f"a + b = {StaticMethodExample.add_two_numbers_static(a, b)}")

# raises Error: calling non-static method using class object.


# print(f"Add via staticmethod with class. a = {a}, b = {b}, a + b = {Static

# raises Error as well: non-static but without self


# print(f"Add via no-self method with instance. a = {a}, b = {b}, a + b = {c

# Whereas non-static method without a self works when we call it via the cla
print(f"Add via no-self method with class. a = {a}, b = {b}")
print(f"a + b = {StaticMethodExample.add_two_numbers_no_self(a, b)}")—

TAKEAWAY: use @staticmethod to get it right in all cases : ).


Inheritance
1. In the class square of our lecture notes, we called
super().__init__(length, length) in the __init__ method. How could
you write the class without relying on the parent's initialization? (If you get it right on
the first try, also try to write a version that doesn't work).
Shortest solution (all pointless stuff cut)
In [21]: class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height

def get_area(self):
return self.width * self.height

class Square(Rectangle):
def __init__(self, length):
# super().__init__(length, length) # Calls parent __init__
self.width = length
self.height = length

def get_diagonal(self):
return (2*self.width**2)**0.5 # recall: a**2 + b**2 = c**2
In [22]: my_square = Square(4)
print(f"Area = {my_square.get_area()}")

Area = 16

The following implementation does not work


In [25]: class SquareBroken(Rectangle):
def __init__(self, length):
self.length = length

def get_diagonal(self):
# Notice the change. This will work!
return (2*self.length**2)**0.5

In [26]: my_broken_square = SquareBroken(4)


print(f"Diagonal = {my_square.get_diagonal()}")

Diagonal = 5.656854249492381

In [27]: print(f"Area = {my_broken_square.get_area()}")

---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[27], line 1
----> 1 print(f"Area = {my_broken_square.get_area()}")

Cell In[21], line 7, in Rectangle.get_area(self)


6 def get_area(self):
----> 7 return self.width * self.height

AttributeError: 'SquareBroken' object has no attribute 'width'

In [ ]:

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy