from IPython.core.display import Image
Wᴇɪʟ ɴɪᴄʜᴛ ᴊᴇᴅᴇ Sᴘʀᴀᴄʜᴇ ʙᴇɴᴜᴛᴢᴇʀғʀᴇᴜɴᴅʟɪᴄʜ ɪsᴛ
Eine flotte 40-Minuten Python Einführung.
Bitte unterbrecht mich sofort bei Fragen, Korrekturen oder Anregungen!
lukas-prokop.at/talks/glt14-python
Vortrag von Lukas Prokop
Grazer Linuxtage // 05.04.2014
"It was nice to learn Python; a nice afternoon" (Don Knuth, SAT 2012 in Trento, Italy)
python 2.x | Linux |
python 2.6 or 2.7 is installed. Run python in the terminal.
|
Windows | Manual installation via python.org | |
py3k | Linux | Install py3k via package manager |
Windows | Manual installation via python.org |
Gentoo and Arch Linux: /usr/bin/python
points to py3k per default
In this presentation I am using ipython3 notebook (a different REPL)
We are in the progress of migrating from python version 2 to 3. See Gerald's talk last year for migration tips.
Python 2.7 is the newest python 2.x version, but development goes on in Python 3 (currently Python 3.4). We use Python 3.4 in the following slides.
Python 2 and 3 are incompatible. Do not write Python code for both versions simultaneously!
print("Hello World")
variable = 1
print(variable)
variable = 42 - 5 * 3
print(variable)
variable = 2
variable = 3
print(variable)
print(type(variable))
# everything is an object (see OOP later) ("dir" lists all methods)
print(dir(variable))
# scalars
a = None # {None}
b = True # {True, False}
c = 1 # optional sign, sequence of digits
d = 1.5
e = "glt14" # " or '
f = b"glt14" # b literal for bytes
g = 3 + 1j # complex
print(type(a), type(b), type(c), type(d), type(e), type(f), type(g))
float('inf')
# arbitrary precision integers
print(2**64)
other_approach = (2**63 - 1 << 1) | 0b1
print(bin(other_approach))
print(other_approach + 1)
# these datatypes are all immutable
print(id(a) == id(True))
print(id(b) == id(1))
print(id(c) == id(1.5))
print(id(d) == id("glt14"))
print(id(e) == id(3 + 1j))
# The difference of repr(var) and str(var)
print(repr('Und er meinte: "Komm\' doch zu den Linuxtagen!"')) # repr
print(str('Und er meinte: "Komm\' doch zu den Linuxtagen!"')) # str
multiline = """This is a rather
lengthy "{}" and therefore
break it into \
several {}
"""
print(multiline.format("message", "lines")) # format is a *method* of the object *multiline*
print(multiline[0:13] + ".")
# You want unicode data internally. You get bytes via I/O.
# bytes = sequence of 1s and 0s
filecontent = b'[user]\n\tname = "m\xCE\xB5isterluk"\n\temail = "admin@lukas-prokop.at"'
# convert to unicode data (you *have* to know the encoding)
# str = sequence of unicode codepoints
content = filecontent.decode('utf-8')
print(len(filecontent))
print(len(content))
# now string operations on `content` work great and are well defined :)
# we remove 1 unicode point (which was *2* bytes) and add 1 new unicode point
content = content[0:17] + 'e' + content[18:]
# writing data somewhere = converting to output encoding
#with open('file.out', 'w') as fp:
# fp.write(content.encode('utf-8'))
# or fp.write shall take care of it (-:
#with open('file.out', 'w', encoding='utf-8') as fp:
# fp.write(content)
# native data structures
demo_list = [1, 2, 3, 4] # [heterogenous] sequence, mutable
demo_set = {1, 2, 3, 4} # [unordered] set of unique objects, mutable
demo_tuple = (1, 2, 3, 4) # semantical unit, immutable
demo_dict = {1: "one", 3: "three"} # {key: value}, mutable, keys must be immutable
print(len(demo_list), len(demo_set), len(demo_tuple))
# Question: Which data structure is not syntactically supported by other dynamic languages?
demo_set = {1, 2, 3, 4, 3}
print(len(demo_set))
nested_list = ["defn", "method", ["this", "m"], ["str", '"Your arg is: "', '" m']]
print(nested_list[0]) # zero-based indexing
print([i**2 for i in range(20)])
# pitfall #1: inclusive, exclusive
import random
# [0, 3)
print(list(range(0, 3)))
# [0, 3]
print({random.randint(0, 3) for n in range(20)})
# operators on objects
a = 1
b = 1
c = "1"
# testing equivalence
print(a == b)
print(a == c) # python dislikes coersion
forty_two = 42
answer = 42
Image(filename='python_var_names.png')
# conditional statements
if True:
print("Hello")
else:
print("World")
if False:
print("Hello")
else:
print("World")
cond1, cond2, cond3 = False, True, False
# No switch statement. Just elif!
if cond1:
print("The world")
elif cond2:
print("is")
elif cond3:
print("not")
else:
print("enough")
import math
if 3 <= math.pi < 4:
print('constant PI: {}'.format(math.pi))
print('constant e: {}'.format(math.e))
# chained comparisons supported by Python, Perl 6 and Mathematica
# for loop over list elements
for i in [42, 3.14159, 666, 256]:
print(i)
# unary "break" and "continue" statements are available
# even "else" for for-loops ^^
# iteration over 0..20
for i in range(20):
print(i)
# while loop
transitions = {1: 2, 2: 4, 3: 3, 4: 3}
visited = set()
current = 1
while current not in visited: # poetic à la Shakespeare ;)
visited.add(current)
current = transitions[current]
print(visited)
# functions = small processing units for repetitive tasks
def my_first_function(first_arg, second_arg):
print(first_arg)
print(second_arg)
# "Off-side rule": code block
# = successive lines of code with same level of indentation
# (please don't mix tabs and spaces; and prefer 4 spaces!)
# call the custom function
my_first_function("Hello", 0x007)
def function_with_return_value(first_arg=3): # default value = 3
return first_arg * 2
print(function_with_return_value(21))
print(function_with_return_value(1 + 2 + 3 + 4 + 5 + 6))
print(function_with_return_value())
def compute_speed(distance_in_m: float, time_in_sec: int) -> float:
"""Computing the speed.
:param distance_in_m: How many meters are we talking about?
:param time_in_sec: In which time span were these meters passed?
:return: Return the speed in km/k
"""
# "/" is float division
# "//" is integer division
return (distance_in_m / time_in_sec) * 3.6
print('{} m/s are {} km/h'.format(42, compute_speed(42, 1)))
def affine(a, b, c):
return a + b * c
print(affine(1, 2, 3))
print(affine(b=2, a=1, c=3)) # passing by keyword
print(affine(1, 2, c=3))
print(affine(*[1, 2, 3]))
print(affine(**{'a': 1, 'b': 2, 'c':3}))
# pitfall #2: CPython implementation error: mutable default parameters
def push_one(lst=[]):
lst.append(1)
return lst
val = push_one()
print(val)
val = push_one()
print(val) # WTH?
def push_one_fixed(lst=None):
if lst is None:
lst = []
lst.append(1)
return lst
val = push_one_fixed()
print(val)
val = push_one_fixed()
print(val) # :)
# lambdas are anonymous functions (only expressions, use for tiny functions)
add_one = lambda x: x + 1
print(add_one(3))
values = [('127.0.0.1', 80), ('83.246.69.174', 22), ("216.239.32.27", 80)]
print(list(map(lambda x: x[0], values)))
def generator_function():
yield 0
yield 1
yield 1
yield 2
yield 3
yield 5
iteration = generator_function()
print(next(iteration))
print(next(iteration))
print(next(iteration))
print(next(iteration))
print(next(iteration))
print(next(iteration))
# print(next(iteration)) # raises StopIteration
def infinity(start=0):
current = start
while True:
yield current
current += 1
for element in infinity():
print(element)
if element >= 20:
break
# Class-based object orientation
# - polymorphism
# - inheritance
# - encapsulation
#
# NO protected, private and public!
# Python idiom: "We are all adults here!"
# name mangling for members starting with "__"
class Car:
def __init__(self):
self.seats = 4
self.radio = False
def __repr__(self):
return "A car"
def speed_up(self, acce):
self.radio += acce
class Glt14Transporter(Car):
def __init__(self):
super().__init__() # super() refers to all parent objects
self.seats = 7
vehicle = Glt14Transporter() # call constructor "__init__"
print(vehicle.seats) # access public member "seats"
print(vehicle.radio) # access public member "radio" inherited from "Car"
# Methods with "__" at beginning and end are special.
# "__init__" is the constructor. "Car.__repr__" is called when repr(obj) is called with obj as instance of Car
# "__len__" represents the length of an object representing a sequence. Returned by len(obj).
# "__eq__" is triggered for comparison with other objects.
#
# And so on and so on…
# See https://docs.python.org/2/reference/datamodel.html
# Python has exceptions
# SyntaxError, ValueError, TypeError, OSError, …
a = "this is not an integer"
int(a)
class ParsingError(Exception):
def __str__(self):
return 'My personal parsing error: ' + self.args[0]
raise ParsingError("Unexpected token '.'")
# Python idiom:
# "Easier to ask for forgiveness than permission"
# Do it and if it fails, do it with another approach
# DO
try:
# I/O example code: Use with statement to manage file handling in a context
with open("/etc/passwd", "a") as fp:
fp.write("meisterluk:x:1000:1000:meisterluk,,,:/home/meisterluk:/bin/zsh\n")
except IOError:
print("Sorry, writing /etc/passwd went wrong")
raise # reraise
# DON'T
import os
if os.access('/etc/passwd', os.W_OK):
with open("/etc/passwd", "a") as fp:
fp.write("meisterluk:x:1000:1000:meisterluk,,,:/home/meisterluk:/bin/zsh\n")
else:
print("Sorry, writing /etc/passwd went wrong")
try:
1/0
except (ZeroDivisionError, TypeError) as e:
print("ZeroDivisionError or TypeError occured")
print(e) # e is error object
else:
print("No exception was raised. Nice!")
finally:
print("Thanks for all the fish!")
# a simple breadth-first search algorithm as real-world example
import collections
def bfs(root, get_children, data):
active = collections.deque([root])
collected_data = []
while active:
current = active.popleft()
for child in get_children(current):
active.append(child)
collected_data.append(data(current))
return collected_data
# tree:
# ↗ 2
# 0 → 1 → 3 → 5 → 6
# ↘ 4 → ↗
tree = [[1], [2,3,4], [], [5], [6], [6], []]
print(bfs(0, lambda n: tree[n], lambda n: n))
# python uses duck typing
# "When I see a bird that walks like a duck and swims like
# a duck and quacks like a duck, I call that bird a duck"
class ModularArithmetic:
def __init__(self, value, base=42):
self.base = base
self.value = value % base
def __repr__(self):
return str(self.value)
def __eq__(self, other):
return self.value == other
def __add__(self, other):
try:
summed = (self.value + other)
except TypeError:
summed = (self.value + other.value)
return ModularArithmetic(summed, self.base)
# DO
def total_sum(elements):
total = ModularArithmetic(0)
for elem in elements:
total = total + elem
return total
print(total_sum([1, 5, 9, ModularArithmetic(55), ModularArithmetic(155)]))
# DON'T
def total_sum(elements):
total = ModularArithmetic(0)
for elem in elements:
if isinstance(elem, ModularArithmetic):
# apply ModularArithmetics-specific addition
summed = total.value + elem.value
total += summed
elif isinstance(elem, int):
# apply int-specific addition
total += elem
return total
print(total_sum([1, 5, 9, ModularArithmetic(55), ModularArithmetic(155)]))
# Thus, do not check the type and apply specific operations.
# Just use the object and it should provide the operations itself.
# Python's import mechanism is pretty complex just like any other.
# Let's consider specific usecases.
# Import "module.py" from working directory
import module
# module.local_variable # access local_variable in module
# Import "module" package by importing folder "module/" containing a "__init__.py" file
import module
# module.local_variable # access local_variable in module/__init__.py
# Import "hashlib" from the stdlib
# If no file or package is found in working directory, import from any sys.path (stdlib)
import hashlib
# Packages might be nested.
# "import os" does not give you access to "os.path"
import os.path # functions of "os" and "os.path" are available now
# You can skip the namespace, but using "from module import class"
from module import local_variable
# "from import" and "import" has semantical differences
# for details see e.g. http://lukas-prokop.at/proj/snippets/py013.html
# Long story, short: I recommend "import" syntax
# Python's stdlib is huge!
# Python idiom: "Batteries included!"
# summary of idioms / design principles of Python
import this
python3 -m http.server
python3 -m cProfile <file>
Here in Graz?
Image(filename='pygraz.png')
Thanks fellows!
😁 Keep on hacking! ☕