Python
Welcome!
This page is the main page for Python
This page is the main page for Python
In Python, we can break our programs up into individual functions, which are individual routines that we can call in our code. Let’s review how to create functions in Python.
The table below lists the flowchart blocks used to represent functions, as well as the corresponding pseudocode:
Operation | Flowchart | Pseudocode |
---|---|---|
Declare Function |
|
|
Call Function |
|
In general, a function definition in Python needs a few elements. Let’s start at the simplest case:
def foo():
print("Foo")
return
Let’s break this example function definition down to see how it works:
def
at the beginning of this function definition. That keyword tells Python that we’d like to define a new function. We’ll need to include it at the beginning of each function definition.foo
. We can name a function using any valid identifier in Python. In general, function names in Python always start with a lowercase letter, and use underscores between the words in the function name if it contains multiple words.()
that list the parameters for this function. Since there is nothing included in this example, the function foo
does not require any parameters.:
indicating that the indented block of code below this definition is contained within the function. In this case, the function will simply print Foo
to the terminal.return
keyword. Since we aren’t returning a value, we aren’t required to include a return
keyword in the function. However, it is helpful to know that we may use that keyword to exit the function at any time.Once that function is created, we can call it using the following code:
foo()
In a more complex case, we can declare a function that accepts parameters and returns a value, as in this example:
def count_letters(input, letter):
output = 0
for i in range(0, len(input)):
if input[i] == letter:
output += 1
return output
In this example, the function accepts two parameters: input
, which could be a string, and letter
, which could be a single character. However, since Python does not enforce a type on these parameters, they could actually be any value. We could add additional code to this function that checks the type of each parameter and raises a TypeError
if they are not the expected type.
We can use the parameters just like any other variable in our code. To return a value, we use the return
keyword, followed by the value or variable containing the value we’d like to return.
To call a function that requires parameters, we can include values as arguments in the parentheses of the function call:
sum += count_letters("The quick brown fox jumped over the lazy dog", "e")
Python allows us to specify default values for parameters in a function definition. In that way, if those parameters are not provided, the default value will be used instead. So, it may appear that there are multiple functions with the same name that accept a different number of parameters. This is called function overloading.
For example, we could create a function named max()
that could take either two or three parameters:
def main():
max(2, 3)
max(3, 4, 5)
def max(x, y, z=None):
if z is not None:
if x >= y:
if x >= z:
print(x)
else:
print(z)
else:
if y >= z:
print(y)
else:
print(z)
else:
if x >= y:
print(x)
else:
print(y)
# main guard
if __name__ == "__main__":
main()
In this example, we are calling max()
with both 2 and 3 arguments from main()
. When we only provide 2 arguments, the third parameter will be given the default value None
, which is a special value in Python showing that the variable is empty. Then, we can use if z is not None
as part of an If-Then statement to see if we need to take that variable into account in our code.
This example also introduces a new keyword, is
. The is
keyword in Python is used to determine if two variables are exactly the same object, not just the same value. In this case, we want to check that z
is exactly the same object as None
, not just that it has the same value. In Python, it is common to use the is
keyword when checking to see if an optional parameter is given the value None
. We’ll see this keyword again in a later chapter as we start dealing with objects.
Python also allows us to specify function arguments using keywords that match the name of the parameter in the function. In that way, we can specify the arguments we need, and the function can use default values for any unspecified parameters. Here’s a quick example:
def main():
args(1) # 6
args(1, 5) # 9
args(1, c=5) # 8
args(b=7, a=2) # 12
args(c=5, a=2, b=3) # 10
def args(a, b=2, c=3):
print(str(a + b + c))
# main guard
if __name__ == "__main__":
main()
In this example, the args()
method has one required parameter, a
. It can either be provided as the first argument, known as a positional argument, or as a keyword argument like a=2
. The other parameters, b
and c
, can either be provided as positional arguments or keyword arguments, but they are not required since they have default values.
Also, we can see that when we use keyword arguments we do not have to provide the arguments in the order they are defined in the function’s definition. However, any arguments provided without keywords must be placed at the beginning of the function call, and will be matched positionally with the first parameters defined in the function.
Finally, Python allows us to define a single parameter that is a variable length parameter. In essence, it will allow us to accept anywhere from 0 to many arguments for that single parameter, which will then be stored in a list. Let’s look at an example:
def main():
max(2, 3)
max(3, 4, 5)
max(5, 6, 7, 8)
max(10, 11, 12, 13, 14, 15, 16)
def max(*values):
if len(values) > 0:
max = values[0]
for value in values:
if value > max:
max = value
print(max)
# main guard
if __name__ == "__main__":
main()
Here, we have defined a function named max()
that accepts a single variable length parameter. To show a parameter is variable length we use an asterisk *
before variable name. We must respect two rules when creating a variable length parameter:
So, when we run this program, we see that we can call the max()
function with any number of arguments, and it will be able to determine the maximum of those values. Inside of the function itself, values
can be treated just like a list.
Before we learn about classes and objects, let’s do a quick exercise to review how to create and use functions in our code.
Write a program that accepts input from a file provided as a command-line argument. If an incorrect number of arguments are provided, or if the program is unable to open the file, it should print “Invalid Arguments” and terminate.
The program’s input will consist of a list of 100 integers, one per line. If any line of the input cannot be converted to an integer, the program should print “Invalid Input” and terminate.
The program should determine whether the list of integers is considered a mathematical set. That is, each item in the list should be unique, with no duplicate numbers. If the input is not a set, it should print “Not a set” and terminate.
If the input is a set, then the program should print the sum of the values in the set and then terminate.
This program should consist of three functions:
main(args)
- The main function that controls the program. It should accept an array of strings representing the command-line arguments to the program.is_set(numbers)
- A function to determine if the given array is a set. The input should be a single array of integers, and the return value should be a Boolean value.sum_set(numbers)
- A function to find the sum of all the elements in the given array. The input should be a single array of integers, and the return value should be an integer.
Don’t forget to include a main guard at the end of the file that passes the contents of sys.argv
as an argument to the main()
function.
There may be easier ways of determining if an array contains duplicate items, but could we simply check that, for each item, there is only one of that item in the list?
This exercise uses a custom grading program to grade submissions, which will be used extensively throughout this course. The first step of the grading process will examine the structure of your code, making sure that it contains the correct classes and functions. The second step will directly examine each function in the program, making sure that they operate as expected. You are welcome to include any additional code to complete the project that is not specified above.
Each step of the grading process will create two files in your work directory showing more detailed output. To open the HTML file as a webpage, right-click on it and select Preview Static. The log file may contain helpful debugging messages if your program experiences an unhandled exception.
{Check It!|assessment}(test-4065492757)
{Check It!|assessment}(test-2578058679)
import sys
def main(argv):
if len(argv) != 2:
print("Invalid Arguments")
sys.exit()
try:
with open(argv[1]) as scanner1:
nums = []
for line in scanner1:
line = line.strip()
input = int(line)
nums.append(input)
if is_set(nums):
print(sum_set(nums))
else:
print("Not a set")
except FileNotFoundError:
print("Invalid Arguments")
return
except IOError:
print("Invalid Arguments")
return
except ValueError:
print("Invalid Input")
return
def is_set(nums):
for i in nums:
count = 0
for j in nums:
if i == j:
count += 1
if count > 1:
return False
return True
def sum_set(nums):
sum = 0
for i in nums:
sum += i
return sum
# main guard
if __name__ == "__main__":
main(sys.argv)
In programming, a class describes an individual entity or part of the program. In many cases, the class can be used to describe an actual thing, such as a person, a vehicle, or a game board, or a more abstract thing such as a set of rules for a game, or even an artificial intelligence engine for making business decisions.
In object-oriented programming, a class is the basic building block of a larger program. Typically each part of the program is contained within a class, representing either the main logic of the program or the individual entities or things that the program will use.
We can represent the contents of a class in a UML Class Diagram. Below is an example of a class called Person
:
Throughout the next few pages, we will realize the design of this class in code.
To create a class in Python, we can simply use the class
keyword at the beginning of our file:
class Person:
pass
As we’ve already learned, each class declaration in Python includes these parts:
class
- this keyword says that we are declaring a new class.Person
- this is an identifier that gives us the name of the class we are declaring.Following the declaration, we see a colon :
marking the start of a new block, inside of which will be all of the fields and methods stored in this class. We’ll need to indent all items inside of this class, just like we do with other blocks in Python.
In order for Python to allow this code to run, we cannot have an empty block inside of a class declaration. So, we can add the keyword pass
to the block inside of the class so that it is not empty.
By convention, we would typically store this class in a file called Person.py
.
Of course, our classes are not very useful at this point because they don’t include any attributes or methods. Including attributes in a class is one of the simplest uses of classes, so let’s start there.
To add an attribute to a class, we can simply declare a variable inside of our class declaration:
class Person:
last_name = "Person"
first_name = "Test"
age = 25
That’s really all there is to it! These are static attributes or class attributes that are shared among all instances of the class. On the next page, we’ll see how we can create instance attributes within the class’s constructor.
Finally, we can make these attributes private by adding two underscores to the variable’s name. We denote this on our UML diagram by placing a minus -
before the attribute or method’s name. Otherwise, a +
indicates that it should be public. In the diagram above, each attribute is private, so we’ll do that in our code:
class Person:
__last_name = "Person"
__first_name = "Test"
__age = 25
Unfortunately, Python does have a way to get around these restrictions as well. Instead of referencing __last_name
, we can instead reference _Person__last_name
to find that value, as in this example:
ellie = Person("Jonson", "Ellie", 29)
ellie._Person__last_name = "Jameson"
print(ellie.last_name) # Jameson
Behind the scenes, Python adds an underscore _
followed by the name of the class to the beginning of any class attribute or method that is prefixed with two underscores __
. So, knowing that, we can still access those attributes and methods if we want to. Thankfully, it’d be hard to do this accidentally, so it provides some small level of security for our data.
We can also add methods to our classes. These methods are used either to modify the attributes of the class or to perform actions based on the attributes stored in the class. Finally, we can even use those methods to perform actions on data provided as arguments. In essence, the sky is the limit with methods in classes, so we’ll be able to do just about anything we need to do in these methods. Let’s see how we can add methods to our classes.
A constructor is a special method that is called whenever a new instance of a class is created. It is used to set the initial values of attributes in the class. We can even accept parameters as part of a constructor, and then use those parameters to populate attributes in the class.
Let’s go back to the Person
class example we’ve been working on and add a simple constructor to that class:
class Person:
__last_name = "Person"
__first_name = "Test"
__age = 25
def __init__(self, last_name, first_name, age):
self.__last_name = last_name
self.__first_name = first_name
self.__age = age
Since the constructor is an instance method, we need to add a parameter to the function at the very beginning of our list of parameters, typically named self
. This parameter is automatically added by Python whenever we call an instance method, and it is a reference to the current instance on which the method is being called. We’ll learn more about this later.
Inside that constructor, notice that we use each parameter to set the corresponding attribute, using the self
keyword once again to refer to the current object.
Also, since we are now defining the attributes as instance attributes in the constructor, we can remove them from the class definition itself:
class Person:
def __init__(self, last_name, first_name, age):
self.__last_name = last_name
self.__first_name = first_name
self.__age = age
We’ve already discussed variable scope earlier in this course. Recall that two different functions may use the same local variable names without affecting each other because they are in different scopes.
The same applies to classes. A class may have an attribute named age
, but a method inside of the class may also use a local variable named age
. Therefore, we must be careful to make sure that we access the correct variable, using the self
reference if we intend to access the attribute’s value in the current instance. Here’s a short example:
class Test:
age = 15
def foo(self):
age = 12
print(age) # 12
print(self.age) # 15
def bar(self):
print(self.age) # 15
print(age) # NameError
As we can see, in the method foo()
we must be careful to use self.age
to refer to the attribute, since there is another variable named age
declared in that method. However, in the method bar()
we see that age
itself causes a NameError
since there is no other variable named age
defined in that scope. We have to use self.age
to reference the attribute.
So, we should always get in the habit of using self
to refer to any attributes, just to avoid any unintended problems later on.
In Python, we can use a special decorator @property
to define special methods, called getters and setters, that can be used to access and update the value of private attributes.
In Python, a getter method is a method that can be used to access the value of a private attribute. To mark a getter method, we use the @property
decorator, as in the following example:
class Person:
def __init__(self, last_name, first_name, age):
self.__last_name = last_name
self.__first_name = first_name
self.__age = age
@property
def last_name(self):
return self.__last_name
@property
def first_name(self):
return self.__first_name
@property
def age(self):
return self.__age
Similarly, we can create another method that can be used to update the value of the age
attribute:
class Person:
def __init__(self, last_name, first_name, age):
self.__last_name = last_name
self.__first_name = first_name
self.__age = age
@property
def last_name(self):
return self.__last_name
@property
def first_name(self):
return self.__first_name
@property
def age(self):
return self.__age
@age.setter
def age(self, value):
self.__age = value
However, this method is not required in the UML diagram, so we can omit it.
To add a method to our class, we can simply add a function declaration inside of our class.
class Person:
def __init__(self, last_name, first_name, age):
self.__last_name = last_name
self.__first_name = first_name
self.__age = age
@property
def last_name(self):
return self.__last_name
@property
def first_name(self):
return self.__first_name
@property
def age(self):
return self.__age
def happy_birthday(self):
self.__age = self.age + 1
Notice that once again we must remember to add the self
parameter as the first parameter. This method will update the private age
attribute by one year.
Now that we have fully constructed our class, we can use it elsewhere in our code through the process of instantiation. In Python, we can simply call the name of the class as a method to create a new instance, which calls the constructor, and then we can use dot-notation to access any attributes or methods inside of that object.
from Person import *
john = Person("Smith", "John", 25)
print(john.last_name)
john.happy_birthday()
Notice that we don’t have to provide a value for the self
parameter when we use any methods. This parameter is added automatically by Python based on the value of the object we are calling the methods from.
We can also build classes that inherit attributes and methods from another class. This allows us to build more complex structures in our code, better representing the relationships between real world objects.
As we learned earlier in this chapter, we can represent an inheritance relationship with an open arrow in our UML diagrams, as shown below:
In this diagram, the Student
class inherits from, or is a subclass of, the Person
class.
To show inheritance in Python, we place the parent class inside of parentheses directly after the name of the subclass when it is defined:
from Person import *
class Student(Person):
pass
From there, we can quickly implement the code for each property and getter method in the new class:
from Person import *
class Student(Person):
@property
def student_id(self):
return self.__student_id
@property
def grade_level(self):
return self.__grade_level
Since the subclass Student
also includes a definition for the method happy_birthday()
, we say that that method has been overridden in the subclass. We can do this by simply creating the new method in the Student
class, making sure it accepts the same number of parameters as the original:
from Person import *
class Student(Person):
@property
def student_id(self):
return self.__student_id
@property
def grade_level(self):
return self.__grade_level
def happy_birthday(self):
super().happy_birthday()
self.__grade_level += 1
Here, we are using the function super()
to refer to our parent class. In that way, we can still call the happy_birthday()
method as defined in Person
, but extend it by adding our own code as well.
In addition, we can use the super()
method to call our parent class’s constructor.
from Person import *
class Student(Person):
@property
def student_id(self):
return self.__student_id
@property
def grade_level(self):
return self.__grade_level
def __init__(self, last_name, first_name, age, student_id, grade_level):
super().__init__(last_name, first_name, age)
self.__student_id = student_id
self.__grade_level = grade_level
def happy_birthday(self):
super().happy_birthday()
self.__grade_level += 1
In addition to private and public attributes and methods, UML also includes the concept of protected methods. This modifier is used to indicate that the attribute or method should not be accessed outside of the class, but will allow any subclasses to access them. Python does not enforce this restriction; it is simply convention. In a UML diagram, the protected keyword is denoted by a hash symbol #
in front of the attribute or method. In Python, we then prefix those attributes or methods with a single underscore _
.
Inheritance allows us to make use of polymorphism in our code. Loosely polymorphism allows us to treat an instance of a class within the data type of any of its parent classes. By doing so, we can only access the methods and attributes defined by the data type, but any overriden methods will use the implementation from the child class.
Here’s a quick example:
steve_student = new Student("Jones", "Steve", "19", "123456", "13")
# We can now treat steve_student as a Person object
steve_person = steve_student
print(steve_person.first_name)
# We can call happy_birthday(), and it will use
# the code from the Student class, even if we
# think that steve_student is a Person object
steve_person.happy_birthday()
# We can still treat it as a Student object as well
print(steve_person.grade_level) # 14
Polymorphism is a very powerful tool in programming, and we’ll use it throughout this course as we develop complex data structures.
Many programming languages include a special keyword static
. In essence, a static
attribute or method is part of the class in which it is declared instead of part of objects instantiated from that class. If we think about it, the word static means “lacking in change”, and that’s sort of a good way to think about it.
In a UML diagram, static attributes and methods are denoted by underlining them.
In Python, any attributes declared outside of a method are class attributes, but they can be considered the same as static attributes until they are overwritten by an instance. Here’s an example:
class Stat:
x = 5 # class or static attribute
def __init__(self, an_y):
self.y = an_y # instance attribute
In this class, we’ve created a class attribute named x
, and a normal attribute named y
. Here’s a main()
method that will help us explore how the static keyword operates:
from Stat import *
class Main:
def main():
some_stat = Stat(7)
another_stat = Stat(8)
print(some_stat.x) # 5
print(some_stat.y) # 7
print(another_stat.x) # 5
print(another_stat.y) # 8
Stat.x = 25 # change class attribute for all instances
print(some_stat.x) # 25
print(some_stat.y) # 7
print(another_stat.x) # 25
print(another_stat.y) # 8
some_stat.x = 10 # overwrites class attribute in instance
print(some_stat.x) # 10 (now an instance attribute)
print(some_stat.y) # 7
print(another_stat.x) # 25 (still class attribute)
print(another_stat.y) # 8
if __name__ == "__main__":
Main.main()
First, we can see that the attribute x
is set to 5 as its default value, so both objects some_stat
and another_stat
contain that same value. Interestingly, since the attribute x
is static, we can access it directly from the class Stat
, without even having to instantiate an object. So, we can update the value in that way to 25, and it will take effect in any objects instantiated from Stat
.
Below that, we can update the value of x
attached to some_stat
to 10, and we’ll see that it now creates an instance attribute for that object that contains 10, overwriting the previous class attribute. The value attached to another_stat
is unchanged.
Python also allows us to create static methods that work in a similar way:
class Stat:
x = 5 # class or static attribute
def __init__(self, an_y):
self.y = an_y # instance attribute
@staticmethod
def sum(a):
return Stat.x + a
We have now added a static method sum()
to our Stat
class. To create a static method, we place the @staticmethod
decorator above the method declaration. We haven’t learned about decorators yet, but they allow us to tell Python some important information about the code below the decorator.
In addition, it is important to remember that a static method cannot access any non-static attributes or methods, since it doesn’t have access to an instantiated object in the self
parameter.
As a tradeoff, we can call a static method without instantiating the class either, as in this example:
from Stat import *
class Main:
@staticmethod
def main():
# other code omitted
Stat.x = 25
moreStat = Stat(7)
print(moreStat.sum(5)) # 30
print(Stat.sum(5)) # 30
if __name__ == "__main__":
Main.main()
This becomes extremely useful in our main()
method. Since we aren’t instantiating our Main
class, we can use the decorator @staticmethod
above the method to clearly mark that it should be considered a static method.
Another major feature of class inheritance is the ability to define a method in a parent class, but not provide any code that implements that function. In effect, we are saying that all objects of that type must include that method, but it is up to the child classes to provide the code. These methods are called abstract methods, and the classes that contain them are abstract classes. Let’s look at how they work!
In the UML diagram above, we see that the describe()
method in the Vehicle
class is printed in italics. That means that the method should be abstract, without any code provided. To do this in Python, we simply inherit from a special class called ABC
, short for “Abstract Base Class,” and then use the @abstractmethod
decorator:
from abc import ABC, abstractmethod
class Vehicle(ABC):
def __init__(self, name):
self.__name = name
self._speed = 1.0
@property
def name(self):
return self.__name
def move(self, distance):
print("Moving");
return distance / self._speed;
@abstractmethod
def describe(self):
pass
Notice that we must first import both the ABC
class and the @abstractmethod
decorator from a library helpfully called ABC
. Then, we can use ABC
as the parent class of our class, and update each method using the @abstractmethod
decorator before the method, similar to how we’ve already used @staticmethod
in an earlier module.
In addition, since we have declared the method describe()
to be abstract, we can either add some code to that method that can be called using super().describe()
from a child class, or we can simply choose to use the pass
keyword to avoid including any code in the method.
Now, any class that inherits from the Vehicle
class must provide an implementation for the describe()
method. If it does not, that class must also be declared to be abstract. So, for example, in the UML diagram above, we see that the MotorVehicle
class does not include an implementation for describe()
, so we’ll also have to make it abstract.
Of course, that means that we’ll have to inherit from both Vehicle
and ABC
. In Python, we can do that by simply including both classes in parentheses after the subclass name, separated by a comma.
Let’s build a quick program following the MVC architecture style to review working with classes, object, inheritance, and polymorphism.
Write a program to store a list of students and teachers at a school. The program should have methods to add a student or a teacher, as well as a method to print the entire list.
The program should conform to the following UML diagram:
Right-click and select “Open image in new tab” to view larger
The purpose of each method will be further described below.
Person
Class__init__()
- constructor that initializes all attributes based on parameterslast_name()
- getter for last_name
attribute—it should be implemented as a propertyget_first_name()
- getter for first_name
attribute—it should be implemented as a propertyget_age()
- getter for age
attribute—it should be implemented as a propertyhappy_birthday()
- method to increase person’s age
attribute by $1$__str__()
- method that overrides the built-in Object
class __str__()
method. It should return a string in the form "first_name last_name: age"
Student
Class__init__()
- constructor that initializes all attributes (including in super class) based on parametersstudent_id()
- getter for student_id
attribute—it should be implemented as a propertygrade_level()
- getter for grade_level
attribute—it should be implemented as a propertyhappy_birthday()
- method to increase student’s age
and grade_level
attribute by $1$__str__()
- method that overrides the built-in Object
class __str__()
method. It should return a string in the form "first_name last_name: age (student_id - grade_level)"
Teacher
Class__init__()
- constructor that initializes all attributes (including in super class) based on parametersclassroom()
- getter for classroom
attribute—it should be implemented as a propertysalary()
- getter for salary
attribute—it should be implemented as a propertyhappy_birthday()
- method to increase teacher’s age
by $1$ and salary
attribute by $1000$__str__()
- method that overrides the built-in Object
class __str__()
method. It should return a string in the form "first_name last_name: age (classroom - $salary)"
View
Classshow_menu()
- a method to show a menu of options to the user. The user should be prompted to input exactly one of the options listed below, which is returned as a String. The wording of the menu is up to you. The method should return whatever was input by the user, without any error checking (that is done in the Controller)
add_student()
- a method to add a new student to the system. The user should input a list of parameters for each attribute as they are listed in the constructor for Student
, separated by spaces. The wording of the prompt is up to you. The method should return whatever was input by the user, without any error checking (that is done in the Controller)
add_teacher()
- a method to add a new teacher to the system. The user should input a list of parameters for each attribute as they are listed in the constructor for Teacher
, separated by spaces. The wording of the prompt is up to you. The method should return whatever was input by the user, without any error checking (that is done in the Controller)list_people()
- a method to list all Person
objects in the persons
list given as a parameter. Each one should be prefixed by an index starting at $0$, incrementing by one for each Person
in the list.
show_error()
- a method to display an error to the user. The parameter error
should be printed to the screen, prefixed by “Error: "Hint: use sys.stdin.readline()
to read an entire line of input anywhere in your code. Don’t forget to import sys
as well!
Controller
Classmain()
- the main method for this program. It should simply instantiate a new instance of the Controller class, and then call the run()
method of that object.__init__()
- the constructor for the Controller object. It initialize the persons
attribute to an empty list, as well as a View
object stored in the view
attribute.run()
- this method consists of a loop that will execute the program until it is terminated. It will call the showMenu()
method of the view to show a menu to the user (see above). Finally, it will parse the string returned by the call to showMenu()
and call additional appropriate methods in the Controller
or View
class to complete the operation. If the user inputs “exit” then it should terminate. Otherwise, the program will repeatedly display the menu to the user until “exit” is chosen. If at any time the user provides input that cannot be properly parsed, the controller should call the showError()
method in the View
class and restart the process (loop back to the beginning) by showing the menu again.add_student()
- this method will receive the string input by the user from the add_student()
method in View
, parse the input, and call the appropriate methods to create a new Student
object and add it to the first empty slot in the persons
list.add_teacher()
- this method will receive the string input by the user from the add_teacher()
method in View
, parse the input, and call the appropriate methods to create a new Teacher
object and add it to the first empty slot in the persons
list.persons()
- these methods are a getter and setter for the persons
attribute. They should be implemented as a property. It is for testing purposes only.A sample execution of the program is shown below.
{Check It!|assessment}(test-3416583454)
{Check It!|assessment}(test-1104470273)
{Check It!|assessment}(test-845982740)
{Check It!|assessment}(test-3746962485)
{Check It!|assessment}(test-3109390640)
{Check It!|assessment}(test-1533527332)
{Check It!|assessment}(test-1355293558)
{Check It!|assessment}(test-3808852464)
{Check It!|assessment}(test-3597936585)
{Check It!|assessment}(test-1788523734)
class Person:
def __init__(self, last_name, first_name, age):
self.__last_name = last_name
self.__first_name = first_name
self.__age = age
@property
def last_name(self):
return self.__last_name
@property
def first_name(self):
return self.__first_name
@property
def age(self):
return self.__age
def happy_birthday(self):
self.__age = self.__age + 1
def __str__(self):
return "{} {}: {}".format(self.first_name, self.last_name, self.age)
from Person import Person
class Student(Person):
def __init__(self, last_name, first_name, age, student_id, grade_level):
super().__init__(last_name, first_name, age)
self.__student_id = student_id
self.__grade_level = grade_level
@property
def student_id(self):
return self.__student_id
@property
def grade_level(self):
return self.__grade_level
def happy_birthday(self):
super().happy_birthday()
self.__grade_level = self.__grade_level + 1
def __str__(self):
return "{} ({} - {})".format(super().__str__(), self.student_id, self.grade_level)
from Person import Person
class Teacher(Person):
def __init__(self, last_name, first_name, age, classroom, salary):
super().__init__(last_name, first_name, age)
self.__classroom = classroom
self.__salary = salary
@property
def classroom(self):
return self.__classroom
@property
def salary(self):
return self.__salary
def __str__(self):
return "{} ({} - ${})".format(super().__str__(), self.classroom, self.salary)
import sys
class View:
def show_menu(self):
print("Please enter one of the following options:")
print(" add student")
print(" add teacher")
print(" list people")
print(" exit")
try:
inp = sys.stdin.readline()
return inp.strip()
except Exception:
return ""
def add_student(self):
print("Please enter the following items for the new student, all on the same line")
print("LastName FirstName Age StudentID GradeLevel")
try:
inp = sys.stdin.readline()
return inp.strip()
except Exception:
return ""
def add_teacher(self):
print("Please enter the following items for the new teacher, all on the same line")
print("LastName FirstName Age Classroom Salary")
try:
inp = sys.stdin.readline()
return inp.strip()
except Exception:
return ""
def list_people(self, persons):
i = 0
for p in persons:
print("{}) {}".format(i, p))
i += 1
def show_error(self, error):
print("Error: {}".format(error))
from Person import Person
from Student import Student
from Teacher import Teacher
from View import View
import sys
class Controller:
@staticmethod
def main(args):
Controller().run()
def __init__(self):
self.__persons = []
self.__view = View()
@property
def persons(self):
return self.__persons
@persons.setter
def persons(self, value):
self.__persons = value
def run(self):
while True:
inp = self.__view.show_menu()
if inp == "add student":
self.add_student(self.__view.add_student())
elif inp == "add teacher":
self.add_teacher(self.__view.add_teacher())
elif inp == "list people":
self.__view.list_people(self.__persons)
elif inp == "exit":
break
else:
self.__view.show_error("Invalid Input!")
def add_student(self, inp):
splits = inp.split(" ")
try:
p = Student(splits[0], splits[1], int(splits[2]), int(splits[3]), int(splits[4]))
self.__persons.append(p)
except Exception:
self.__view.show_error("Unable to parse input!")
def add_teacher(self, inp):
splits = inp.split(" ")
try:
p = Teacher(splits[0], splits[1], int(splits[2]), splits[3], int(splits[4]))
self.__persons.append(p)
except Exception:
self.__view.show_error("Unable to parse input!")
# main guard
if __name__ == "__main__":
Controller.main(sys.argv)