6 Classes/Packages for Python
6.1 Functions
Functions are declared with the def
keyword and returned from the return
keyword.
Example 6.1
Each function can have positional arguments and keyword arguments.
- Keyword arguments are most commonly used to specify default values.
- If no keywords are given, all arguments will be recognized by the positions.
- If both positional arguments and keyword arguments are given, positional arguments have to be in front.
- The order of keyword arguments are not important.
Example 6.2 (Mutable objects as default value) It is highly recommended NOT to set any mutatable objects as the default value of an input of a function. The reason is that this default object is initialized when the function is defined, not when the function is called. Then all function calls will share the same default object.
A typical example is an empty list. If you use an empty list as the defaul value, that list will be passed to the next function call, which is no longer empty. Please see the following example.
Every time the function is called with no arguments, the default value is used, which is the same list initialized at the beginning. The list at the begining is an empty list. But after we put things inside, it is no longer empyt.
If you want to set a mutable object as a default, the way is as follows:
6.2 Classes
A class is an abstract structure that can be used to hold both variables and functions. Variables in a class are called attributes, and functions in a class are called methods.
A class is defined in the following way.
In this example, we define a class Circle
, which represents a circle. There is one attribute radius
, and one method area
. When define a cirlce, we need to specify its radius, and we could use the method area
to compute the area of the circle.
Here we define two circles. The first circle cir1
is of radius 1
. This 1
comes from the default value. Check the definition of Circle.__init__()
.
The second circle cir2
is of radius 5
, and this number is specified when we initialize the Circle
instance.
Then we compute the areas of these two circles by calling the area()
method. You can also use cir1.radius
to get access the radius of the circle. The syntax difference between attributes and methods is the ()
at the end.
6.2.1 self
You may notice the self
variable in the definition of the classes. The self
is used to refered to the class its. When you want to get access to the class attributes or the class methods, you may use self
.
Take the code as an example.
In the __init__
function, there are two radius
.
radius
is the local variable that is used by the function. It is also the input argument.self.radius
is the class attribute, that is shared by all class methods. For example, we may add another class method to the classCircle
.
Both area()
and perimeter()
use the same self.radius
.
6.2.2 A design example
Assume that we live in a world without Pandas, and we would like to design a table object. Then what do we need?
A table should have multiple rows and multiple columns. We should be able to get access entries by columns and row index. We should also be able to display the table by using the print
funciton.
Therefore we may write the following class.
class myTableClass():
def __init__(self, listoflist=None):
if listoflist is None:
listoflist = [[]]
self.nrows = len(listoflist)
self.ncols = len(listoflist[0])
self.data = listoflist
self.shape = (self.nrows, self.ncols)
def get(self, i, j):
return self.data[i][j]
def __str__(self):
tmp = [' '.join([str(x) for x in row]) for row in self.data]
return '\n'.join(tmp)
This is a very brief table object. We may add more things to it. For example, we could talk about column names.
class myTableClass():
def __init__(self, listoflist=None, columns=None):
if listoflist is None:
listoflist = [[]]
if columns is None:
columns = list()
self.nrows = len(listoflist)
self.ncols = len(listoflist[0])
self.data = listoflist
self.shape = (self.nrows, self.ncols)
self.columns = columns
def get(self, i, j):
return self.data[i][j]
def rename(self, columns=None):
if columns is not None:
self.columns = columns
def __str__(self):
tmp = [' '.join([str(x) for x in row]) for row in self.data]
if len(self.columns) != 0:
tmp.insert(0, self.columns)
return '\n'.join(tmp)
6.3 Inheritance
One of the most important feature of classes is inheritance. Attributes and methods can be passed from parents to children, and child classes can override those attributes and methods if needed.
For example, we would like to first write a people
class.
This people
class defines a people who can eat. Then using this people
class, we could build a children class: student
.
Now you can see that this stu1
is a student
, but it has all attributes and methods as a people
. However at current stage student
and people
are exactly the same since we don’t have any new codes for student
. Let us improve it a little bit.
class student(people):
def __init__(self, name='default', age=20, grade=1):
super().__init__(name, age)
self.grade = grade
def eat(self):
print('eat in the cafe.')
stu1 = student('name1', 10)
stu1.eat()
eat in the cafe.
Now student
class override the eat()
method from people
. If someone is a student
, he or she will eat in the cafe instead of just eat something.
In addition, you may also notice that the __init__()
constructor function is also overriden. The first part is super().__init__(name, age)
which is just call the people
’s constructor function. The second part is new in student
, that we add a new attribute grade
to it. Now stu1
have attributes from people
and the new attribute defined in student
.
6.4 packages / modules
Main reference is RealPython and [1].
6.4.1 import
In most cases we won’t only write one single Python file. If we want to use codes from other files, we need to import
.
- If both files are in the same folder, e.g.
file1.py
andfile2.py
, you may just putimport file2
infile1.py
, and usefile2.myfunction()
to call functions or variables defined infile2.py
. - If both files are in the same folder, and you just want to use one function from
file1.py
infile2.py
, you mayfrom file1 import myfunction()
, and then directly writemyfunction()
infile2.py
.
Example 6.3 This is from file1.py
.
s = "This is from file1.py."
a = [100, 200, 300]
print(s)
def foo(arg):
print(f'arg = {arg}')
class Foo:
pass
This is from file1.py.
In file2.py
, we could get access to these variables and functions and classes as follows.
Please see the following Example to get a feel about how namespace works.
Example 6.4
We may use dir()
to look at all objects in the current namespace.
6.4.2 __name__
__name__
is a variable to tell you want is the current active namespace. See the following example.
Example 6.5
The result file1
means that the codes in file1.py
are now treated as a package and are imported into other files.
The result __main__
means that the codes we are writing now are treated as in the “active” enviroment.
You may see the following codes in a lot of places.
It means that the following codes will only be run in the “active” mode. If you import the codes as a package, these part of codes won’t be run.
6.4.3 Packages
Pacages is a way to group and organize mutliple modules. It allow for a hierachical structuring of the module namespace using dot notation.
Creating a package is straightforward, since it makes use of the operating system’s inherent hierarchical file structure.
Python defines two types of packages, regular packages and namespace packages. The above package is the regular one. Namespace packages allow codes are spread among different folders. We won’t talk about it in this course.
To create a regular package, what you need to do is to organize the files in suitable folders, and then add an __init__.py
in each folder. The file can be empty, or you could add any initialization codes for the package which is represented by the folder.
Let us put the previous file1.py
and file2.py
into subfolder assests/codes/
. To make it into a package assests
and a subpackage codes
, we need to put __init__.py
in each folder.
6.5 Exercieses
Exercise 6.1 (Heron’s formula) Consider a triangle whose sides are \(a\), \(b\) and \(c\). Heron’s formula states that the area of this triangle is \[\sqrt{s(s−a)(s−b)(s−c)}\quad\text{ where } s=\frac12(a+b+c).\]
Please write a function that given three points computes the area of the triangle with vertices being the given points. The input is required to be a list
of three tuple
s, where each tuple
contains two numbers representing the 2d-coordinate of a point.
Exercise 6.2 (array) Write a function to reverse an 1D NumPy array (first element becomes last).
Exercise 6.3 (Compare two numpy
arraies) Consider two numpy
arraies x
and y
. Compare them entry by entry. We would like to know how many are the same.
Write a function that the inputs are x
and y
, and the output is the number of the same numbers.
6.6 Projects
Problems are based on [2].
Exercise 6.4 (Comma Code) Say you have a list value like this: spam = ['apples', 'bananas', 'tofu', 'cats']
.
Write a function that takes a list value as an argument and returns a string with all the items separated by a comma and a space, with and
inserted before the last item. For example, passing the previous spam
list to the function would return ‘apples, bananas, tofu, and cats’. But your function should be able to work with any list value passed to it. Be sure to test the case where an empty list []
is passed to your function.
Exercise 6.5 (Fantasy Game Inventory) You are creating a fantasy video game. The data structure to model the player’s inventory will be a dictionary where the keys are string values describing the item in the inventory and the value is an integer value detailing how many of that item the player has. For example, the dictionary value {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12}
means the player has 1 rope, 6 torches, 42 gold coins, and so on.
Write a function named displayInventory()
that would take any possible inventory
and display it like the following:
Note that this is the function version of Exercise 2.17.
Exercise 6.6 Create a Car class with two instance attributes:
.color
, which stores the name of the car’s color as a string..mileage
, which stores the number of miles on the car as an integer.
Then instantiate two Car objects — a blue car with 20,000 miles and a red car with 30,000 miles — and print out their colors and mileage. Your expected output are below:
Exercise 6.7 Create a GoldenRetriever
class that inherits from the Dog
class. Give the sound
argument of GoldenRetriever.speak()
a default value of Bark
. Use the following code for your parent Dog
class: