This is an old revision of the document!


1. Introduction to Python

Python3 is a multi-paradigm language, that combines the imperative style with the functional style. It also supports Object-Oriented programming, albeit in a limited way.

Python3 offers a few programming constructs that:

  • make life real easy for the programmer,
  • yield efficient programs,
  • sum-up to a programming style called pythonic (a blend of imperative and functional features).

This lab will introduce some of these constructs.

List traversal

1.1. Write a function f(l) which determines the maximum of a list l of integers.

The pythonic way

The pythonic way

Traversing a list using a for is done as follows:

for elem in collection:
    # do something

This idiom can be used for any collection not just lists, as you will discover during the lab.

1.2. Modify the previous function to f(l,start,stop) and determine the maximum between positions start and stop.

The pythonic way

The pythonic way

When we care about the indices of a list, we can use the function range(x,y) which returns a list of integers starting from x until y-1.

for i in range(0,len(collection)):
    # do something with collection[i]

Slicing lists

1.3. Find the longest sequence of positive integers from a list. E.g. for the list [1,3,-1,2,0,1,5,4,-2,4,5,-3,0,1,2] the answer is [2,0,1,5,4].

The pythonic way

The pythonic way

Python supports various list slicing operations, as well as “overloaded” indexing:

# the sublist from positions 0 to x of l:
l[0:x]
# the very same list:
l[:x]
# the last element of a list:
l[:-1]
# the last three elements of a list:
l[:-3]
# the slice from n-3 to n-1, where n is the number of elements from the list
l[-3:-1]

1.4. Write a pythonic function to check if a list is palindrome.

The most widely used datatypes in Python are lists and dictionaries. Additionally, at LFA, we will also use: sets, stacks.

Stacks

In Python, lists are also used as stacks:

l = []
l.append(x)   # add x at the TOP of the stack (this will be the last element of the list)
l[-1]         # this is the top of the stack
x = l.pop()   # removes and returns an element from the top of the stack.

1.5. Write a function which determines (and returns) the longest sequence of consecutive integers from a list.

Dictionaries

Python dictionaries are actually hash maps (or hash tables). The keys k are dispersed using a hash-function into buckets. Each bucket will hold its respective values. Some examples of dictionary usage:

#create an empty dictionary:
d = {}
 
#add or modify a key-value:
d[k]=v
 
#search if a key k is defined in a dictionary d:
if k in d:
  # do something

1.6. Write a function which determines the number of occurrences of each character in a string.

The pythonic way

The pythonic way

def count(l):
   d = {}
   for x in l:
      if x in d:
         d[x] += 1
      else:
         d[x] = 1
   return d

Sets

Sets are collections where each element is unique. Sets can be traversed and indexed exactly as lists. They are initialised using the constructor set:

# the empty set:
s = set()
 
# set constructed from a list:
s = set([1,2,3])
 
# adding a new element to the set:
s.add(4)

1.7. Determine the list of characters which occur in a string. E.g. for “limbajeformale” we have: “limbajefor”. (Hint 1: in Python there is no difference between a character and a singleton string and strings are just lists; Hint 2: to create a list from another object, use list()).

Pairs

Pairs are very similar to lists and can be substituted by the latter in many cases:

p = (x, y)     # a pair where the first element is x and the second is y
t = (x, y, z)  # a tuple
 
fst = p[0]
snd = p[1]
 
for k in t:
  # do something with element k of the tuple
 

Higher-order functions and lambdas

The most used higher-order functions in Python are map and reduce:

from functools import reduce
 
def allplus1(l):
   return map(lambda x:x+1,l)
 
def sum_l (l):
   # inner functions are very useful for defining local, reusable functionality
   def plus(x,y):
      return x + y
   return reduce(plus,l) # reduce is a little different from Haskell folds: it does not use an initial value
 
def short_sum(l):
   return reduce(lambda x,y:x+y,l) # observe the syntax for lambdas

List comprehensions

1.8. Write a function which takes a list of records first name, last name, CNP (encoded as tuples), and returns a list of the last names of all females having the age below the average of all ages. E.g. [ (“Mary”, “Smith”, 2030694123456), (“Anne”, “Doe”, 2121092123456), (“Matei”, “Dan”, 1121202123456), (“Maggie”, “Byrne”, 2121078123456)] yields [“Smith”, “Doe”]. Maggie was born in '78

Inner functions List comprehensions, filters Classes and inheritance, instance-of toString Higher-order functions and lambdas Unpacking (for tuples, lists)

The Pythonic way

The Pythonic way

This text will be hidden

 solution 

Exercise 6 Write a function which searches for a list of patterns in a text.

def find_patterns (pattern_list, text):
    # checks if pattern is found at position index in text
    def inner_search (pattern,index):

Remark:

  • Python supports functional-style programming to some extent.
def plus1(x):
    return x + 1
 
print(map(plus1,[1,2,3]))
print(map(lambda x:x+1, [1,2,3]))

Exercise 8 Modify the previous implementation and instead of for, use map (cast the return of map to list: list(map(…)))

However, it is more common in Python to employ list comprehensions instead of map:

def plus1(x):
    return x + 1
 
print([plus1(x) for x in [1,2,3]])
print([(x + 1) for x in [1,2,3]]))

List comprehensions also support the functionality of filter:

print([(x+1) for x in [1,2,3,4,5,6] if (x % 2 == 0)])

Exercise 9 Modify the previous implementation and instead of for, use list comprehensions.

Classes and inheritance

We discuss a few basics on classes and inheritance starting from the following example:

class Tree:
 
    def size(self):
        pass
    def contains(self, key):
        pass
 
class Void(Tree):
    def __init__(self):
        pass
 
    def __str__(self):
        return "Nil"
 
    def size(self):
        return 0
 
    def contains(self,key):
        return False

In the previous example, the class Tree acts as an interface. Python does not natively support interfaces, but class inheritance is supported. The instruction pass does nothing, and it helps us defer the method implementation.

The definition:

class Void(Tree)

tells us that class Void inherits Tree. Note that this contract is not binding in Python. The program will be interpreted even if Void does not correctly implement the methods in Tree.

The function

def __init__(self):

is the class constructor. We cannot explicitly define multiple constructors (but workarounds exist). Also note the mandatory presence of self, which is the Python equivalent of this. Each member class must mark as first argument self, otherwise it is not a proper member of the class, and just a nested function.

The function

def __str__(self):

is the Python equivalent for toString(). An object can be displayed using the function str

Exercise 9 Create the class Node which models non-empty trees. Implement methods size and contains.

List of exercises

  • Write a function which prints EACH repeating character from a string. (Hint: strings are lists of characters).
  • Write a function returns the number of repetitions of each character from a text. (Hint: use dictionaries to store the number of repetitions).
  • Write a function returns the number of unique characters from a list. Use Python sets.
  • Write a function which takes a pattern and a text and prints all indexes where an occurrence of pattern in text are found. Use Python list slicing.
  • Write a function which searches for a list of patterns in a text, by extending the stub below. Whenever possible, use map instead of for.
def find_patterns (pattern_list, text):
    # checks if pattern is found at position index in text
    def inner_search (pattern,index):
  • Extend the class example shown previously to include class Node which models non-empty trees. Implement methods size and contains.