Yet Another Python 101


In [1]:
print("Hi, All!")


Hi, All!

Agenda

  • Advocacy.
  • High Speed python overview.
    • Duck or dynamic typing, everything is an object, but variables are only "tags".
    • Control the Python environment a.k.a searching modules, packages.
  • Built in types and data structures.
    • Primitives types (int, long, float).
    • Basic math.
    • Iterables (A.K.A. the Iterator Protocol).
    • Lists & tuples.
    • Strings.
    • Sets.
    • Files.
    • Maps.
  • Other data structures in standard library.
    • datetime.
    • heapq.
    • collections.
      • Counter.
      • deque.
      • namedtuple.
      • defaultdict.
      • OrderedDict.
  • Included Batteries.
    • csv.
    • unittest.
    • Other standard library goodies.
  • Python Language.
    • Indentation.
    • Python Keywords.
    • Python Built-ins.
    • if (falsiness in built in types).
    • for (the iterator protocol).
    • while.
    • try/except.
    • def (functions)
  • A bit of functional style.
    • High Order Functions and Closures.
    • Decorators.
  • Generators and Generator expressions.
  • Context Managers.
  • OOP
    • class.
    • constructor.
    • static methods.
    • class methods.
  • Not included batteries.

Advocacy

Why Python?

Look at Raymond Hettinger's slides.

Brief Bio: Python Core developer and main Python instructor @ Cisco.

My Opinion?

  • Easy to learn.
  • Incredibly versatile.
  • Indentation.
  • Interactive prompt (REPL).
  • "One way to do it". Python has a Zen.
  • Great Community.

In [2]:
import this


The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

TRIVIA: Tim Peters a.k.a Uncle Tim has invented a new sorting algorithm during Python's lists development.

High Speed Python Overview.

Dynamic (or Duck) typing.

Key concept: in Python everything is an object.   Key concept: variables have no type.   What does it means in practice?   Variables are only "tags", or "names" to refer to an object. Variables are bound to objects with the assignment operator "=".   If one tries to access an unassigned variable a NameError exception is raised.

a

NameError: name 'a' is not defined

Key concept: in Python objects have only attributes.   What does it means in practice?

When an object's method or attribute is "called" using the "dot notation", but the method/attribute is not present, an "AttributeError" exception is raised.   If the method/attribute is present, it gets "called".

In other words, Python will check only attribute presence at run time. Since there's no compiler or static typing is VITAL to TEST the script before use it in production!

a = None
a.strip()

AttributeError: 'NoneType' object has no attribute 'strip'

Modules and Packages.

Key Concept: a module is a "script" which contains objects used by another script who "imports" the module using the import keyword.   Key Concept: Python searches modules into his standard library path or in paths contained into the PYTHONPATH environment variable, which is a "colon" separated list of paths.

Key Concept: a Python package is a directory that contains a special filename: __init__.py, package's modules are all scripts into that directory. Modules may be imported in __init__.py.

  • Example: define a module with an integer object and import it.
  • Example: define a package that contains a module that contains a string object and import it.
  • Exercise: write a script that imports the module and the package and prints the integer and string objects.

Built in types and data structures.

GLOSSARY: builtin: anything implemented into the Python interpreter itself.

It's simple to obtain the list of all Python's builtins:


In [3]:
print(dir(__builtins__))


['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__IPYTHON__', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'display', 'divmod', 'enumerate', 'eval', 'exec', 'filter', 'float', 'format', 'frozenset', 'get_ipython', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

Types

Key concept: There are immutable types and mutable types.   What this means in practice?   An immutable object cannot be modified by any means, it can only be created or destroyed.   Immutable types are: int, long, float, bool, str, tuple.

Immutability

s = "hello"
s[1]
s[1] = 'i'

TypeError: 'str' object does not support item assignment

Primitive Types

  • int (integers)
  • float
  • bool

int, create.


In [4]:
i = 10
i


Out[4]:
10

In [5]:
i = int()
i


Out[5]:
0

int, cast.


In [6]:
i = int("10")
i


Out[6]:
10

In [7]:
i = int("F", 16)
i


Out[7]:
15

int, operators.

  • Arithmetric: += + - * ** %
  • Logic: > < >= <= == !=

float, create.


In [8]:
f = 10.0
f


Out[8]:
10.0

float, cast.


In [9]:
f = float(10)
f


Out[9]:
10.0

In [10]:
f = float("10")
f


Out[10]:
10.0

No base conversion for float!

float, operators

Ok, same as int.

  • Arithmetric: += + - * ** %
  • Logic: > < >= <= == !=

Be careful with == !=.

bool, create.


In [11]:
b = True
b


Out[11]:
True

In [12]:
b = bool()
b


Out[12]:
False

In [13]:
b = bool(1)
b


Out[13]:
True

bool, operators.

Ok, I think you got it!

Basic Math

With int, float, long types is possible to do basic floating point math, all results are promoted to float.

GLOSSARY: extension: a python module implemented in C, in form of a shared library.

Although the math module is not a built-in, it is implemented as C extension. For detail:

import math

help(math)


In [14]:
import math
i = 16
f = 16.0

In [15]:
math.sqrt(i)


Out[15]:
4.0

In [16]:
math.sqrt(f)


Out[16]:
4.0

Iterables.

key concept: The Iterable abstraction is a Python concept that generalizes the idea of "sequence".

We will see that "Iterable" objects are the only objects applicable to the "for" statements: in every iteration an Iterable yields an element of the "sequence" starting from the first element.

Tuples, lists, strings and sets are all Iterables and built-ins types. Tuples and strings are also immutable types.

Tuples

The following operators apply to tuples:  

  • Index operator [n] n position (int).
  • Slice operator [n:m:s] n,m range (int, int), s step (int).
  • All the comparison logical operators (<, >, <=, >=, ==, !=)
  • Concatenation operators (+, +=, *).
  • The containment operator "in".
  • Unpacking.

The built-in function len() can be used to count elements.

NOTE: len() is applicable to all "collection" objects, not only iterables.

Tuple create


In [17]:
t = 1,2,3,4,5
t


Out[17]:
(1, 2, 3, 4, 5)

In [18]:
t = (1,2,3,4,5)
t


Out[18]:
(1, 2, 3, 4, 5)

Tuple cast

Is possible to create an empty tuple or to cast (or transform) an Iterable into a tuple by using tuple().


In [19]:
t = tuple()
t


Out[19]:
()

In [20]:
t = tuple("12345")
t


Out[20]:
('1', '2', '3', '4', '5')

Index operator


In [21]:
t[0]


Out[21]:
'1'

In [22]:
t[1]


Out[22]:
'2'

In [23]:
t[-1]


Out[23]:
'5'
t[2] = 'A'

TypeError: 'tuple' object does not support item assignment

Slice operator


In [24]:
t[0:3]


Out[24]:
('1', '2', '3')

In [25]:
t[3:]


Out[25]:
('4', '5')

In [26]:
t[::2]


Out[26]:
('1', '3', '5')

In [27]:
t[1::2]


Out[27]:
('2', '4')

Concatenation


In [28]:
t = (1,2,3,4,5)
t += 6,7
t


Out[28]:
(1, 2, 3, 4, 5, 6, 7)

In [29]:
t = t * 2
t


Out[29]:
(1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7)

Containment operator


In [30]:
2 in t


Out[30]:
True

Please note that containment does not apply to "sub-tuples", but only to elements: in this case integers.


In [31]:
(1, 2) in t


Out[31]:
False

Count elements


In [32]:
len(t)


Out[32]:
14

Elements

Although I used integers elements, tuples may contain any object.


In [33]:
t = 1, 1.0, "hi, there"
t


Out[33]:
(1, 1.0, 'hi, there')

Unpacking


In [34]:
i, f, s = t
s


Out[34]:
'hi, there'

Lists

Same operators apply to lists:  

  • Index operator [n] n position (int).
  • Slice operator [n:m:s] n,m range (int, int), s step (int).
  • All the comparison logical operators (<, >, <=, >=, ==, !=)
  • Concatenation operators (+, +=, *).
  • The containment operator "in".
  • Unpacking.

The built-in function len() can be used to count elements

Lists are Mutable. Lists can be modified with methods, for example append.

References and Copies


In [35]:
l = [1,2,3,4,5]
l2 = l
id(l)


Out[35]:
4438804296

In [36]:
id(l2)


Out[36]:
4438804296

In [37]:
l2 = list(l)
l


Out[37]:
[1, 2, 3, 4, 5]

In [38]:
id(l2)


Out[38]:
4438700744

In [39]:
l3 = l[::]
l3


Out[39]:
[1, 2, 3, 4, 5]

In [40]:
id(l3)


Out[40]:
4438704008

I can modify a list


In [41]:
l3[2] = 9
l3


Out[41]:
[1, 2, 9, 4, 5]

In [42]:
id(l3)


Out[42]:
4438704008

Methods


In [43]:
print(dir(l))


['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

In [44]:
l.append(10)
l


Out[44]:
[1, 2, 3, 4, 5, 10]

Lists Performance

Operation Big-O Efficiency
index [] O(1)
index assignment O(1)
append O(1)
pop() O(1)
pop(i) O(n)
insert(i,item) O(n)
del operator O(n)
Operation Big-O Efficiency
iteration O(n)
contains (in) O(n)
get slice [x:y] O(k)
del slice O(n)
set slice O(n+k)
reverse O(n)
concatenate O(k)
sort O(n log n)
multiply O(nk)

So Python lists are "Array Lists", implemented into the interpreter. Use them as arrays without fear!

Sets

A set is a mutable, unordered, unique collection of objects. It is designed to reflect the properties and behavior of a true mathematical set.   The following operators apply to a set:  

  • The containment operator "in".
  • The built-in function "len()" can be used to count elements.
  • The set operations are: | (union), & (intersection), - (difference), ^ (simmetric_difference)
  • Sets can be manipulated with methods, for example issubset.
  • Sets operators have the corresponding methods.
  • Sets are iterable.

Methods


In [45]:
print(dir(set()))


['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__iand__', '__init__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']

A set can be created with set(). set() takes any iterable as parameters to create a set.


In [46]:
s = set([1,2,2,2,3,4,5,6,6])
s


Out[46]:
{1, 2, 3, 4, 5, 6}

In [47]:
l = list(s)
l


Out[47]:
[1, 2, 3, 4, 5, 6]

So, sets can be used to remove duplicates!

ATTENTION: Set are "iterables" but the insertion order is not guaranteed.

Sets have an immutable counterpart: frozenset


In [48]:
fs = frozenset("aaabbbcvfzggg")
fs


Out[48]:
frozenset({'a', 'b', 'c', 'f', 'g', 'v', 'z'})

Strings

String literals can be defined with any of single quotes ('), double quotes (") or triple quotes (''' or """). All give the same result with two important differences.  

  1. If you quote with single quotes, you do not have to escape double quotes and vice-versa.
  2. If you quote with triple quotes, your string can span multiple lines.

The following operators apply to strings:

  • Index operator [n] n position (int).
  • Slice operator [n:m:s] n,m range (int, int), s step (int).
  • All the comparison logical operators (<, >, <=, >=, ==, !=)
  • Concatenation operators (+, +=, *).
  • The containment operator "in".
  • The built-in function "len()" can be used to count elements.

Strings are immutable but have methods for example strip.


In [49]:
s = ''
s


Out[49]:
''

In [50]:
s = str()
s


Out[50]:
''

In [51]:
s = "Portuguese Man o' War what a strange animal!"
s


Out[51]:
"Portuguese Man o' War what a strange animal!"

In [52]:
s = 'yes, is made up of many individuals called "zoids"'
s


Out[52]:
'yes, is made up of many individuals called "zoids"'

In [53]:
s = """Is incredible isn't it?
watch out from his venom filled tentacles"""
s


Out[53]:
"Is incredible isn't it?\nwatch out from his venom filled tentacles"

Index operator


In [54]:
s[0]


Out[54]:
'I'
s[0] = 'U'

TypeError: 'str' object does not support item assignment

Immutability!

Concatenation


In [55]:
s = 'Hello!'
s


Out[55]:
'Hello!'

In [56]:
s += ' How are you?'
s


Out[56]:
'Hello! How are you?'

In [57]:
s * 2


Out[57]:
'Hello! How are you?Hello! How are you?'

NOTE: due to strings immutability, is advisable to avoid the use of string concatenation to produce long strings of text, this is more true in loops: string concatenation in loops is a performance killer. Is better to accumulate many strings in a list and "join" them, will will see this in next slides.

Containment


In [58]:
'Hello' in s


Out[58]:
True

The "in" operator is an handy way to check for sub-strings. Use it whenever possible.

Formatting

The format method is useful to produce formatted strings given sequence of data in form of a tuple or mapped data in form of a dictionary. This is more efficient than string concatenation.


In [59]:
s = "{} {} is an actor his age is {}".format("Johnny", "Depp", 56)
s


Out[59]:
'Johnny Depp is an actor his age is 56'

In [60]:
s = "{1} {0} is an actor his age is {2}".format("Depp", "Johnny", 56)
s


Out[60]:
'Johnny Depp is an actor his age is 56'

In [61]:
s = "{firstname} {lastname} is an actor his age is {age}".format(age=56,
firstname="Johnny", lastname="Depp")
s


Out[61]:
'Johnny Depp is an actor his age is 56'

Methods


In [62]:
print(dir(s))


['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

split and join

Split and join are two very useful string methods and are the base of the powerful Python's text processing capabilities.

text.split(separator) "splits" the text string in a list of strings where chunks are interleaved by the separator string. If there's no separator string as argument, the separator is any number of blanks: space or tabs.

separator.join(list) conversely produce a string from a list of strings where string chunks are "joined" by interleaving the separator string. If the separator is "", the empty string, chunks are joined without interleaving.

join can be used to produce a big text string from chunks accumulated into a list of strings in a very efficient way respect to string concatenation. For example the list of strings may contain the lines of a text file and join can be used to produce the final text of the text file.

split is the base for simple parsing tasks of text files or analysis of record oriented text files, like for example /etc/passwd.


In [63]:
s = 'here is a   string with many\tspaces'
s


Out[63]:
'here is a   string with many\tspaces'

In [64]:
chunks = s.split()
chunks


Out[64]:
['here', 'is', 'a', 'string', 'with', 'many', 'spaces']

In [65]:
chunks[5] = 'single'
' '.join(chunks)


Out[65]:
'here is a string with single spaces'

In [66]:
s.split(' ')


Out[66]:
['here', 'is', 'a', '', '', 'string', 'with', 'many\tspaces']

In [67]:
s.split('\t')


Out[67]:
['here is a   string with many', 'spaces']

In [68]:
''.join(s.split())


Out[68]:
'hereisastringwithmanyspaces'

In [69]:
','.join(s.split())


Out[69]:
'here,is,a,string,with,many,spaces'

Files

Files, again, are a Python interpreter built-in type. File access is very simple, files are always created by the open function that takes two string arguments: the file path to open and the type of operation: read or write

The main operations are "r", "rb", "w", "wb": text read, binary read, text write, binary write respectively. The default operation is "r" if no argument is given.

Python reserves a special treatment for text files: text files are iterable objects, this means that they can be used in a for statement and in every iteration a file object yields a line of text.


In [70]:
f = open('examples/greetings.txt')
print(f.read())
f.close()


eng it
hello ciao
goodbye arrivederci

Using a "Context Manager" (we will talk better later about this)


In [71]:
with open('examples/greetings.txt') as f:
    print(f.read())


eng it
hello ciao
goodbye arrivederci

Methods


In [72]:
print(dir(f))


['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'writelines']

In [73]:
print(f.closed)


True

Dictionaries

A dictionary is an implementation of a key-value mapping that might go by the name "hashtable" or "associative array" in other languages.

WARNING: Dictionary order is undefined and implementation-specific. It can be different across interpreters, versions, architectures, and more. Even multiple executions in the same environment.

A dictionary is not an iterable. What does it means in practice? Dictionaries cannot be used in for statements, although is possible to convert them into tuples with the items method.


In [74]:
d = {}
d


Out[74]:
{}

In [75]:
d = dict()
d


Out[75]:
{}

In [76]:
d = {'firstname': 'John', 'lastname': 'Smith'}
d


Out[76]:
{'firstname': 'John', 'lastname': 'Smith'}

The dictionary use a special kind of index operator [key]. Any immutable object can be used as a key, although typical keys are strings or integers, tuples in some cases.


In [77]:
d['lastname']


Out[77]:
'Smith'

In [78]:
d['firstname']


Out[78]:
'John'

A Dictionary is mutable! So I can modify it


In [79]:
d['firstname'] = 'Johnny'
d['lastname'] = 'Deep'
d


Out[79]:
{'firstname': 'Johnny', 'lastname': 'Deep'}

I can add keys and values.


In [80]:
import datetime
today = datetime.date.today()
d['age'] = today.year - 1963
d


Out[80]:
{'age': 56, 'firstname': 'Johnny', 'lastname': 'Deep'}
d['bankaccountpassword']

KeyError: 'bankaccountpassword'

If a key is not present a KeyError is raised. If you prefer you can "default" a value.


In [81]:
d.get('bankaccountpassword', 'UNKNOWN')


Out[81]:
'UNKNOWN'

Dictionaries performance

operation Big-O Efficiency
copy O(n)
get item O(1)
set item O(1)
delete item O(1)
contains (in) O(1)

So Dictionaries are the equivalent of a Java HashMap built into the interpreter itself.

So one can use them for tasks where insertion ordering or sorting is not important. This is the main differences with lists.

The main task of a Python programmer is to choose among the data structures that Python designers give us not reinventing the wheel when possible.

Methods


In [82]:
print(dir(d))


['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

Other data structures in standard library.

Dates


In [83]:
import datetime
today = datetime.date.today()
today


Out[83]:
datetime.date(2019, 12, 9)

In [84]:
print(dir(today))


['__add__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__radd__', '__reduce__', '__reduce_ex__', '__repr__', '__rsub__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', 'ctime', 'day', 'fromordinal', 'fromtimestamp', 'isocalendar', 'isoformat', 'isoweekday', 'max', 'min', 'month', 'replace', 'resolution', 'strftime', 'timetuple', 'today', 'toordinal', 'weekday', 'year']

In [85]:
today + datetime.timedelta(days=10)


Out[85]:
datetime.date(2019, 12, 19)

In [86]:
now = datetime.datetime.now()
now


Out[86]:
datetime.datetime(2019, 12, 9, 19, 59, 35, 454944)

In [87]:
print(dir(now))


['__add__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__radd__', '__reduce__', '__reduce_ex__', '__repr__', '__rsub__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', 'astimezone', 'combine', 'ctime', 'date', 'day', 'dst', 'fromordinal', 'fromtimestamp', 'hour', 'isocalendar', 'isoformat', 'isoweekday', 'max', 'microsecond', 'min', 'minute', 'month', 'now', 'replace', 'resolution', 'second', 'strftime', 'strptime', 'time', 'timestamp', 'timetuple', 'timetz', 'today', 'toordinal', 'tzinfo', 'tzname', 'utcfromtimestamp', 'utcnow', 'utcoffset', 'utctimetuple', 'weekday', 'year']

In [88]:
now.hour


Out[88]:
19

Heapq

heapq is a priority queue where the smallest element is always at index 0.


In [89]:
ints = list(range(10))
import random
random.shuffle(ints)
ints


Out[89]:
[8, 6, 5, 9, 1, 3, 0, 2, 4, 7]

In [90]:
import heapq
heapq.heapify(ints)
ints[0]


Out[90]:
0

In [91]:
heapq.heappop(ints)


Out[91]:
0

In [92]:
ints


Out[92]:
[1, 2, 3, 4, 6, 8, 5, 9, 7]

Counter

Counter is an handy class to count things.


In [93]:
import re
import collections
with open('examples/romeoandjuliet.txt') as will:
    words = re.findall(r'\w+', will.read().lower())
    print(collections.Counter(words).most_common(10))


[('and', 749), ('the', 684), ('i', 659), ('to', 575), ('a', 475), ('of', 395), ('my', 357), ('that', 352), ('is', 350), ('romeo', 340)]

Is useful for log file analysis isn't it?

Deque

Double linked lists.


In [94]:
import collections
def palindrome(word):
    if not word:
        return False
    if len(word) > 1:
        d = collections.deque(word.lower())
        while d:
            if d.pop() != d.popleft():
                return False
    return True

print(palindrome('Anna'))


True

In [95]:
print(palindrome('abracadabra'))


False

OrderedDict

A dictionary that remembers the insertion order.


In [96]:
import collections
d = {'CSCO': 26, 'APPL': 108, 'IBM':140, 'INTC': 29}
dv = collections.OrderedDict(sorted(d.items(), key=lambda x: -x[1]))
dv


Out[96]:
OrderedDict([('IBM', 140), ('APPL', 108), ('INTC', 29), ('CSCO', 26)])

Used with the sorted built in function we can have sorted dictionaries.

TRIVIA: sorted is Raymond Hettinger's stuff!

defaultdict

A dict with default value if a key does not exists. A default_factory function can be provided.


In [97]:
import collections
stocks = [('CSCO', 29), ('APPL', 130), ('APPL', 140),
          ('CSCO', 27), ('INTC', 28)]
dd = collections.defaultdict(list)
for stock, value in stocks:
    dd[stock].append(value)
dd.items()


Out[97]:
dict_items([('CSCO', [29, 27]), ('APPL', [130, 140]), ('INTC', [28])])

Namedtuple

A tuple where elements can be accessed by name.


In [98]:
from __future__ import print_function
import collections
records = [('CSCO', 27), ('APPL', 140), ('INTC', 28)]
stocks_nt = collections.namedtuple('stocks', ['name', 'value'])
stocks = [stocks_nt._make(record) for record in records]
for stock in stocks:
    print(stock.name, stock.value)


CSCO 27
APPL 140
INTC 28

This is Raymond Hettinger's stuff too!

Included Batteries

All the good stuff already present in the Python interpreter and Standard Library is an "Included Battery".

Examples?

csv

Comma separarated values files compatible with Excel by default.


In [99]:
import csv

with open('examples/stocks.csv') as stocks:
    for stock in csv.reader(stocks):
        name, value = stock
        print(name, value)


NAME VALUE
CSCO 27
IBM 141
APPL 139
import csv
hero_vs_villain = [('hero', 'villain'), ('spiderman', 'goblin'), ('batman', 'jocker')]
with open('examples/comics.csv', 'wb') as comics:
    comic_writer = csv.writer(comics)
    comic_writer.writerows(hero_vs_villain)

unittest

For test driven development.

# test some code

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

import unittest

class TestGreeting(unittest.TestCase):
    "Test case for 'greeting'."
    def test_greeting(self):
        "Add many tests as you want as methods."
        self.assertEqual('Hello, World', greeting('World'))

unittest.main()

Other Standard Library Goodies

OK! Is impossible to give examples of all the good stuff in Python's Standard Library.

Anyway I would like to mention:

  • sys
  • os
  • argparse/optparse
  • logging
  • itertools
  • functools
  • Network programming (socket, xmlrpc, SimpleHTTPServer, asyncio)
  • Serialization (pickle, cPickle)
  • Debugger and profiler (pbd, profile)

So, my message is: before write something on your own, check in http://www.python.org before!

Python Language

Indentation

In Python the code executed when a certain condition is met is called "block".   Each line of code in a certain scope must be indented equally and indented more than the surrounding top level scope. The standard (defined in PEP-8) is to use 4 spaces for each level of block indentation.   Statements preceding blocks generally end with a colon ":".

Because there are no semi-colons or other end-of-line indicators, breaking lines of code requires either a continuation character (\ as the last char) or for the break to occur inside an unfinished structure (such as open parentheses).

Questions?

I do really need to indent correctly the code to make it work?

Yeah!

So, there's no block signalling characters like braces and this will be for ever in this way?

from __future__ import braces

SyntaxError: not a chance

Oh, Yeah!

Python reserved keywords


In [100]:
import keyword
print(keyword.kwlist)


['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

Print

Note: is a statement in Python 2, is a function in Python 3.


In [101]:
from __future__ import print_function
print('Numbers')


Numbers

In [102]:
print('Numbers', 1, 2, 3)


Numbers 1 2 3

In [103]:
l = [1, 2, 3, 4]
print(l)


[1, 2, 3, 4]

In [104]:
print(l.__str__())


[1, 2, 3, 4]

if

if expression:
    statement(s)
elif expression:
    statement(s)
else:
    statement(s)

statement can be equal to pass.

pass means "no operation in this if branch".

Falsiness

All the following are equal to False in a if expression.

  • None
  • Zero values: 0, 0L, 0.0
  • Empty string "" or ''.
  • Empty tuple ()
  • Empty list []
  • Empty dict {}   All other cases: are True

for

for element in iterable:
    statement(s)

A break statement will exit the for cycle. A continue statement will skip all the following statements and go to next iteration.

Use iterables, not "arrays".


In [105]:
a = ['a', 'b', 'c']
for i in range(len(a)):
    print(a[i])


a
b
c

In [106]:
for c in a:
    print(c)


a
b
c

Ok, but if a really need an index?


In [107]:
# don't do this
for i in range(len(a)):
    print(i + 1, a[i])


1 a
2 b
3 c

In [108]:
for i, c in enumerate(a):
    print(i + 1, c)


1 a
2 b
3 c

Ok, what if I have two lists?


In [109]:
b = [1, 2, 3]
for c, i in zip(a, b):
    print(c, i)


a 1
b 2
c 3

Other looping idioms


In [110]:
s = 'abracadabra'
j = sorted(s)
print(''.join(j))


aaaaabbcdrr

In [111]:
j = []
for c in reversed(s):
    j.append(c)
print(''.join(j))


arbadacarba

Looping on a dictionary

The items method returns a tuple of tuples where the inner tuples are (key, value).


In [112]:
d = {'a': 1, 'b': 2, 'c': 3}
for key, value in d.items():
    print(key, value)


b 2
a 1
c 3

ATTENTION: the order is not preditable! (But you can use sorted)

List comprehensions

List comprehension is a idiom used for list transformations.

You can think at it like a more efficient for loop where you start from a list and obtain another list from the first.

Why is more efficient?

Because for loops in Python are very generic, no assumption on iterables or object in iterables is possibile. In list comprehension, instead, the iterable is always a list, so it was possible to add C code into the Interpreter to deal with this idiom.

List comprehensions syntax

new_list = [transform(x) for x in xs if predicate(x)]

In [113]:
pows = [x ** 2 for x in range(10)]
pows


Out[113]:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

In [114]:
odd_pows = [x ** 2 for x in range(10) if x % 2]
odd_pows


Out[114]:
[1, 9, 25, 49, 81]

In [115]:
even_pows = [x ** 2 for x in range(10) if not x % 2]
even_pows


Out[115]:
[0, 4, 16, 36, 64]

Donts

  • When looping on a list do not insert, append, delete items. rebinding an item at an existing index is ok.
  • When looping on a dictionary do not add or delete items. Rebinding the value of an existing key is ok.
  • When looping on a set do not add or delete items.

While

while expression:
    statement(s)

A break statement will exit the for cycle.

A continue statement will skip all the following statements and go to next iteration.

Falsiness/Truthiness is the same as if.

try/except/finally

try:
    guarded clause
except expression:
    exception handler
finally:
    clean up code

In [116]:
try:
    1/0
except ZeroDivisionError:
    print('caught')


caught

Functions

def function_name(parameters):
    "docstring" 
    statement(s)

Key concept: parameters are "passed by value" if type is immutable, by reference otherwise.

Key concept: if no return value is specified a function will return None.

Passing positional parameters


In [117]:
from __future__ import print_function
def func(i, xs):
    "i: int, xs:list."
    i = i * 3
    print('i', i)
    xs[0] = 2
    xs.append(42)
    print('xs', xs)

k = 1
ys = [1]
func(k, ys)
print('k is immutable', k)
print('ys is mutable', ys)


i 3
xs [2, 42]
k is immutable 1
ys is mutable [2, 42]

Passing parameters by name


In [118]:
func(xs=[10,20], i=3)


i 9
xs [2, 20, 42]

Default values for parameters


In [119]:
def func_with_default(xs, i=3):
    "xs:list, i:int."
    func(i, xs)
func_with_default([11, 22])


i 9
xs [2, 22, 42]

Use default values for immutable types. Is possible to use them for mutable types but the default object is created once.


In [120]:
def func_with_mutable_default(i, xs=[1]):
    "i:int, xs:list."
    func(i, xs)
func_with_mutable_default(4)
func_with_mutable_default(5)


i 12
xs [2, 42]
i 15
xs [2, 42, 42]

Arguments list and arguments map

A function can accept an argument list or an argument map.

But use them very carefully only when is strictly necessary, prefer always formal arguments like the examples showed before.


In [121]:
def arg_list_func(*args):
    for arg in args:
        print(arg)
arg_list_func(1, 'hello', [1, 2, 3])


1
hello
[1, 2, 3]

In [122]:
def kw_arg_func(**kwargs):
    for k, v in kwargs.items():
        print(k,v)
kw_arg_func(a="hello", b="there", c=1, d=[1,2])


b there
a hello
d [1, 2]
c 1

In [123]:
def kw_arg_list_func(*args, **kwargs):
    for arg in args:
        print(arg)
    for k, v in kwargs.items():
        print(k,v)
kw_arg_list_func(1,2,"bye",x=1,y=3,s="cartesian",l=[1,2])


1
2
bye
y 3
s cartesian
x 1
l [1, 2]

Return Values


In [124]:
def power2(x):
    return x ** 2
power2(3)


Out[124]:
9

In [125]:
def power22(x, y):
    return x ** 2, y ** 2
power22(3, 4)


Out[125]:
(9, 16)

Functions are "First class objects"

What this means? Basically that functions are objects, not only a place to store some code. You can pass them as parameters or assign them to new variables.


In [126]:
powerZZ = power22
powerZZ(4, 5)


Out[126]:
(16, 25)

Functional Style

Python is not a strict functional language, but from the start it adopted some "functional style". This functional style has become more prevalent in modern Python code so is necessary to look at the basic ideas.

High Order Functions and Closures

A high order function is a function that contains inner functions.


In [127]:
def make_adder(x):
    def adder(y):
        return x + y
    return adder

In [128]:
inc = make_adder(1)
inc(2)


Out[128]:
3

In [129]:
greeter = make_adder('Hello')
greeter(' Closure')


Out[129]:
'Hello Closure'

What happened?

  • Since in Python a function is a "First class object" it can be returned by a function.
  • The adder inner function can see the x variable and "close" it. The "closure" is over the x variable.
  • A "closure" is implemented in Python by means of "High Order Functions".

Next Step. Suppose we want to write a "something" able to log the argument(s) and return value(s) of a function.

This can be done with a closure.


In [130]:
def logit(func):
    def wrapper(*args, **kwargs):
        print(args, kwargs)
        return_value = func(*args, **kwargs)
        print(return_value)
        return return_value
    return wrapper

In [131]:
def power2(x):
    return x ** 2

power2 = logit(power2)
power2(5)


(5,) {}
25
Out[131]:
25

Decorators

Python's Decorators are not related to the Decorator Pattern.

It's only a way to write something like

power2 = logit(power2)

In a clearer way.


In [132]:
@logit
def power2(x):
    return x ** 2

power2(3)


(3,) {}
9
Out[132]:
9

This is the simplest way to write a Decorator, but I think you got the Idea.

Generators

The generators are functions that return values with the yield keyword.


In [133]:
def loop3():
    "A toy Generator."
    yield 1
    yield 2
    yield 3
    
for i in loop3():
    print(i)


1
2
3

In Python even the code is iterable!

A Generator is a sort of stoppable function. The context of a Generator is always saved: instruction pointer and local variables.


In [134]:
def looper(x):
    a = 0
    while a < x:
        yield a
        a += 1

for i in looper(3):
    print(i)


0
1
2

In [135]:
def fib(n):
    "Fibonacci generator."
    a, b = 0, 1
    while a < n:
        yield a
        prev = a
        a = b
        b = a + prev

for f in fib(30):
    print(f)


0
1
1
2
3
5
8
13
21

Generator have a nice property. They are like an Iterator: they are lazy. They don't create list or tuples that consume memory before the iteration

Python has built in Generators: for example enumerate or os.walk.

Generator expressions

Generator expression are a sort of "lazy" version of List Comprehensions.

They are useful to save memory when is necessary to pile up many transformation and/or filtering over an iterable.


In [136]:
ge = (x ** 2 for x in range(10))
type(ge)


Out[136]:
generator

In [137]:
odd_pows = [x for x in ge if x % 2]
odd_pows


Out[137]:
[1, 9, 25, 49, 81]

In [138]:
type(odd_pows)


Out[138]:
list

Context Managers

We saw Context Managers previouly using the with statement. This is a great way to semplify code and factor out setup and teardown methods by "implementing" the Context Manager interface: __enter__ and __exit__.


In [139]:
class Engine:
    def __enter__(self):
        print('Ignite!')
    def __exit__(*args):
        print('Turn key')
    def boost(self):
        print('Quench my thirst with gasoline')

dominic = Engine()
with dominic:
    dominic.boost()


Ignite!
Quench my thirst with gasoline
Turn key

OOP


In [140]:
class Pizza:
    "All love it."
p = Pizza
type(p)


Out[140]:
type

In Python, classes are First Class Objects.


In [141]:
po = Pizza()
type(po)


Out[141]:
__main__.Pizza

This is how to create a Pizza's instance.


In [142]:
class BioPizza(Pizza):
    "This is healty."
    gluten_free = True

BioPizza.gluten_free


Out[142]:
True

In [143]:
bp = BioPizza()
bp.gluten_free


Out[143]:
True

This is a class attribute. And see how inherit from base class.

Methods and constructor


In [144]:
class Pizza:
    def __init__(self, toppings=[]):
        self.toppings = toppings
    def get_toppings(self):
        return self.toppings

p = Pizza(['pepperoni'])
p.get_toppings()


Out[144]:
['pepperoni']
  • self is the Java this and is explicit.
  • There aren't access modifiers.
  • But, by convention, protected methods start with _, private methods start with __.

Static methods


In [145]:
class Pizza:
    healthiness = 1
    @staticmethod
    def default_healthiness():
        return Pizza.healthiness
    def __init__(self, toppings=[]):
        self.toppings = toppings
    def get_toppings(self):
        return self.toppings
    def healty_factor(self):
        return self.default_healthiness()

Pizza.default_healthiness()


Out[145]:
1

In [146]:
p = Pizza()
p.healty_factor()


Out[146]:
1

Class methods.


In [147]:
class BioPizza(Pizza):
    gluten_free = True
    @classmethod
    def is_gluten_free(cls):
        return cls.gluten_free

BioPizza.is_gluten_free()


Out[147]:
True

In [148]:
bp = BioPizza(['eggplants'])
bp.is_gluten_free()


Out[148]:
True

Not included Batteries

Add-ons for Standard Library

Tools

  • virtualenv, but also pyenv, pipenv (libraries management)
  • pylint (static checker)
  • py.test (unit test framework)

Killer Applications

  • OpenStack
  • django
  • celery
  • twisted
  • zmq
  • ...

DevOps Automation FW

  • Ansible
  • Fabric
  • RobotFramework
  • ...

Intepreters

Commercial Distributions

  • Continuum Analytics Anaconda
  • Enthought Canopy
  • ActiveState ActivePython

IDEs

  • IDLE
  • PyDev - Eclipse
  • PyCharm

Conferences

  • Pycon
  • EuroPython
  • SciPy
  • EuroSciPy

tail -f Questions