Lecture 5

More Functions and Objects

CS1302 Introduction to Computer Programming

Global variables

How can different functions work on the same variable?

  • Why use global in the first 2 functions but not the last?
    • A local variable must be defined within a function, and so... 
    • An assignment defines a local variable unless global is used, and so...

What happens with global declarations removed?

count = count + 1
count += 1

Hint:

Issues with global variables

Functions with global variables are dependent.

Functional independence

With global variables,

  • codes are less predictable, more difficult to reuse/extend, and
  • tests cannot be isolated, making debugging difficult.

To ensure functional independence, define pure functions which

  • return the same value for the same arguments, and
  • have no observable side effects including input/output.

Default parameters

Can we call a function without specifying all its arguments?

  • step is a default parameter.
  • start and stop are non-default parameters.

Can we set start=1 as a default parameter instead of stop=5?

  • Default parameters must follow non-default parameters. Why?
  • How can range sets the default for start but not stop? Hint.
  • Default parameters are filled from back to front.
  • We can specify argument when calling a function.

Can we set multiple default parameter?

Recursion

Can we define a function using the function itself?

What does the function compute?

  • \(f(n)=n\cdot f(n-1)=n\cdot (n-1) \cdots 1\)=   
  • More efficient? No because more resources need to keep track of the execution.
  • Easier to write? Yes because wishful thinking leads to code reuse.

Why recursion instead of iteration?

Can we define the same function without recursion? 

Reusing Functions

Say you would like to do check if a number is divisible by the numbers 2, 3, 5 and 7. Normally, you would write something like 

Remember that the modulo operator \(\%\) takes the reminder of the division of number \(n\) by the numbers 2, 3, 5, and 7.

How do we avoid copy-pasting the same logic (code duplication) to decrease redundancy?

  • Copy-pasting -> BAD PRACTICE
  • Not easy to "transport" a function into other programs
  • If a bug (unexpected behavior) is discovered in our isDivisibleBy function, we would have to repeat the copy-paste function multiple times.

Further simplification:

Functions as Data

Functions in Python are objects, just like the previous types you have studied (strings, integers). Thus, we can treat it like data.

Lambda Expressions

Lambda expressions are simple, anonymous functions to simplify function calls.

Local Function Definitions

Sometimes also referred to as nested functions

Break down functions into smaller components in a function

Partial Application

Application of passing functions as arguments to other functions

Break down functions into smaller components in a function

 

Let us get back to our previous example:

Objects

Python supports object oriented programming (OOP). As previously mentioned, each Python object has a type, or class.

 

An object is an instance of a class.

 

With OOP, we combine data and methods.

 

Before we go deeper into objects, let us look back at some of  the general ideas and ways to express and use objects.

Object Methods

In general, a method call will have the following syntax:

object

method name

parameter list

(

)

.

object

period (dot)

 

method name

parameter list

: expression that represents object

: associates object expression with method

  to be called

: name of method to execute

: comma-separated list of method's parameters

  always required even if there are no parameters

  similar to parameter list for a function call

Object Methods

Example: String Objects

Python strings are objects. They provide the perfect example of combining data and methods.

course_name = "cs1302"
print("Welcome to the {} course, hope you like it so far!"
    .format(course_name.upper()))
# Output: Welcome to the CS1302 course, hope you like it so far!

String contains the data "cs1302" and has the upper() method

Optional:

For more string methods, check out https://docs.python.org/3/library/string.html

File Objects

Sometimes, we might want to read data from a file or save the results of our program execution into a file. We can achieve this with Python's standard library.

# Reading a file named file.txt
f = open("file.txt") # Equivalent of open("file.txt", "r")
for line in f:
  print(line.strip())
f.close()

There are several different modes for opening a file, specified in the second argument of the open function

  • 'r', opens file for reading, default (no argument)
  • 'w', opens file for writing, create new file if nonexistent
  • 'a', opens file and adds text to the end of file (append)

Context Manager

Works with classes which provide initialization (open) and finalization (close) protocols. These protocols are defined for objects using the built-in __enter__and __exit__methods.

object-creation

object

:

 with

as

block

Context Manager

with

object-creation

 

as

 

object

block

: reserved word, begins the statement

: attempts to create an object, only executes

  function in block if creation successful

: reserved word, binds object created by object-

  creation expression to a variable

: binding for object created by object-creation

: contains code which can use the previously

  bounded object 

object-creation

object

:

 with

as

block

Example: File Objects

Formal name of file objects is \(TextIOWrapper\), found in the built-in \(io\) module. No import statement is required since the functions from \(io\) module are exposed by default.

open

filename

open mode

,

)

(

Returns a file (\(TextWrapperIO\)) object. Supported open modes are:

- 'r' for reading, default when no open

  mode is specified

- 'w' for writing

- 'a' for appending data to file

*appending = adding data to end of file

Example: File Objects

When a file has been opened with 'w' or 'a', we can write to it with a file object's \(write\) method.

 

 Usually, we have to call the \(close\) method to close the file.

Example: File Objects

with open("my_diary.txt", "w") as f:
  # Note that \n represents a newline operator
  f.write("I am currently attending\n")
  f.write("The CS1302 course\n")

  print(f.read())

  for line in f:
    print(line.strip())
    

With context managers, we can automatically close the file after all function inside the block executes:

Fraction Objects

Provided by the \(Fraction\) class from the \(fractions\) module

Modelling rational numbers (numbers as ratio of two integers) with a numerator and denominator.

Reserved Object Methods in Fractions

Python reserves some special names for some methods.

 

For instance, the \(Fraction\) class provides an __add__ method. This function is invoked when we add two fractions. The + operator is an example of syntactic sugar for the __add__ method.

Reserved Object Methods in Fractions

Other special methods which Python provides:

- __mul__, multiplication: f.__mul__(g) is equivalent to f * g
- __eq__, relational quality: f.__eq__(g) is equivalent to f == g
- __gt__, greater than: f.__gt__(g) is equivalent to f > g
- __sub__, subtraction: f.__sub__(g) is equivalent to f - g
- __neg__, unary minus: f.__neg__() is equivalent to -f

 

Let's apply these concepts to what you have learned from previous lectures in the next slide!

Object Mutability and Aliasing

A variable is a name that labels an object. We have treated a variable as if it represents the object. For example, given the following statement:

We often refer to the object \(frac1\). In fact, this \(frac1\) "object" is the \(Fraction\) object representing the rational number \(\frac{1}{2}\).

 

To better understand objects, let us have a deeper look.

Object Mutability and Aliasing

A variable is a name that labels an object. We have treated a variable as if it represents the object. For example, given the following statement:

We often refer to the object \(frac1\). In fact, this \(frac1\) "object" is the \(Fraction\) object representing the rational number \(\frac{1}{2}\).

 

To better understand objects, let us have a deeper look.

Object Mutability and Aliasing

Object Mutability and Aliasing

This aliasing behavior might be a problem. Let's say we created two class variables c1 and c2.

Why is this so?

Garbage Collection

The following statement:

 

f  = Fraction(2, 3)

 

Creates a Fraction object representing the rational number \(\frac{2}{3}\) and assigns the variable f to the object. By doing so, the Python interpreter reserves some computer memory to hold the object and initializes the objects as such:

 

f.numerator = 2

f.denominator = 3

Garbage Collection

If we reassign the statement in the previous slide to be:

 

f  = None

Where None is a special type which represents no object, the Fraction object previously created is now effectively abandoned and is unused for the remainder of the program's execution. In other words, it is now garbage.

 

Garbage is memory allocated by a program which is no longer accessible by the program. Python's interpreter is able to automatically do garbage collection using a reference counting garbage collector.

Reference Counting

Let us look at the visualization below:

CS1302 Lecture 5

By Chung Chan

CS1302 Lecture 5

  • 187