There and Hack Again

A Python's Tail

Michael-Keith Bernard

mkbernard.dev@gmail.com

Twitter/Github/IRC: @SegFaultAX

What we'll cover

  • Why?
  • Goals
  • Basic Python
  • Discussion: Mutability & Hashability
  • Moar Basic Python
  • Control Flow
  • Functions
  • Discussion: Objects, all the way down
  • Functions, Redux
  • Fastest Intro to OOP
  • Python Builtin
  • Python Ecosystem
  • Q & A

Why?

  • Easy to learn, easy to read, easy to write
  • Fantastic teaching language
  • Clear concise syntax with low overhead/ceremony
  • Widely available on every major OS platform
  • Comprehensive tooling (editors, linting, debugging)
  • One of the most popular languages in the world!
  • "Batteries Included" standard library
  • Massive FOSS ecosystem, ~57k packages in PyPI (Maven Central ~100k, CPAN ~148k)
  • Pleasure to develop (alone or in groups)
  • The REPL! (That's Read-Eval-Print-Loop)

REPL

Read-Eval-Print-Loop is a tool common to many languages. It enables rapid prototyping and exploratory programming. This talk is entirely in REPL format!

Just type "python" at the command line to start

Goals

Get basic familiarity with Python

Syntax and Structure

Standard Library

Open Source Ecosystem

Don't try and remember everything I talk about. Just listen and let it get into your consciousness.

Goals

This is not a CS101 talk

The only way to reach mastery is by years of dedicated effort and practice. 

Don't sweat the small stuff! Don't worry about technical terms and complex-sounding words.

The XY Problem

The XY Problem

  1. I want to do X
  2. To do X I think I need to do Y
  3. I don't know how to do Y
  4. Ask how to do Y
  5. Confuse yourself and others
  6. Finally describe your original problem, X
  7. Get solution for X (probably doesn't involve Y)
  8. Move on with your life

The XY Problem

Full Name,Email,User Id
Brooke Bednar,Brenda@antoinette.com,0
Mrs. May Keeling,Penelope_McLaughlin@kari.org,1
"Dashawn Cummings, The Third",Dorothy@macey.biz,2
  1. Need to parse CSV
  2. Try splitting with "," but breaks on last line of input
  3. Try parsing comma inside " with regular expression
  4. Fail - ask for help on parsing comma in "" with re
  5. Frustrate everyone
  6. Finally mention that this is for a CSV parser
  7. import csv, n00b

The XY Problem

Avoiding the XY Problem

State the following as unambiguously as possible:

  1. What are you trying to do?
  2. What are the inputs?
  3. What are the *expected* outputs?
  4. What are the *actual* outputs?
  5. What errors are there, if any?
  6. Show some code!

Part 1: The Basics

The Basics: Numbers

>>> 1
1
>>> 1.1
1.1
>>> 123 + 456
579
>>> 2 + 4 * 8
34
>>> 2 ** 32
4294967296
>>> 2 ** 128
340282366920938463463374607431768211456L
>>> 22 / 7
3
>>> 22 / 7.0
3.142857142857143

The Basics: Booleans

>>> True
True
>>> False
False

>>> True and False
False
>>> True and True
True

>>> False or True
True
>>> False or False
False

>>> not True
False
>>> not False
True

The Basics: Booleans

>>> def test():
...     print "Got to test!"
...     return True
...

>>> True or test()
True

>>> False or test()
Got to test!
True

>>> False or test() or test()
Got to test!
True

>>> True and test() and test()
Got to test!
Got to test!
True

The Basics: Booleans

>>> bool("Hello")
True
>>> bool(123)
True
>>> bool(0)
False
>>> bool([])
False
>>> 0 or 0.0 or "" or [] or {} or () or None or False or "All of them are false-y!"
'All of them are false-y!'

The Basics: Strings

>>> "Hello, world!"
'Hello, world!'

>>> 'This is an AWESOME string!'
'This is an AWESOME string!'

>>> """This is a
... multiline "string" with quotes
... and that's totally fine
... """
"This is a\nmultiline string\nand that's totally fine\n"

>>> "I can put\nescape characters\nright in my code!"
'I can put\nescape characters\nright in my code!'

>>> u"Unicodeは素晴らしいです"
u'Unicode\u306f\u7d20\u6674\u3089\u3057\u3044\u3067\u3059'

>>> u"Unicodeは素晴らしいです".encode("utf-8")
'Unicode\xe3\x81\xaf\xe7\xb4\xa0\xe6\x99\xb4\xe3\x82\x89\xe3\x81\x97\xe3\x81\x84\xe3\x81\xa7\xe3\x81\x99'

The Basics: Strings

>>> "abc" + "123"
'abc123'
>>> ("simple " "automatic " "concatenation")
'simple automatic concatenation'

>>> ", ".join(["abc", "def", "ghi"])
'abc, def, ghi'

>>> "My name is: %s and I am %d years old" % ("Jimbo", 17)
'My name is: Jimbo and I am 17 years old'

>>> "My name is {name} and I am {age} years old".format(name="Jimbo", age=17)
'My name is Jimbo and I am 17 years old'

>>> "this is just a test".split()
['this', 'is', 'just', 'a', 'test']

>>> "nan" * 17 + " Batman!"
'nannannannannannannannannannannannannannannannannan Batman!'

The Basics: Lists

>>> [1, 2, 3, 4]
[1, 2, 3, 4]
>>> ["Sally", "Dick", "Jane"]
['Sally', 'Dick', 'Jane']

>>> [1, "John", True]
[1, 'John', True]
>>> [[1, 2], [3, 4], [5, 6]]
[[1, 2], [3, 4], [5, 6]]

>>> a = []
>>> a.append(1)
>>> a
[1]
>>> a.append(2)
>>> a
[1, 2]

>>> a + [3, 4]
[1, 2, 3, 4]
>>> a
[1, 2]
>>> a += [3, 4]
>>> a
[1, 2, 3, 4]

The Basics: Lists

>>> names = ["Tom", "Dick", "Sally", "Jane"]
>>> names.index("Sally")
2
>>> names.index("foobar")
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: 'foobar' is not in list

>>> names[2]
'Sally'

>>> names[len(names)-1]
'Jane'
>>> names[-1]
'Jane'
>>> names[-2]
'Sally'

The Basics: Lists

>>> names = ["Tom", "Dick", "Sally", "Jane"]

>>> names[0:2]
['Tom', 'Dick']
>>> names[1:2]
['Dick']
>>> names[2:2]
[]

>>> names[:2]
['Tom', 'Dick']
>>> names[2:len(names)]
['Sally', 'Jane']
>>> names[2:]
['Sally', 'Jane']

>>> names[::2]
['Tom', 'Sally']
>>> names[1::2]
['Dick', 'Jane']

The Basics: Strings (again)

>>> phrase = "I am the Real Slim Shady!"

>>> phrase[0]
'I'
>>> phrase[-1]
'!'

>>> phrase[0:4]
'I am'

>>> phrase[::2]
'Ia h elSi hd!'
>>> phrase[1::2]
' mteRa lmSay'

>>> phrase[::-1]
'!ydahS milS laeR eht ma I'

>>> [1, 2, 3, 4][::-1]
[4, 3, 2, 1]

The Basics: Dicts

>>> {}
{}

>>> {"name": "Jimbo", "age": 17}
{'age': 17, 'name': 'Jimbo'}

>>> dude = {"name": "Jimbo", "age": 17}

>>> dude["name"]
'Jimbo'
>>> dude["age"]
17

>>> dude["height"] = 183 # cm
>>> dude["height"]
183
>>> dude
{'age': 17, 'name': 'Jimbo', 'height': 183}

>>> del dude["age"]
>>> dude
{'name': 'Jimbo', 'height': 183}

The Basics: Dicts

>>> a = {}
>>> "mk" in a
False
>>> b = {"mk": 123}
>>> "mk" in b
True

>>> a["mk"]
Traceback (most recent call last):
  File "<input>", line 1, in <module>
KeyError: 'mk'

>>> a.get("mk")
>>> print a.get("mk")
None
>>> a.get("mk", 1234)
1234

>>> a = {"foo": 1, "bar": 2}
>>> b = {"bar": 3, "baz": 4}
>>> a.update(b)
>>> a
{'baz': 4, 'foo': 1, 'bar': 3}
>>> b
{'baz': 4, 'bar': 3}

Mutability & Hashability

Mutability

Python has "mutable" and "immutable" data types.

A "mutable" data type can be updated in place after it has been fully initialized. Update operations that modify the internal state of an object are said to be "mutating" the object.

An "immutable" data type cannot be modified after construction. Since these values cannot be updated directly, your only option is to generate new values by copying part or all of an existing value.

Hashability

Python uses a "hash" algorithm to speed up comparison operations.

Mutable data types do not have a stable hashing algorithm since their internal state could change therefore changing the resulting hash value.

Immutable data types never change after construction, therefore their hash value will never change. All builtin immutable data types in Python are said to be "hashable"

Hashability

Only hashable objects can be used as keys in dicts or elements of sets. For a collection type to be hashable, all elements must be hashable, recursively.

Examples of mutable data types include lists, dicts, and most kinds of user-defined objects.

Examples of immutable data types include numbers (ints, floats), strings, and tuples.

The Basics: Dicts (cont'd)

>>> d = {}
>>> d["John Jameson"] = 123
>>> d[("Foo", (1, 2))] = 456

>>> d[{"error": "incoming"}] = None
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: unhashable type: 'dict'

>>> s = set()
>>> s.add(1)
>>> s
set([1])
>>> s.add(1)
>>> s
set([1])

>>> s.add([1, 2, 3])
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: unhashable type: 'list'

The Basics: Tuples*

>>> (1, 2, 3, 4)
(1, 2, 3, 4)

>>> ("a", "b", 1, 2, 3)
('a', 'b', 1, 2, 3)

>>> a = (1, 2, 3)
>>> a[0] = 4
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

>>> {("jason", 1): 1, ("adam", 1): 1}
{('adam', 1): 1, ('jason', 1): 1}

>>> {["jason", 1]: 1, ["adam", 1]: 1}
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: unhashable type: 'list'

*Pronounced "too-pul" not "tuh-pl"

The Basics: Sets

>>> a = set([1, 1, 1, 2, 2, 3])
>>> a
set([1, 2, 3])

>>> a.add(4)
>>> a
set([1, 2, 3, 4])

>>> b = set([3, 4, 5, 6])
>>> a.union(b)
set([1, 2, 3, 4, 5, 6])

>>> a.intersection(b)
set([3, 4])

>>> a | b
set([1, 2, 3, 4, 5, 6])
>>> a & b
set([3, 4])

>>> set([1, 2, 3]).issubset(set([1, 2, 3, 4, 5]))
True

The Basics: Sets

>>> a = {1, 2, 3, 4}
>>> b = {4, 5, 6, 7}

>>> a.difference(b)
set([1, 2, 3])
>>> b.difference(a)
set([5, 6, 7])

>>> a.symmetric_difference(b)
set([1, 2, 3, 5, 6, 7])

>>> a.add(["foo", "bar"])
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: unhashable type: 'list'

>>> a.add(("foo", "bar"))
>>> a
set([('foo', 'bar'), 1, 2, 3, 4])

Note: Based on dict internally; same restrictions apply.

The Basics: Assignment

>>> n = 0

>>> a, b = 1, 2
>>> a
1
>>> b
2
>>> a, b = (3, 4)
>>> a
3
>>> b
4

>>> a, b = [5, 6]
>>> a
5
>>> b
6

>>> a, b, c = 1, 2
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: need more than 2 values to unpack

The Basics: Operators

Source: https://docs.python.org/2/library/stdtypes.html

The Basics: Operators

Source: https://docs.python.org/2/library/stdtypes.html

The Basics: Operators

Source: https://docs.python.org/2/library/stdtypes.html

The Basics: Operators

Source: https://docs.python.org/2/library/stdtypes.html

Part 2: Control Flow

Control Flow: if/elif/else

>>> a = [1, 2, 3]

>>> if 5 in a:
...     print "Yay!"

>>> if 2 in a:
...     print "Awesome!"
...
Awesome!

>>> if 5 in a:
...     print "That's my favorite number!"
... else:
...     print "Awww schucks!"
...
Awww schucks!

>>> if 5 in a:
...     print "That's my favorite number!"
... elif 4 in a:
...     print "Well, that's second best!"
... elif 3 in a:
...     print "Honestly not a fan."
... else:
...     print "Awww schucks!"
...
Honestly not a fan.

Control Flow: while

>>> n = 0
>>> while n < 5:
...     print n
...     n += 1
...
0
1
2
3
4

>>> while True:
...     # Warning, infinite loop
...     print "Python is neat!"
...
Python is neat!
Python is neat!
Python is neat!
Python is neat!
HeatDeathOfUniverseError: program terminated due to universal heat death

Control Flow: for..in

>>> for i in [1, 2, 3]:
...     print i
...
1
2
3

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> for i in range(5):
...     print i
...
0
1
2
3
4

>>> name = ["Tom", "Dick", "Jane"]
>>> for i in range(len(name)):
...     print i, name[i]
...
0 Tom
1 Dick
2 Jane

Control Flow: break

>>> for n in [1, 3, 5, 6, 7, 9]:
...     if n % 2 == 0:
...         print "found an even!", n
...         break
...     else:
...         print "odd", n
...
odd 1
odd 3
odd 5
found an even! 6

Control Flow: continue

>>> for n in [1, 2, 3, 5, 6, 7, 9]:
...     if n % 2 == 0:
...         continue
...     print "found an odd number!", n
...
found an odd number! 1
found an odd number! 3
found an odd number! 5
found an odd number! 7
found an odd number! 9

Control Flow: for..else

>>> range(2, 5)
[2, 3, 4]
>>> range(5, 5)
[]

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print n, 'equals', x, '*', n/x
...             break
...     else:
...         # loop fell through without finding a factor
...         print n, 'is a prime number'
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3

Source: https://docs.python.org/2/tutorial/controlflow.html#for-statements

Control Flow: pass

>>> for i in range(10):
...     pass
...

>>> for i in range(10):
...     if i % 2 == 0:
...         pass
...     print "num", i
...
num 0
num 1
num 2
num 3
num 4
num 5
num 6
num 7
num 8
num 9

Whitespace & pep8

(aka the great "tab vs space" debate)

Whitespace

Python code features syntactically significant whitespace. This means that code blocks are delimited by indent level rather than by delimiting characters such as { } (as in C/C++) or "being .. end".

Significant whitespace leads to cleaner code overall, and reduces block nesting ambiguity present in other languages.

Multi-line statements such a def, if, for, while, and class (among others) end in ":" followed by at least one line with an increased indent-level. pass can be used as a trivial place holder statement.

Whitespace: Example

>>> range(2, 5)
[2, 3, 4]
>>> range(5, 5)
[]

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print n, 'equals', x, '*', n/x
...             break
...     else:
...         # loop fell through without finding a factor
...         print n, 'is a prime number'
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3

Source: https://docs.python.org/2/tutorial/controlflow.html#for-statements

PEP8

Python has a formal RFC process to introduce changes into the language or ecosystem. "Python Enhancement Proposals", or PEPs, cover a huge variety of topics and proposals, each of which undergo rigorous community discussion and debate before being accepted.

PEP8 is the official Python style guide. It describes the syntactic and stylistic idioms used by the Python core team and the broader Open Source ecosystem. Most projects adhere to PEP8, and many won't accept contributions that violate PEP8 standards.

Break + Q&A

Part 3: Functions

Functions: Intro

>>> def simple():
...     print "Hello, world!"
...
>>> simple()
Hello, world!

>>> def add_one(n):
...     return n + 1
...
>>> add_one(5)
6

Functions: Intro

>>> def add(a, b):
...     return a + b
...
>>> add(1, 2)
3

>>> def square(n):
...     return n * n
...

>>> def squares(start, end):
...     for n in range(start, end):
...         print square(n)
...

>>> squares(2, 6)
4
9
16
25

Functions: Default Args

>>> def say_hello(name="Michael-Keith"):
...     print "Hello there, {}".format(name)
...
>>> say_hello()
Hello there, Michael-Keith

>>> say_hello("Bryant")
Hello there, Bryant

>>> def say_hello_a_lot(n, name="Michael-Keith"):
...     for i in range(n):
...         print "Hello there, {}".format(name)
...
>>> say_hello_a_lot(3)
Hello there, Michael-Keith
Hello there, Michael-Keith
Hello there, Michael-Keith

>>> say_hello_a_lot(3, "Bryant")
Hello there, Bryant
Hello there, Bryant
Hello there, Bryant

>>> say_hello_a_lot()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: say_hello_a_lot() takes at least 1 argument (0 given)

Functions: Keyword Args

>>> def say_hello_a_lot(n=3, name="Fess"):
...     for i in range(n):
...         print "Hello there, {}".format(name)
...
>>> say_hello_a_lot()
Hello there, Fess
Hello there, Fess
Hello there, Fess

>>> say_hello_a_lot(name="Wes")
Hello there, Wes
Hello there, Wes
Hello there, Wes

>>> say_hello_a_lot(name="Brian", n=2)
Hello there, Brian
Hello there, Brian

>>> say_hello_a_lot(4, "Ken")
Hello there, Ken
Hello there, Ken
Hello there, Ken
Hello there, Ken

Functions: *args

>>> def greet_peoples(*names):
...     for name in names:
...         print "Hello there, {}".format(name)
...
>>> greet_peoples()

>>> greet_peoples("Vic")
Hello there, Vic

>>> greet_peoples("Yuri", "Naga")
Hello there, Yuri
Hello there, Naga

>>> def greet_peoples_a_lot(n, *names):
...     for name in names:
...         for i in range(n):
...             print "Hello there, {}".format(name)
...
>>> greet_peoples_a_lot(2, "Henry", "Joseph")
Hello there, Henry
Hello there, Henry
Hello there, Joseph
Hello there, Joseph

Functions: **kwargs

>>> def kwargs_test(**kwargs):
...     return kwargs
...
>>> kwargs_test(foo=1, bar=2, baz=3)
{'baz': 3, 'foo': 1, 'bar': 2}

>>> def describe_stuff(**kwargs):
...     for k in kwargs:
...         print "Who are we kidding? {} is {}!".format(k, kwargs[k])
...
>>> describe_stuff(python="awesome", frank="Ken")
Who are we kidding? python is awesome!
Who are we kidding? frank is Ken!

Functions: call */**

>>> names = ["Jane", "Dick", "Sally"]
>>> def greet_peoples(*names):
...     for name in names:
...         print "Hello there, {}".format(name)
...
>>> greet_peoples(*names)
Hello there, Jane
Hello there, Dick
Hello there, Sally

>>> things = {"python": "awesome", "frank": "ken"}
>>> def describe_stuff(**kwargs):
...     for k in kwargs:
...         print "Who are we kidding? {} is {}!".format(k, kwargs[k])
...
>>> describe_stuff(**things)
Who are we kidding? python is awesome!
Who are we kidding? frank is ken!

Mix-and-match positional arguments, keyword arguments, *args, and **kwargs!

Everything is an... Object?

Everything is an Object

Everything in Python is an object. An object is composed of 4 ideas:

  1. An object has an identity
  2. An object has state
  3. An object has an behavior
  4. An object has a type

Everything is an Object

Identity

The identity of an object is the property that distinguishes it from other objects in the system. Identity is consistent over time, even if the object's state evolves. Many objects in the system will have distinct identities while having similar internal state.

>>> a = []
>>> b = []

>>> (id(a), id(b))
(4554702144, 4554544408)

>>> a += [1, 2, 3]
>>> b += [1, 2, 3]

>>> (id(a), id(b))
(4554702144, 4554544408)

>>> a == b
True
>>> a is b
False

Everything is an Object

State

The state of an object is defined as the contents of its attributes or fields at a point in time. Immutable objects have a fixed state for all points in time after their initialization. Mutable objects can have different state at different points in time.

>>> a = []
>>> a
[]

>>> a += [1, 2, 3]
>>> a
[1, 2, 3]

>>> a += [4, 5, 6]
>>> a
[1, 2, 3, 4, 5, 6]

Everything is an Object

Behavior

Behavior is the set of valid transformations that can be applied to an object. Some behaviors may mutate the object's internal state, while others may produce new derivative objects. Functions and methods provide the behavioral aspects of the object model.

>>> a = [1, 2, 3]
>>> len(a)
3

>>> a.append(4)
>>> a
[1, 2, 3, 4]

>>> b = {"foo": "bar"}
>>> b
{'foo': 'bar'}

>>> del b["foo"]
>>> b
{}

Everything is an Object

Type

Types define the initialization processbehavior, and state structure of an object. Types are used by the runtime to enforce invariants about the construction and usage of objects. Types can be arranged in arbitrary abstract hierarchies using inheritance.

>>> a = [1, 2 ,3]
>>> b = {"foo": "bar"}

>>> type(a)
<type 'list'>
>>> type(b)
<type 'dict'>

>>> a + [1, 2, 3]
[1, 2, 3, 1, 2, 3]

>>> a + b
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: can only concatenate list (not "dict") to list

Everything is an Object

If you forget everything the previous 5 slides said, remember only the following 3 points:

  1. Everything in Python is an Object
  2. All objects have intrinsic properties like identity, state, behavior, and a type
  3. All objects are values; all values are "first-class"*

*A value is "first-class" if it can be used like most other values in the system. In practical terms, first-class values can be assigned to variables, passed to functions, returned from functions, etc.

Examples: ints, strings, lists, and functions!

Part 4: Advanced Fucntions

Advanced Fns: Return

>>> def add(a, b):
...     return a + b
...
>>> add(1, 2)
3

>>> def add_sub(a, b):
...     return (a + b, a - b)
...
>>> add_sub(1, 2)
(3, -1)

>>> added, subbed = add_sub(1, 2)
>>> added
3
>>> subbed
-1

Advanced Fns: HOF

>>> def add(a, b):
...     return a + b
...
>>> add(1, 2)
3

>>> def apply_op(op, a, b):
...     return op(a, b)
...
>>> apply_op(add, 1, 2)
3

>>> def sub(a, b):
...     return a - b
...
>>> apply_op(sub, 1, 2)
-1

>>> def apply_many_ops(a, b, **ops):
...     for k in ops:
...         print "{} {} {} = {}".format(a, k, b, ops[k](a, b))
...
>>> apply_many_ops(1, 2, plus=add, subtract=sub)
1 subtract 2 = -1
1 plus 2 = 3

Advanced Fns: HOF

Higher-order functions

A higher-order function is a function that operates on other functions in one or both of the following ways:

  1. It accepts a function as input
  2. It returns a function as a result

apply_op in the previous example was a higher-order function because it accepted a function (the operator to be applied) as an argument along with a and b.

>>> def add(a, b):
...     return a + b
...
>>> def apply_op(op, a, b):
...     return op(a, b)
...
>>> apply_op(add, 1, 2)
3

Advanced Fns: Lambda

>>> def add(a, b):
...     return a + b
...
>>> add(1, 2)
3

>>> add = lambda a, b: a + b
>>> add(1, 2)
3

>>> def apply_op(op, a, b):
...     return op(a, b)
...
>>> apply_op(lambda x, y: x + y, 10, 20)
30

Advanced Fns: Decorator

>>> def add(a, b):
...     return a + b
...

>>> def print_arguments(fn):
...     def _wrapper(*args, **kwargs):
...         res = fn(*args, **kwargs)
...         print "Called {} with args {} and kwargs {}. Returned {}".format(
...             fn.func_name, args, kwargs, res)
...         return res
...     return _wrapper
...
>>> print_arguments(add)
<function _wrapper at 0x10f7bd0c8>

>>> wrapped_add = print_arguments(add)
>>> wrapped_add(1, 2)
Called add with args (1, 2) and kwargs {}. Returned 3
3
>>> add(1, 2)
3

Advanced Fns: Decorator

>>> def print_arguments(fn):
...     def _wrapper(*args, **kwargs):
...         res = fn(*args, **kwargs)
...         print "Called {} with args {} and kwargs {}. Returned {}".format(
...             fn.func_name, args, kwargs, res)
...         return res
...     return _wrapper
...
>>> @print_arguments
... def add(a, b):
...     return a + b
...
>>> add(1, 2)
Called add with args (1, 2) and kwargs {}. Returned 3
3

# @foo
# @bar
# def my_fn():
#     pass
#
# is equivalent to
#
# def my_fn():
#     pass
# my_fn = foo(bar(my_fn))

Advanced Fns: Generator

>>> def nat():
...     nums = []
...     num = 0
...     while True:
...         nums.append(num)
...         num += 1
...     return nums
...
>>> nat()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 5, in nat
KeyboardInterrupt

>>> def nat():
...     n = 0
...     while True:
...         yield n
...         n += 1
...
>>> nums = nat()
>>> next(nums)
0
>>> next(nums)
1
>>> next(nums)
2

Break + Q&A

Part 5: Advanced Looping

Looping: Comprehensions

>>> nums = [1, 2, 3, 4]
>>> def inc_nums(nums):
...     res = []
...     for num in nums:
...         res.append(num + 1)
...     return res
...
>>> inc_nums(nums)
[2, 3, 4, 5]

>>> [n + 1 for n in nums]
[2, 3, 4, 5]

>>> squares = [n ** 2 for n in range(10)]
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Looping: Comprehensions

>>> nums = [1, 2, 3]

>>> def pairs(l1, l2):
...     res = []
...     for e1 in l1:
...         for e2 in l2:
...             res.append((e1, e2))
...     return res
...
>>> pairs(nums, nums)
[(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3)]

>>> [(e1, e2) for e1 in nums for e2 in nums]
[(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3)]

>>> [(e1, e2) for e1 in nums for e2 in nums if e1 != e2]
[(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]

Looping: range/enumerate

>>> names = ["Tom", "Dick", "Sally"]
>>> for i in range(len(names)):
...     print i, names[i]
...
0 Tom
1 Dick
2 Sally

>>> for i, name in enumerate(names):
...     print i, name
...
0 Tom
1 Dick
2 Sally

Looping: dict.iteritems

>>> d = {"foo": "bar", "spam": "eggs"}
>>> for k in d:
...     print "k: {}, v: {}".format(k, d[k])
...
k: foo, v: bar
k: spam, v: eggs

>>> for k, v in d.iteritems():
...     print "k: {}, v: {}".format(k, d[k])
...
k: foo, v: bar
k: spam, v: eggs

Looping: dict.iteritems

>>> d = {"foo": "bar", "spam": "eggs"}
>>> for k in d:
...     print "k: {}, v: {}".format(k, d[k])
...
k: foo, v: bar
k: spam, v: eggs

>>> for k, v in d.iteritems():
...     print "k: {}, v: {}".format(k, d[k])
...
k: foo, v: bar
k: spam, v: eggs

Part 6: Exceptions

Exceptions: try..except

>>> 1 / 0
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

>>> try:
...     1 / 0
... except:
...     print "Mistakes were made"
...
Mistakes were made

>>> def blow_up():
...     1 / 0
...
>>> blow_up()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 2, in blow_up
ZeroDivisionError: integer division or modulo by zero

Exceptions: Matching

>>> try:
...     1 / 0
... except ZeroDivisionError:
...     print "Thats not even a number!"
...
Thats not even a number!

>>> try:
...     1 / 0
... except KeyError:
...     print "This won't be executed"
...
Traceback (most recent call last):
  File "<input>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

>>> try:
...     1 / 0
... except KeyError:
...     print "How would you get here?"
... except ZeroDivisionError:
...     print "Fix your math!"
...
Fix your math!

Exceptions: Exc Object

>>> try:
...     1 / 0
... except ZeroDivisionError as exc:
...     print "I blew up because of {}".format(exc)
...
I blew up because of integer division or modulo by zero

>>> try:
...     1 / 0
... except Exception as e:
...     print "Failed because of {}".format(e)
...
Failed because of integer division or modulo by zero

# Note: ZeroDivisionError is a subclass of Exception (as are all exceptions)

Exceptions: try..finally

>>> try:
...     1 / 0
... except:
...     print "I died"
... finally:
...     print "Run me no matter what"
...
I died
Run me no matter what

>>> try:
...     1 + 1
... except:
...     print "I died"
... finally:
...     print "Run me no matter what"
...
2
Run me no matter what

Exceptions: [re-]raise

>>> try:
...     1 / 0
... except:
...     print "Thing have gone poorly"
...     raise
...
Thing have gone poorly
Traceback (most recent call last):
  File "<input>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

>>> try:
...     raise ValueError("This is not the right thing")
... except:
...     print "Try to recover!"
...
Try to recover!

Part 7: Object Oriented Programming

OOP

Python borrows the core of its Object Oriented Programming model from other classically OOP languages like Java and C++.

 

Warning: Object Oriented Programming is a complex topic that warrants a talk of its own. OOP is rarely worth the added mental and technical complexity for new programmers.

 

If you're generally new to programming, skip OOP until you feel more comfortable with language fundamentals.

OOP

>>> class User(object):
...     def __init__(self, first_name, last_name, email):
...         self.first_name = first_name
...         self.last_name = last_name
...         self.email = email
...
...     def say_hello(self):
...         print "{} says hello!".format(self.first_name)
...
...     @property
...     def full_name(self):
...         return " ".join([self.first_name, self.last_name])
...
>>> u = User("John", "Smith", "jsmith@example.com")
>>> u.first_name
'John'
>>> u.last_name
'Smith'
>>> u.email
'jsmith@example.com'
>>> u.full_name
'John Smith'
>>> u.say_hello()
John says hello!
>>> type(u)
<class '__main__.User'>

Part 8: Builtin and Standard Libary

Interesting Builtins

>>> abs(1)
1
>>> abs(-1)
1

>>> nums = [1, 3, 5, 6]
>>> any(e % 2 == 0 for e in nums)
True
>>> all(e % 2 == 0 for e in nums)
False

>>> int("1337")
1337
>>> float("1337")
1337.0
>>> float("1337.1337")
1337.1337

>>> max(nums)
6
>>> max(["Tom", "Jane", "Albert"], key=len)
'Albert'
>>> min(["Tom", "Jane", "Albert"], key=len)
'Tom'

Interesting Builtins


>>> nums = range(5)
>>> nums
[0, 1, 2, 3, 4]

>>> sorted(nums)
[0, 1, 2, 3, 4]
>>> sorted(nums, reverse=True)
[4, 3, 2, 1, 0]

>>> sum(nums)
10

>>> names = ["Tom", "Jane", "Albert"]
>>> reversed(names)
<listreverseiterator object at 0x10880d410>
>>> list(reversed(names))
['Albert', 'Jane', 'Tom']
>>> names[::-1]
['Albert', 'Jane', 'Tom']

>>> zip(names, nums)
[('Tom', 0), ('Jane', 1), ('Albert', 2)]
>>> dict(zip(names, nums))
{'Jane': 1, 'Albert': 2, 'Tom': 0}

"Batteries Included"

Python has a massive standard library that has tools for virtually every common workload. There are over 287 modules in the standard library as of Python 2.7.9.

A selection of interesting modules in the standard lib:

  • argparse - Unix-style command line argument parser
  • csv, json - codecs for encoding and decoding common serializations
  • datetime - advanced time parsing, formatting, and arithmetic
  • urllib, urlparse - fully featured http client and URL/URI processing
  • re - PCRE-style regular expression library
  • glob, sys, subprocess - cross-platform tools for interacting with the system
  • random, math - secure RNG and advanced arithmetic
  • hashlib, uuid - cryptographic hashing algorithms and UUID generators
  • multiprocessing - parallel computation management (via processes)

Python Ecosystem

Python is one of the most popular languages in the world. Its wealth of Free and Open Source code is what makes it so attractive. Libraries you should check out:

  • requests - the best HTTP client for Python
  • Ansible - dead-simple orchestration, great for small to medium numbers of hosts
  • Fabric - excellent ssh/paramiko-based orchestration utility and DSL
  • Flask - fantastic micro-framework for building simple web applications
  • SQLAlchemy - general purpose ORM with (possibly) the least brain dead API I've seen for an ORM
  • Django - if full-stack frameworks are your thing

Python Caveats

Python has made many trade-offs, some of which are more important than others, but they're worth noting:

  • Python is (comparatively) slow. If absolute performance if a requirement, prefer Java/C/C++/etc.
  • Project management is done using virtualenv (for version management) and pip (for package management). There are no all-in-one solutions like bundler, gradle, maven, etc.
  • CPython (the reference implementation) and most other Python implementations lack parallelism due to the global interpreter lock. There is no workaround for this, parallelism is achieved via multiprocessing instead.
  • Setuptools (the main distribution library for Python) is highly complex but also extremely customizable. Read the docs carefully if you intend on releasing your own packages publicly.

Python 2.x vs 3.x

Python 3 has been in development since 2008. That last official version of Python 2 is 2.7. No official feature development will be done on 2.x apart from bug fixes and security patches. This decision was made to help increase adoption for Python 3.x

Adoption for 3.x has been very slow due to the severe backwards incompatibilities it introduces. As a result, the vast majority of libraries are still written for 2.x first, then ported to 3.x. 2.x is the most popular version of Python today.

Python 2.x vs 3.x

Is Python 3.x the right choice for me?

  1. Do all the libraries you're likely to need support Python 3 natively?
  2. If the answer to #1 is no, are you willing to personally contribute time to port them to 3.x?
  3. If the answer to #2 is yes, are you willing to maintain those libraries indefinitely, or help the current maintainer integrate your changes safely?

For many workloads, Python 2.x is still the best option. Especially for scripting or ops-related work, I suggest Python 2.x

Next Steps

Hopefully you now have an overview of what Python is capable of, and enough knowledge to be able to read and write basic applications.

Additional Resources:

  1. Official Python Tutorial: https://docs.python.org/2/tutorial/
  2. Learn Python the Hard Way: http://learnpythonthehardway.org/book/
  3. Code Academy - http://www.codecademy.com/en/tracks/python
  4. IRC: #python irc.freenode.net
  5. Mailing Lists: https://mail.python.org/mailman/listinfo

El Fin.

Thanks for listening! Questions?

There and Hack Again: A Python's Tail

By Michael-Keith Bernard

There and Hack Again: A Python's Tail

A quick tutorial on Python for the (mostly) non-programmer

  • 1,885