Python Individualtraining (5 Tage, beginnend 28.10.2019)

Der Kurs nahm eine Woche in Anspruch, da hat so einiges Platz. Hier die gesammelten Notizen inklusive Code; für schnell erklärte Sachen gibts ein Jupyter Notebook (Download).

Exercises

Slide 51, “Exercises: Basics”

Write a program that takes a single digit as commandline parameter. Print the English word for that digit.

#!/usr/bin/python3

# Exercises: Basics (~51)

# Write a program that takes a single digit as commandline
# parameter. Print the English word for that digit.

import sys


translation = {
    0: 'zero',
    1: 'one',
    2: 'two',
    3: 'three',
    4: 'four',
    5: 'five',
    6: 'six',
    7: 'seven',
    8: 'eight',
    9: 'nine',
}

digit = int(sys.argv[1])
if 0 <= digit <= 9:
    print(translation[digit])
else:
    print('nix')

Slide 58, “Exercises: While Loop”

Write a program that takes an integer commandline parameter and checks whether that number is prime!

#!/usr/bin/python3

# Exercises: While Loop (~58)

# Write a program that takes an integer commandline parameter and
# checks whether that number is prime!


# Exercises: Lists, Loops, Functions (~94)

# Modify the prime number detection program from one of the previous
# exercises: make the prime number detection a function, and call the
# function instead. The function (is prime() is a likely name) takes a
# number, and returns a boolean value as appropriate.


import sys

def is_prime(candidate):
    if candidate < 2:
        return False

    divisor = 2
    while divisor <= candidate//2:
        if candidate % divisor == 0:
            return False
        divisor += 1
    else:
        return True



candidate = int(sys.argv[1])

print(is_prime(candidate))

Slide 94, “Exercises: Lists, Loops, Functions”

Write a function uniq() that takes a sequence as input. It returns a list with duplicate elements removed, and where the contained elements appear in the same order that is present in the input sequence. The input sequence remains unmodified.

#!/usr/bin/python3

# Exercises: Lists, Loops, Functions (~94)

# Write a function uniq() that takes a sequence as input. It returns a
# list with duplicate elements removed, and where the contained
# elements appear in the same order that is present in the input
# sequence. The input sequence remains unmodified.


# Deviations from the requirement:

# 1. implement uniq() as a generator. use yield to produce items.

# 2. demonstrate how to use uniq() as a filter, connecting nodes
#    together using "pipes" as in the pseudo-filter expression:

#    random | uniq | print


import random


def uniq(l):
    '''filter that iterates over the input sequence and produces an item
    only if that was not yet seen in the input sequence.

    '''

    have = set()
    for i in l:
        if i not in have:
            have.add(i)
            yield i

def randomnumbers(howmany):
    "produces'howmany' random numbers."
    for _ in range(howmany):
        yield random.randrange(10)


for i in uniq(randomnumbers(100)):
    print(i)

Slide 121, “Exercises: Strings”

Write a program that receives any number of arguments and prints them out right justified at column 20.

#!/usr/bin/python3

# Exercises: Strings (~121)

# Write a program that receives any number of arguments and
# prints them out right justified at column 20.

import sys


for s in sys.argv[1:]:
    while len(s) >= 20:
        print(s[:20])
        s = s[20:]
    print(s.rjust(20))


Miscellaneous

Famous Generator Introduction

Producing an infinite sequence (Fibonacci, what else)

#!/usr/bin/python3

# Generators, yield. implementing an infinite sequence (fibonacci is a
# cool example) which would not be so easy if we had only functions
# (these can only return once).

import time


def fibonacci():
    prev = 1
    cur = 1

    yield prev
    yield cur
    
    while True:
        next = prev + cur
        yield next
        prev = cur
        cur = next

for fibonum in fibonacci():
    print(fibonum)

eval(): Convert a String into a Python Data Structure

During the uniq exercise, people tend to want to pass a Python list from the commandline, naively.

Question: how can I take an argument from the commandline (say, sys.argv[1]) and interpret that as a list?

Answer: eval

The following program does that. Call it like so,

$ ./eval-argv.py '[1, 2, 3, 4]'

(‘$’ is the shell prompt, the quotes are necessary to prevent the shell from splitting arguments at the spaces)

#!/usr/bin/python3

import sys

input_list_string = sys.argv[1]
input_list = eval(input_list_string)
print(input_list)

Operator Overloading

Here’s a little snippet that demonstrates this. See the docs for more.

#!/usr/bin/python3

# question: from C++ I know that I can overload (arithmetic) operators
# for types/classes that I define. how is this done in Python?

class Integer:
    def __init__(self, n):
        self.__n = n

    @property
    def n(self):
        return self.__n

    def __lt__(self, rhs):
        return self.__n < rhs.__n

    def __le__(self, rhs):
        return not self > rhs

    def __eq__(self, rhs):
        return self.__n == rhs.__n

    def __iadd__(self, n):
        'iadd: +=, "in-place add"'
        self.__n += n
        return self

    def __add__(self, n):
        'add: +, called on self, the right hand "a" side in "a+b"'
        new_number = self.__n + n
        return Integer(new_number)

    def __radd__(self, n):
        'radd: +, called on the right hand side if the lhs does not support it'
        new_number = n + self.__n
        return Integer(new_number)

x = Integer(1)
y = Integer(2)

print('1<2', x < y)
print('2<1', y < x)
print('1>2', x > y)
print('1==1', x == x)
print('1!=1', x != x)
print('1<=2', x <= y)
print('1<=1', x <= x)

x += 1
print(x.n)

z = x + 1
print(z.n)

z = 1 + x
print(z.n)

Getters and Setters

Called “Properties” in Python; see below for a snippet. See the docs for more.

#!/usr/bin/python3

# question: in C# we have getters and setters. how is this done in
# Python?

# answer: use the property *decorator*

class MakesNoSense:
    def __init__(self, number):
        self.__number = number

    @property
    def number(self):
        return self.__number

    @number.setter
    def number(self, n):
        self.__number = n

num = MakesNoSense(42)
print(num.number)
num.number = 666
print(num.number)

More on Listcomprehensions and Generator Expressions

#!/usr/bin/python3


input_numbers = [1,2,3,4]

def squares(numbers):
    '''return a list containing the squares of the numnbers from the input
    sequence

    '''

    list_of_squares = []
    for i in numbers:
        list_of_squares.append(i**2)
    return list_of_squares

print('dumb function: squares({})'.format(input_numbers))
for i in squares(input_numbers):
    print(i)

# for such simple things as square numbers, use a list
# comprehension. this makes the code shorter - you omit a function
# definition.
print('list compehension: [n**2 for n in {}]'.format(input_numbers))
for i in [n**2 for n in input_numbers]:
    print(i)

# list comprehensions still allocate memory to hold the list. with
# minimal effort, you can save that allocation by transforming the
# list comprehension into a generator expression.
print('generator expression: (n**2 for n in {})'.format(input_numbers))
for i in (n**2 for n in input_numbers):
    print(i)

More on Local and Global Scope and Global Variables

#!/usr/bin/python3

# Functions: Local and Global Variables (~92)


def local_assignment():

    '''assign to l in local scope. this creates a local variable
    l. (variables are generally creates at first assignment.)

    '''

    l = 7

def read_global_variable():
    '''accesses a variable g that has never been assigned to in local
    scope. this goes out into the enclosing (global) scope and looks
    it up there.

    '''
    
    print('read_global_variable: g =', g)

def local_shadows_global():

    '''assign to g in local scope. this does *not* assign to the global
    variable g, but creates a local variable g.

    '''

    g = 42
    print('local_shadows_global: g =', g)

def explicit_global_assignment():
    global g

    print('explicit_global_assignment: before assignment g =', g)
    g = 42
    print('explicit_global_assignment: after assignment g =', g)


# first assignment to g in global scope creates g in global scope.
g = 666

local_assignment()
read_global_variable()

local_shadows_global()
print('global g =', g)

explicit_global_assignment()
print('global g =', g)

Closures: Between Local and Global

#!/usr/bin/python3

# closure: at the time of function creation (execution of the 'def
# statement), all referenced names are captured to form an
# intermediate scope, the 'closure'

def create_function(parameter):
    loc = 42

    # at this point, we have two variables in local scope - 'loc' and
    # 'parameter'.
    print('create_function: loc={}, parameter={}'.format(loc, parameter))

    # with this in place, create a function object by executing the
    # def statement. note how the function is not executed, but only
    # created/compiled.
    def inner_function():
        # reference variables loc and parameter. these are defined
        # neither in local nor in global scope. but they are found in
        # the enclosing scope - the locals of create_function(), which
        # forms an intermediate scope, the 'closure', whic his added
        # to the lookup chain of inner_function().
        print('parameter {}, loc {}'.format(parameter, loc))

    return inner_function

f_one = create_function('one')
f_one()
f_two = create_function('two')
f_two()

Project

We had two days left, in addition to the usual three days which are sufficient to learn the basics. A group project was launched, with the somewhat real-life topic “talking to switches and doing all kinds of stuff with that information”.

This gave us the opportunity to discover a couple of areas more closely.

  • Object oriented programming (a switch has interfaces, and both have properties)

  • Storing all that in databases

  • Exception handling

  • Commandline interfaces

  • Unit testing

See Github for source code.