%%html
<!-- The customized css for the slides -->
<link rel="stylesheet" type="text/css" href="../styles/python-programming-introduction.css"/>
<link rel="stylesheet" type="text/css" href="../styles/basic.css"/>
# Install the necessary dependencies

import os
import sys
!{sys.executable} -m pip install --quiet pandas scikit-learn numpy matplotlib jupyterlab_myst ipython

43.3. Python programming advanced#

43.3.1. Table of Content#

  • Control flow

    • if, for, range, while, break, continue

  • Functions

    • Default argument values, keyword arguments, arbitrary argument lists, unpacking argument lists

    • Lambda expressions, documentation strings, function annotations, function decorators

  • Classes

    • Class, object, method, inheritance, multiple inheritance

  • Modules

    • import, Packages

  • Errors and exceptions

43.3.2. Control flow#

43.3.2.1. The if statement#

  • Used for conditional execution

  • If a condition is true, we run a block of statements

  • There can be zero or more elif parts

  • The else part is optional

  • An if … elif … elif … sequence is a substitute for the switch or case statements found in other languages

number = 15
if number < 0:
    print('Number is less than zero')
elif number == 0:
    print('Number equals to zero')
elif number < 1:
    print('Number is greater than zero but less than one')
else:
    print('Number bigger than or equal to one')
Number bigger than or equal to one

43.3.3. Control flow#

43.3.3.1. The for statement#

  • Sequence: an ordered collection of items

  • A looping statement which iterates over a sequence of objects, i.e. go through each item in a sequence

  • An else clause is optional, when included, it is always executed once after the for loop is over unless a break statement is encountered.

words = ['cat', 'window', 'a', 'defenestrate']

for word in words:
    print(len(word))
3
6
1
12
words = ['cat', 'window', 'a', 'defenestrate']

for word in words:
    if len(word) == 1:
        break
    print(len(word))
else:
    print('All words are printed!')
3
6

43.3.4. Control flow#

43.3.4.1. The for statement#

The for statement can be used in tandem with:

  • range(): to iterate over a sequence of numbers

iterated_numbers = []

for number in range(5):
    iterated_numbers.append(number)

print(iterated_numbers)
[0, 1, 2, 3, 4]

43.3.5. Control flow#

43.3.5.1. The for statement#

The for statement can be used in tandem with:

  • enumerate(): to get a counter and the value from the iterable at the same time

words = ['Mary', 'goes', 'to', 'school']

for word_index, word in enumerate(words):
    print(str(word_index) + ' ' + word)
0 Mary
1 goes
2 to
3 school

43.3.6. Control flow#

43.3.6.1. The for statement#

The for statement can be used in tandem with:

  • items(): to loop through dictionaries

  • the key and corresponding value can be retrieved at the same time

student_admission = {'Ming': 'Shanghai University', 'Hong': 'Peking University'}
names = []
universities = []

for key, value in student_admission.items():
    names.append(key)
    universities.append(value)

print(names)
print(universities)
['Ming', 'Hong']
['Shanghai University', 'Peking University']

43.3.7. Control flow#

43.3.7.1. The while statement#

  • Repeatedly executes a block of statements as long as a condition is true

  • True and False

  • Any non-zero integer value is true; zero is false

number = 2
power = 5
result = 1

while power > 0: # or, while power:
    result *= number
    power -= 1

# 2^5 = 32
assert result == 32

43.3.8. Control flow#

43.3.8.1. The break statement#

  • Breaks out of the innermost enclosing “for” or “while” loop

number_to_be_found = 42

# This variable will record how many time we've entered the "for" loop.
number_of_iterations = 0

for number in range(100):
    if number == number_to_be_found:
        # Break here and don't continue the loop.
        break
    else:
        number_of_iterations += 1

assert number_of_iterations == 42

43.3.9. Control flow#

43.3.9.1. The continue statement#

  • Continues with the next iteration of the loop

even_numbers = []

for number in range(0, 10):
    if number % 2 == 0:
        even_numbers.append(number)
        continue

print(even_numbers)
[0, 2, 4, 6, 8]

43.3.10. Functions#

  • A function is a group of statements that exist within a program for the purpose of performing a specific task

  • Since the beginning of the session, we have been using a number of Python’s built-in functions, including:

    • len()

    • range()

    • print()

  • The keyword def introduces a function definition, which is followed by the function name and the parenthesized list of formal parameters

43.3.11. Functions#

43.3.11.1. Defining a function#

def myfunction():
    print ("Printed from inside a function")

43.3.11.2. Calling the function#

myfunction()
Printed from inside a function
# Defining a function
def greet(name):
    return 'Hello, ' + name

# Calling the function
print(greet('John'))
Hello, John
# Defining a function
def greet(name):
    return 'Hello, ' + name

# Assign functions to variables
greet_someone = greet

# Calling the function
print(greet_someone('John'))
Hello, John
# Define functions inside other functions

def greet_again(name):
    def get_message():
        return 'Hello, '

    result = get_message() + name
    return result

print(greet_again('John'))
Hello, John
# Functions can be passed as parameters to other functions

def greet_one_more(name):
    return 'Hello, ' + name

def call_func(func):
    other_name = 'John'
    return func(other_name)

print(call_func(greet_one_more))
Hello, John

43.3.12. Functions#

43.3.12.1. Scopes of variables inside functions#

  • Local variable:

    • Variables that are defined inside a function are considered “local” to that function

    • Objects outside the “scope” of the function will not be able to access that variable

    • Different functions can have their own local variables that use the same variable name

    • These local variables will not overwrite one another since they exist in different “scopes”

def count_bugs():
    numbugs = 1000
    print ("Shanghai has", numbugs, "bugs")

count_bugs()
Shanghai has 1000 bugs

43.3.13. Functions#

43.3.13.1. Scopes of variables inside functions#

  • Global Variables:

    • When a variable is created outside all of your functions it is considered a “global variable”

    • Global variables can be accessed by any statement in your program file, including by statements in any function

    • If you want to be able to change a global variable inside a function you must first tell Python that you wish to do this using the “global” keyword inside your function

name = 'Mary'

def show_name():
    global name
    print("Function 1:", name)
    name = 'John'
    print("Function 2:", name)

print("Before calling the function:", name)
show_name()
print("After calling the function:", name)
Before calling the function: Mary
Function 1: Mary
Function 2: John
After calling the function: John
# Passing Arguments to a function

def square(num):
    print(num ** 2)

square(5)
25
# Passing Multiple Arguments to a function

def average(num1, num2, num3):
    sum = num1 + num2 + num3
    avg = sum / 3
    print(avg)

average(2, 4, 9)
5.0
# A function can return multiple values

def myfunction():
    x = 5
    y = 10
    return x, y

p, q = myfunction()

print(p)
print(q)
5
10
# Default argument values

def power_of(number, power=2):
    """ Raises number to specific power.
    You may notice that by default the function raises number to the power of two.
    """
    return number ** power

print(power_of(3))
print(power_of(2, 4))
print(power_of(2, power=4))
9
16
16
# Keyword arguments

def my_function(first_name, last_name, age):
    print(first_name, last_name, age)

my_function(age=22, last_name="John", first_name="James")
James John 22
# Arbitrary argument lists

def product(*numbers):
    total = 1
    for n in numbers:
        total *= n
    return total

print(product(2, 4))
print(product(3, 4))
print(product(4, 5, 2))
8
12
40
# Arbitrary argument lists with keyword arguments

def product(*numbers, initial=1):
    total = initial
    for n in numbers:
        total *= n
    return total

print(product(2, 4))
print(product(3, 4, initial=1))
print(product(4, 5, 2, initial=3))
8
12
120

43.3.14. Unpacking argument lists (* and ** statements)#

def fun(a, b, c, d):
    print(a, b, c, d)

my_list = [1, 2, 3, 4]

# This doesn't work
fun(my_list)

43.3.14.1. Code output:#

TypeError: fun() takes exactly 4 arguments (1 given)

43.3.15. Unpacking argument lists (* and ** statements)#

  • *args (Non-keyword arguments)

  • **kwargs (Keyword arguments)

# Unpacking argument lists (`*` and `**` statements)

def print_four_numbers(a, b, c, d):
    print(a, b, c, d)

my_list = [1, 2, 3, 4]

# Unpacking list into four arguments
print_four_numbers(*my_list)
1 2 3 4
# Unpacking argument lists (`*` and `**` statements)

def print_four_numbers(a, b, c, d):
    print(a, b, c, d)

my_list = [1, 2, 3, 4, 5]

# Unpacking list into four arguments

print_four_numbers(*my_list)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_30912\2198447506.py in <module>
      8 # Unpacking list into four arguments
      9 
---> 10 print_four_numbers(*my_list)

TypeError: print_four_numbers() takes 4 positional arguments but 5 were given
def fun(*args):
    for arg in args:
        print(arg)

fun('Hello', 'Welcome', 'to', 'Open', 'Academy', 'Machine', 'Learning')
Hello
Welcome
to
Open
Academy
Machine
Learning
# Unpacking argument lists (`*` and `**` statements)

def fun(a, b, c):
    print(a, b, c)

d = {'a' : 2, 'b' : 4, 'c' : 10}
fun(**d)
2 4 10
# Unpacking argument lists (`*` and `**` statements)

def fun(first_word, second_word):
    return first_word + ', ' + second_word + '!'

dict_1 = {'first_word': 'Hello', 'second_word': 'World'}
print(fun(**dict_1))

dict_2 = {'second_word': 'World', 'first_word': 'Hello'}
print(fun(**dict_2))
Hello, World!
Hello, World!
# Lambda expressions

x = lambda a : a + 10
print(x(5))
15
# Lambda expressions

x = lambda a, b : a * b
print(x(5, 6))
30
# Lambda expressions

pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
pairs.sort(key=lambda pair: pair[1])

print(pairs)
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
# Using lambda() Function with filter()

# Example 1: Filter out all odd numbers using filter() and lambda function

li = [5, 7, 22, 97, 54, 62, 77, 23, 73, 61]
final_list = list(filter(lambda x: (x % 2 != 0), li))
print(final_list)
[5, 7, 97, 77, 23, 73, 61]
# Using lambda() Function with filter()

# Example 1: Example 2: Filter all people having age more than 18, using lambda and filter() function

ages = [13, 90, 17, 59, 21, 60, 5]
adults = list(filter(lambda age: age > 18, ages))
print(adults)
[90, 59, 21, 60]
# Using lambda() Function with map()
# Example 1: Multiply all elements of a list by 2 using lambda and map() function

li = [5, 7, 22, 97, 54]
final_list = list(map(lambda x: x * 2, li))
print(final_list)
[10, 14, 44, 194, 108]
# Using lambda() Function with map()
# Example 2: Transform all elements of a list to upper case using lambda and map() function

animals = ['dog', 'cat', 'parrot', 'rabbit']
uppered_animals = list(map(lambda animal: animal.upper(), animals))
print(uppered_animals)
['DOG', 'CAT', 'PARROT', 'RABBIT']

43.3.16. Functions#

43.3.16.1. Documentation strings (docstring)#

“Code is more often read than written.”

— Guido van Rossum

  • A docstring is a string literal that occurs as the first statement in a module, function, class, or method definition.

  • Such a docstring becomes the __doc__ special attribute of that object.

# Documentation strings

def square(n):
    '''Takes in a number n, returns the square of n'''
    return n**2

print(square.__doc__)
Takes in a number n, returns the square of n
# Documentation strings

print(print.__doc__)
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)

Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file:  a file-like object (stream); defaults to the current sys.stdout.
sep:   string inserted between values, default a space.
end:   string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
# Documentation strings

import pickle

print(pickle.__doc__)
Create portable serialized representations of Python objects.

See module copyreg for a mechanism for registering custom picklers.
See module pickletools source for extensive comments.

Classes:

    Pickler
    Unpickler

Functions:

    dump(object, file)
    dumps(object) -> string
    load(file) -> object
    loads(string) -> object

Misc variables:

    __version__
    format_version
    compatible_formats
help(print)
Help on built-in function print in module builtins:

print(...)
    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
    
    Prints the values to a stream, or to sys.stdout by default.
    Optional keyword arguments:
    file:  a file-like object (stream); defaults to the current sys.stdout.
    sep:   string inserted between values, default a space.
    end:   string appended after the last value, default a newline.
    flush: whether to forcibly flush the stream.

43.3.17. Functions#

43.3.17.1. Function annotations#

  • Function annotations are completely optional metadata information about the types used by user-defined functions.

# Function annotations

def breakfast(ham: str, eggs: str = 'eggs') -> str:
    return ham + ' and ' + eggs

print(breakfast.__annotations__)
{'ham': <class 'str'>, 'eggs': <class 'str'>, 'return': <class 'str'>}

43.3.18. Functions#

43.3.18.1. Function decorators#

  • Wrappers to existing functions

  • Dynamically alter the functionality of a function

  • Ideal when you need to extend the functionality of functions that you don’t want to modify

# Function decorators

def greeting(name):
    return "Hello, {0}!".format(name)

def decorate_with_p(func):
    def function_wrapper(name):
        return "<p>{0}</p>".format(func(name))
    return function_wrapper

my_get_text = decorate_with_p(greeting)

print(greeting('John'))
print(my_get_text('John'))
Hello, John!
<p>Hello, John!</p>
# Function decorators

def decorate_with_p(func):
    def function_wrapper(name):
        return "<p>{0}</p>".format(func(name))
    return function_wrapper

@decorate_with_p
def greeting(name):
    return "Hello, {0}!".format(name)

print(greeting('John'))
<p>Hello, John!</p>
# Function decorators

def decorate_with_div(func):
    def function_wrapper(text):
        return "<div>{0}</div>".format(func(text))
    return function_wrapper

@decorate_with_div
def greeting(name):
    return "Hello, {0}!".format(name)

print(greeting('John'))

@decorate_with_div
@decorate_with_p
def greeting(name):
    return "Hello, {0}!".format(name)

print(greeting('John'))
<div>Hello, John!</div>
<div><p>Hello, John!</p></div>

43.3.19. Classes#

  • To group data and functions belonging together to form custom data types

  • A Class is like an object constructor or a “blueprint” for creating objects

# Classes

class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age

  def say_hello(self):
    print("Hello my name is " + self.name + ", I am " + str(self.age) + " years old.")

p1 = Person("John", 36)
p1.say_hello()

p2 = Person("Mary", 25)
p2.say_hello()

p1.age = 39
p1.say_hello()
Hello my name is John, I am 36 years old.
Hello my name is Mary, I am 25 years old.
Hello my name is John, I am 39 years old.

43.3.20. Classes#

43.3.20.1. Inheritance#

  • Allows a derived class to reuse the same code and modify it accordingly

  • Derived classes may override methods of their base classes.

# Inheritance

class Person:
    def __init__(self, name):
        self.name = name

    def get_name(self):
        return self.name

class Employee(Person):
    def __init__(self, name, staff_id):
        Person.__init__(self, name) # super().__init__(name)
        self.staff_id = staff_id

    def get_full_id(self):
        return self.get_name() + ', ' + self.staff_id

employee = Employee('John', 'A23')
print(employee.get_full_id())
John, A23
# Multiple inheritance

class Clock:
    time = '11:23 PM'
    def get_time(self):
        return self.time

class Calendar:
    date = '12/08/2018'
    def get_date(self):
        return self.date

class CalendarClock(Clock, Calendar):
    pass

calendar_clock = CalendarClock()
print(calendar_clock.get_date())
print(calendar_clock.get_time())
12/08/2018
11:23 PM

43.3.21. Modules#

  • As your program gets longer, you may want to split it into several files for easier maintenance

  • Python has a way to put definitions in a file and use them in a script or in an interactive instance of the interpreter

  • Such a file is called a module

  • A module is a file containing Python definitions and statements

  • A Package is a way of structuring Python’s module namespace by using “dotted module names”

# Modules

import sys
sys.path
['C:\\Users\\lunde\\PycharmProjects\\machine-learning-ocademy-ai\\open-machine-learning-jupyter-book\\slides\\python-programming',
 'd:\\programs\\python3_7_4\\python37.zip',
 'd:\\programs\\python3_7_4\\DLLs',
 'd:\\programs\\python3_7_4\\lib',
 'd:\\programs\\python3_7_4',
 '',
 'C:\\Users\\lunde\\AppData\\Roaming\\Python\\Python37\\site-packages',
 'd:\\programs\\python3_7_4\\lib\\site-packages',
 'd:\\programs\\python3_7_4\\lib\\site-packages\\docloud-1.0.257-py3.7.egg',
 'd:\\programs\\python3_7_4\\lib\\site-packages\\win32',
 'd:\\programs\\python3_7_4\\lib\\site-packages\\win32\\lib',
 'd:\\programs\\python3_7_4\\lib\\site-packages\\Pythonwin',
 'C:\\Users\\lunde\\AppData\\Roaming\\Python\\Python37\\site-packages\\IPython\\extensions',
 'C:\\Users\\lunde\\.ipython']
# Packages

# import only pi from math module
from math import pi

print("The value of pi is", pi)
The value of pi is 3.141592653589793
# Packages

# import all names from the standard module math
from math import *

print("The value of pi is", pi)
print("The value of sin(pi) is", sin(pi))
The value of pi is 3.141592653589793
The value of sin(pi) is 1.2246467991473532e-16

43.3.22. Let’s try with our own module#

We write those code into the file my_module.py:

def my_sum(a, b):
    return a + b
%%writefile my_module.py

def my_sum(a, b):
    return a + b
Writing my_module.py
from my_module import my_sum

print(my_sum(3, 5))
8
!rm my_module.py

43.3.23. Errors and exceptions#

  • Even if a statement or expression is syntactically correct, it may cause an error when an attempt is made to execute it

  • Errors detected during execution are called exceptions and are not unconditionally fatal

try:
    result = 10 * (1 / 0)  # division by zero
    print("Result: ", result)
except ZeroDivisionError:
    print("Error: division by zero")
Error: division by zero
try:
    result = 10
    print("Result: ", result)
    result += 'a'
    print("Result: ", result)
except ZeroDivisionError:
    print("Error: division by zero")
except:
    # We should get here because of division by zero.
    print("Error: unexpected error")
Result:  10
Error: unexpected error

43.3.24. Your turn! 🚀#

  • Practice the Python programming (advanced) by following the assignment

43.3.25. References:#