haecksathon #7

Opening notes

Agenda

  • Short language history
  • A descriptive take
  • Language introduction
  • Practice

Language history

Python started as a successor to ABC in 1998.

It’s named after Monty Python’s Flying Circus.

The dictator

Guido van Rossum in 2014

Guido van Rossum in 2014, source

Logo 1990 - 2006

The old python logo used 1990 - 2006

Logo 2006 - present

The current python logo used 2006 - present
Python 3 meme

Python 3 is a bit of a story

2.7 -> 3.0

Python version history timeline

Language influences

  • Multi-paradigm, leaning towards object orientation
  • ABC: syntax
  • Haskell, Standard ML:
    • itertools, functools
    • list-, set-, dictionary- comprehensions

A Descriptive take

Visit aspects to approximate a shape

Read Eval Print Loop (REPL)

Python logo thinking

Indentation

  • Indentation is load-bearing
  • Indentation is usually 4 spaces
def foo(x, y):
    if y == 3:
        x += 1
    x *= 2
    return x

Typing

  • Typing is dynamic
  • Since 3.5 typing library for annotations
def foo(x, y):
    if y == 3:
        x += 1
    x *= 2
    return x
def foo(x: int, y: int) -> int:
    if y == 3:
        x += 1
    x *= 2
    return x

Multi paradigm

wikipedia: multi-paradigm programming languages

Garbage collection

We don’t allocate or free memory manually.

x = [2, 3, 5, 7]
x = "foo" * x[0] # 'foofoo'
x = x[1:4] # 'oof'

We ‘do stuff’, and memory is managed for us.

The PEP process

Haben wir noch peps meme

PEPs

Standard library

Reading: it’s ‘free’ ;)

  • Examples
    • ast: handle abstract syntax trees
    • re: regular expressions

Standard library

Standard library

Getting to know the language

Billion dollar mistake?

Yes: None

I call it my billion-dollar mistake.
It was the invention of the null reference in 1965.
Tony Hoare, 2009

Booleans

  • True, False
  • and, or, not
  • <, >, <=, >=, ==, !=

Comparison intuition

1 < 2 < 3
3 > 2 > 1
1 < 3 > 2
True + True
False * 3
2 > True
1 < 'test' # 💣

Arithmetic

+, -, *, /, **, %, //, ()

  • Support for mixed arithmetic
  • Builtin: int, float, complex, Fraction, Decimal

Strings

print("Hello World!")

'Something "like this"'
"Wouldn't \"'\" be practical?"

"""
What if we want multiline strings?
That mix both '"' and "'" wildly?
"""

'''Don't fret ;)'''

f"1 + 2 = {1 + 2}"

Control flow

..ideas of structured programming

if, elif, else

def f(x):
    y = x % 3
    if y == 0:
        return 'foo'
    elif y == 1:
        return 'bar'
    else:
        return 'baz'

for

for i in range(5):
    print(f"{i}: {i**2}")

for i in range(2, 6):
    if i == 4:
        continue
    print(f"{i}: {i**2}")

for i in range(5, 26, 5):
    print(f"{i}: {i**2}")

for

for x in [2, 3, 5, 7]:
    print(x)

for c in 'test':
    print(f"'{c}'")
d = {'foo': 2, 'bar': 3, 'baz': 5}

for k in d.keys():
    pass

for v in d.values():
    pass

for (k, v) in d.items():
    pass

for / break / else

for c in s:
    if c == 'e':
        print("aha!")
        break
else:
    print(':(')

while

def c(n: int) -> int:
    if n % 2 == 0:
        return n // 2
    else:
        return 3*n + 1
n = 39

while n != 1:
    print(n)
    n = c(n)

try / raise / except

def f(n: int) -> float:
    if n == 7:
        raise ValueError('f will not divide by 7.')
    return 5 / n

try / raise / except

def g(n: int) -> float:
    r = 0
    try:
        r = f(n)
    except ZeroDivisionError as e:
        print(f"We've had a little {type(e)}")
        return 0
    except:
        return 7
    else:
        r *= 2
    finally:
        print('we did it.')
    return r

Collections

  • Lists, Tuples, Dicts, Sets
  • Slicing, intersections, ..
  • Comprehensions

Collections

  • Lists: ['t', 'e', 's', 't']
  • Tuples: ('foo', 23, False)
  • Sets: {2, 3, 5, 7}
  • Dicts {'foo': 1, 'bar': 2, 'baz': 3}

Indexing & Slicing

xs = [1, 2, 3, 4, 5, 6]
xs[2]      # 3
xs[-3]     # 4
xs[2:4]    # [3, 4]
xs[-3:]    # [4, 5, 6]
xs[:-3]    # [1, 2, 3]
xs[::2]    # [1, 3, 5]
xs[1:-1:2] # [2, 4]

Sets

s1 = {1, 2, 3}
s2 = {3, 4, 5}

s1 | s2 # {1, 2, 3, 4, 5}
s1 & s2 # {3}
s1 - s2 # {1, 2}

Dicts

d = {"foo": 2, "bar": 3, "baz": 5}
d['foo']         # 2
d.get('lurch',7) # 7
d['lurch'] = 7   # {'foo': 2, 'bar': 3, 'baz': 5, 'lurch': 7}
'bar' in d       # True
d.items()  # dict_items([('foo', 2), ('bar', 3), ('baz', 5)])
d.keys()   # dict_keys(['foo', 'bar', 'baz'])
d.values() # dict_values([2, 3, 5])

Comprehensions

{x: str(x) * x for x in range(1, 11)}
triplets = [(a, b, c)
    for c in range(1, 101)
    for b in range(1, c)
    for a in range(1, b)
    if a*a + b*b == c*c]

Functions

  • Definition & Arguments
  • *args, **kwargs
  • Default values footgun

Arguments

def sub(a: int, b: int) -> int:
    return a - b
sub(5, 3)
sub(b=3, 7)
sub(a=7, b=3)
sub(b=3, a=2)
sub(7, a=2) # Nah!

*args

def minMaxSum(*xs):
    return (min(*xs), max(*xs), sum(xs))
minMaxSum(1, 2, 3) # (1, 3, 6)
xs = [2, 3, 5]
minMaxSum(*xs) # (2, 5, 10)

**kwargs

def stuff(a=7, **kwargs):
    d = { k: v * 2 for (k, v) in kwargs.items()}
    d['a'] = a**2
    return d
stuff(0)             # {'a': 0}
stuff(b=2, a=2, c=3) # {'b': 4, 'c': 6, 'a': 4}
d = {'foo': 1, 'bar': 2, 'baz': 3}
stuff(**d) # {'foo': 2, 'bar': 4, 'baz': 6, 'a': 49}

Default values footgun

def f(x: int, xs: list[int] = []) -> int:
    xs.append(x)
    return sum(xs)
f(1, [2, 3]) # 6
f(1) # 1
f(1) # 2
f(1) # 3

Classes

The grouping together of attributes (values) and methods (functions) alongside various abstractions.

Plain classes

class Bike:
    def __init__(self):
        self.distance_covered = 0

    def ride(self, distance: float | int):
        self.distance_covered += distance
    
    def get_distance(self):
        return self.distance_covered

    def __repr__(self):
        return f"Bike(distance_covered={self.distance_covered})"

Plain classes

b = Bike()       # Bike(distance_covered=0)
b.ride(5.5)      # Bike(distance_covered=5.5)
b.get_distance() # 5.5

dataclasses

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

    def __add__(self, p: Point):
        return Point(self.x + p.x, self.y + p.y)

    @property
    def length(self):
        return (self.x**2 + self.y**2)**0.5
p1 = Point(1, 2) # Point(x=1, y=2)
p2 = Point(2, 3) # Point(x=2, y=3)
p3 = p1 + p2     # Point(x=3, y=5)
p3.length        # 5.830951894845301

Skipping

  • Generators
  • Decorators
  • Overwrites
  • Statements like with, match
  • Coroutines, async, await, ..
  • … a whole lot

Practice

Transformation of the python logo to the cool s

Suggestion

What if we did Project Euler on this?

Namely: Problems 1, 2, 3