Python Handbook
Python Handbook
Python Handbook
Preface
The Python Handbook
Conclusion
1
Preface
The Python Handbook follows the 80/20 rule: learn in 20% of the time the
80% of a topic.
Enjoy!
2
The Python Handbook
1. Introduction to Python
2. Installing Python
3. Running Python programs
4. Python 2 vs Python 3
5. The basics of working with Python
5.1. Variable
5.2. Expressions and statement
5.3. Comment
5.4. Indentation
6. Data Types
7. Operators
7.0.1. Assignment operator
7.0.2. Arithmetic operator
7.0.3. Comparison operator
7.0.4. Boolean operator
7.0.5. Bitwise operator
7.0.6. is and in
3
17. Tuples
18. Dictionaries
19. Sets
20. Functions
21. Objects
22. Loops
22.1. while loop
22.2. for loop
22.3. Break and continue
23. Classes
24. Modules
25. The Python Standard Library
26. The PEP8 Python style guide
27. Debugging
28. Variables scope
29. Accept arguments from the command line
30. Lambda Functions
31. Recursion
32. Nested Functions
33. Closures
34. Decorators
35. Docstrings
36. Introspection
37. Annotations
38. Exceptions
39. The with statement
40. Installing 3rd party packages using pip
1. Introduction to Python
4
Python is literally eating the programming world. It is growing in popularity
and usage in ways that are pretty much unprecedented in the history of
computers.
There is a huge variety of scenarios that Python excels in. Shell scripting,
task automation, Web development are just some basic examples.
Python is the language of choice for data analysis and machine learning,
but it can also adapt to create games and work with embedded devices.
Many students learn Python as their first programming language. Many are
learning it right now, many will learn it in the future. And for many of them,
Python will be the only programming language they need.
Thanks to this unique position, Python is likely going to grow even more in
the future.
The ecosystem is huge. There seems to be a library for everything you can
imagine.
5
This has pros and cons. In particular we can mention that you write
programs faster, but on the other hand you have less help from the tools to
prevent possible bugs and you will find out about some kinds of issues only
by executing the program at runtime.
Starting with Python is very easy. All you need is to install the official
package from python.org, for Windows, macOS or Linux, and you're ready to
go.
6
If you are new to programming, in the following posts I will guide you to go
from zero to becoming a Python programmer.
Lower level languages like C++ and Rust might be great for expert
programmers, but daunting to begin, and they take a long time to master.
Python, on the other hand, is a programming language for programmers, of
course, but also for the non-programmers. The students, the people doing
their day job with Excel, the scientists.
2. Installing Python
Go to https://www.python.org, choose the Downloads menu, choose your
operating system and a panel with a link to download the official package will
appear:
7
Make sure you follow the specific instructions for your operating system. On
macOS you can find a detailed guide on https://flaviocopes.com/python-
installation-macos/.
If you open your terminal and type python , you will see a screen like this:
8
This is the Python REPL (Read-Evaluate-Print-Loop)
Notice the >>> symbol, and the cursor after that. You can type any Python
code here, and press the enter key to run it.
name = "Flavio"
print(name)
9
Note: in the REPL, you can also just type name , press the enter key
and you'll get the value back. But in a program, you are not going to see
any output if you do so - you need to use print() instead.
You can access the same interactive prompt using the IDLE application that's
installed by Python automatically:
This might be more convenient for you because with the mouse you can move
around and copy/paste more easily than with the terminal.
Those are the basics that come with Python by default. However I
recommend to install IPython, probably the best command line REPL
application you can find.
Install it with
Make sure the pip binaries are in your path, then run ipython :
10
ipython is another interface to work with a Python REPL, and provides
some nice features like syntax highlighting, code completion, and much
more.
The second way to run a Python program is to write your Python program
code into a file, for example program.py :
11
Note that we save Python programs with the .py extension, that's a
convention.
In this case the program is executed as a whole, not one line at a time. And
that's typically how we run programs.
On Linux and macOS a Python program can also be transformed into a shell
script, by prepending all its content with a special line that indicates which
executable to use to run it.
12
chmod u+x program.py
./program.py
This is especially useful when you write scripts that interact with the
terminal.
One of them is using VS Code, and in particular the official Python extension
from Microsoft:
13
After installing this extension you will have Python code autocompletion and
error checking, automatic formatting and code linting with pylint , and
some special commands, including:
14
Python: Run Python File in Terminal to run the current file in the
terminal:
and many more. Just open the command palette (View -> Command Palette,
or Cmd-Shift-P) and type python to see all the Python-related commands:
15
Another way to easily run Python code is to use repl.it, a very nice website
that provides a coding environment you can create and run your apps on, in
any language, Python included:
16
Signup (it's free), then under "create a repl" click Python:
17
and you will be immediately shown an editor with a main.py file, ready to be
filled with a lot of Python code:
18
Once you have some code, click "Run" to run it on the right side of the
window:
19
I think repl.it is handy because:
4. Python 2 vs Python 3
One key topic to talk about, right from the start, is the Python 2 vs Python 3
discussion.
Python 3 was introduced in 2008, and it's been in development as the main
Python version, while Python 2 continued being maintained with bug fixes
and security patches until early 2020.
20
Many programs are still written using Python 2, and organizations still
actively work on those, because the migration to Python 3 is not trivial and
those programs would require a lot of work to upgrade those programs. And
large and important migrations always introduce new bugs.
But new code, unless you have to adhere to rules set by your organization
that forces Python 2, should always be written in Python 3.
In this example we assign a string with the value "Roger" to the name label:
name = "Roger"
age = 8
underscore character. It can't start with a number. These are all valid
variable names:
21
name1
AGE
aGE
a11111
my_name
_name
123
test!
name%
Other than that, anything is valid unless it's a Python keyword. There are
some keywords like for , if , while , import and more.
There's no need to memorize them, as Python will alert you if you use one of
those as a variable, and you will gradually recognize them as part of the
Python programming language syntax.
1 + 1
"Roger"
name = "Roger"
print(name)
22
name = "Roger"; print(name)
5.3. Comment
In a Python program, everything after a hash mark is ignored, and
considered a comment:
5.4. Indentation
Indentation in Python is meaningful.
name = "Flavio"
print(name)
In this case, if you try to run this program you would get a IndentationError:
6. Data Types
Python has several built-in types.
23
If you create the name variable assigning it the value "Roger", automatically
this variable is now representing a String data type.
name = "Roger"
You can check which type a variable is using the type() function, passing
the variable as an argument, and then comparing the result to str :
name = "Roger"
type(name) == str #True
Or using isinstance() :
name = "Roger"
isinstance(name, str) #True
Notice that to see the True value in Python, outside of a REPL, you
need to wrap this code inside print() , but for clarity reasons I avoid
using it
We used the str class here, but the same works for other data types.
First, we have numbers. Integer numbers are represented using the int
age = 1
type(age) == int #True
fraction = 0.1
type(fraction) == float #True
You saw how to create a type from a value literal, like this:
name = "Flavio"
age = 20
24
Python automatically detects the type from the value type.
You can also create a variable of a specific type by using the class constructor,
passing a value literal or a variable name:
name = str("Flavio")
anotherName = str(name)
You can also convert from one type to another by using the class constructor.
Python will try to determine the correct value, for example extracting a
number from a string:
age = int("20")
print(age) #20
fraction = 0.1
intFraction = int(fraction)
print(intFraction) #0
This is called casting. Of course this conversion might not always work
depending on the value passed. If you write test instead of 20 in the
above string, you'll get a ValueError: invalid literal for int() with base
Those are just the basics of types. We have a lot more types in Python:
and more!
25
7. Operators
Python operators are symbols that we use to run operations upon values and
variables.
assignment operator
arithmetic operators
comparison operators
logical operators
bitwise operators
age = 8
age = 8
anotherVariable = age
26
1 + 1 #2
2 - 1 #1
2 * 2 #4
4 / 2 #2
4 % 3 #1
4 ** 2 #16
4 // 2 #2
Note that you don't need a space between the operands, but it's good for
readability.
print(-4) #-4
+=
-=
*=
/=
%=
..and so on
Example:
age = 8
age += 1
# age is now 9
27
Python defines a few comparison operators:
==
!=
>
<
>=
<=
You can use those operators to get a boolean value ( True or False )
depending on the result:
a = 1
b = 2
a == b #False
a != b #True
a > b #False
a <= b #True
not
and
or
When working with True or False attributes, those work like logical AND,
OR and NOT, and are often used in the if conditional expression
evaluation:
condition1 = True
condition2 = False
28
Otherwise, pay attention to a possible source of confusion.
or used in an expression returns the value of the first operand that is not a
falsy value ( False , 0 , '' , [] ..). Otherwise it returns the last operand.
print(0 or 1) ### 1
print(False or 'hey') ### 'hey'
print('hi' or 'hey') ### 'hi'
print([] or False) ### False
print(False or []) ### []
and only evaluates the second argument if the first one is true. So if the first
argument is falsy ( False , 0 , '' , [] ..), it returns that argument.
Otherwise it evaluates the second argument:
29
Bitwise operators are rarely used, only in very specific situations, but they are
worth mentioning.
7.0.6. is and in
Let's say you have a function that compares an age variable to the 18
Instead of writing:
def is_adult(age):
if age > 18:
return True
else:
return False
def is_adult(age):
return True if age > 18 else False
First you define the result if the condition is True, then you evaluate the
condition, then you define the result if the condition is false:
30
9. Strings
A string in Python is a series of characters enclosed into quotes or double
quotes:
"Roger"
'Roger'
name = "Roger"
name = "Roger"
name += " is a good dog"
You can convert a number to a string using the str class constructor:
str(8) #"8"
A string can be multi-line when defined with a special syntax, enclosing the
string in a set of 3 quotes:
31
print("""Roger is
years old
""")
print('''
Roger is
years old
''')
32
None of those methods alter the original string. They return a new, modified
string instead. For example:
name = "Roger"
print(name.lower()) #"roger"
print(name) #"Roger"
You can use some global functions to work with strings, too.
name = "Roger"
print(len(name)) #5
name = "Roger"
print("ger" in name) #True
For example, how do you add a double quote into a string that's wrapped into
double quotes?
name = "Roger"
"Ro"Ger" will not work, as Python will think the string ends at "Ro" .
The way to go is to escape the double quote inside the string, with the \
backslash character:
name = "Ro\"ger"
This applies to single quotes too \' , and for special formatting characters
like \t for tab, \n for new line and \\ for the backslash.
33
Given a string, you can get its characters using square brackets to get a
specific item, given its index, starting from 0:
name = "Roger"
name[0] #'R'
name[1] #'o'
name[2] #'g'
name = "Roger"
name[-1] #"r"
name = "Roger"
name[0:2] #"Ro"
name[:2] #"Ro"
name[2:] #"ger"
10. Booleans
Python provides the bool type, which can have two values: True and
False (capitalized)
done = False
done = True
statements:
34
done = True
if done:
# run some code here
else:
# run some other code
When evaluating a value for True or False , if the value is not a bool we
have some rules depending on the type we're checking:
done = True
type(done) == bool #True
class:
done = True
isinstance(done, bool) #True
The global any() function is also very useful when working with booleans,
as it returns True if any of the values of the iterable (list, for example)
passed as argument are True :
book_1_read = True
book_2_read = False
The global all() function is same, but returns True if all of the values
passed to it are True :
35
ingredients_purchased = True
meal_cooked = False
11. Numbers
Numbers in Python can be of 3 types: int , float and complex .
age = 8
You can also define an integer number using the int() constructor:
age = int(8)
To check if a variable is of type int , you can use the type() global
function:
fraction = 0.1
36
fraction = float(0.1)
To check if a variable is of type float , you can use the type() global
function:
complexNumber = 2+3j
complexNumber = complex(2, 3)
Once you have a complex number, you can get its real and imaginary part:
complexNumber.real #2.0
complexNumber.imag #3.0
Again, to check if a variable is of type complex , you can use the type()
global function:
37
You can perform arithmetic operations on numbers, using the arithmetic
operators: + , - , * , / (division), % (remainder), ** (exponentiation)
and // (floor division):
1 + 1 #2
2 - 1 #1
2 * 2 #4
4 / 2 #2
4 % 3 #1
4 ** 2 #16
4 // 2 #2
+=
-=
*=
/=
%=
..and so on
age = 8
age += 1
round() given a number, returns its value rounded to the nearest integer:
round(0.12) #0
You can specify a second parameter to set the decimal points precision:
38
round(0.12, 1) #0.1
Several other math utility functions and constants are provided by the
Python standard library:
12. Constants
Python has no way to enforce a variable to be a constant.
class Constants(Enum):
WIDTH = 1024
HEIGHT = 256
Otherwise if you want to rely on naming conventions, you can adhere to this
one: declare variables that should never change uppercase:
WIDTH = 1024
No one will prevent to overwrite this value, and Python will not stop it.
39
13. Enums
Enums are readable names that are bound to a constant value.
To use enums, import Enum from the enum standard library module:
class State(Enum):
INACTIVE = 0
ACTIVE = 1
Once you do so, you can reference State.INACTIVE and State.ACTIVE , and
they serve as constants.
print(State.ACTIVE)
The same value can be reached by the number assigned in the enum:
print(State(1)) will return State.ACTIVE . Same for using the square
brackets notation State['ACTIVE'] .
len(State) # 2
40
14. User Input
In a Python command line application you can display information to the
user using the print() function:
name = "Roger"
print(name)
This approach gets input at runtime, meaning the program will stop
execution and will wait until the user types something and presses the
enter key.
You can also do more complex input processing and accept input at program
invocation time, and we'll see how to do that later on.
This works for command line applications. Other kinds of applications will
need a different way of accepting input.
41
condition = True
if condition == True:
# do something
When the condition test resolves to True , like in the above case, its block
gets executed.
What is a block? A block is that part that is indented one level (4 spaces
usually) on the right:
condition = True
if condition == True:
print("The condition")
print("was true")
The block can be formed by a single line, or multiple lines as well, and it ends
when you move back to the previous indentation level:
condition = True
if condition == True:
print("The condition")
print("was true")
In combination with if you can have an else block, that's executed if the
condition test of if results to False :
42
condition = True
if condition == True:
print("The condition")
print("was True")
else:
print("The condition")
print("was False")
And you can have different linked if checks with elif , that's executed if
the previous check was False :
condition = True
name = "Roger"
if condition == True:
print("The condition")
print("was True")
elif name == "Roger":
print("Hello Roger")
else:
print("The condition")
print("was False")
The second block in this case is executed if condition is False , and the
name variable value is "Roger".
In a if statement you can have just one if and else checks, but
multiple series of elif checks:
43
condition = True
name = "Roger"
if condition == True:
print("The condition")
print("was True")
elif name == "Roger":
print("Hello Roger")
elif name == "Syd":
print("Hello Syd")
elif name == "Flavio":
print("Hello Flavio")
else:
print("The condition")
print("was False")
if and else can also be used in an inline format, which lets us return a
value or another based on a condition.
Example:
a = 2
result = 2 if a == 0 else 3
print(result) # 3
16. Lists
Lists are an essential Python data structure.
The allow you to group together multiple values and reference them all with a
common name.
For example:
44
items = ["Roger", 1, "Syd", True]
You can check if an item is contained into a list with the in operator:
items = []
You can reference the items in a list by their index, starting from zero:
items[0] # "Roger"
items[1] # 1
items[3] # True
Using the same notation you can change the value stored at a specific index:
items[0] = "Roger"
items.index("Roger") # 0
items.index("Syd") # 2
As with strings, using a negative index will start searching from the end:
items[-1] # True
items[0:2] # ["Roger", 1]
items[2:] # ["Syd", True]
45
Get the number of items contained in a list using the len() global function,
the same we used to get the length of a string:
len(items) #4
You can add items to the list by using a list append() method:
items.append("Test")
items.extend(["Test"])
items += ["Test"]
items.remove("Test")
#or
items.extend(["Test1", "Test2"])
46
These append the item to the end of the list.
To add an item in the middle of a list, at a specific index, use the insert()
method:
items.sort()
Tip: sort() will only work if the list holds values that can be compared.
Strings and integers for example can't be compared, and you'll get an
error like TypeError: '<' not supported between instances of 'int' and
The sort() methods orders uppercase letters first, then lowercased letters.
To fix this, use:
items.sort(key=str.lower)
instead.
Sorting modifies the original list content. To avoid that, you can copy the list
content using
itemscopy = items[:]
print(sorted(items, key=str.lower))
47
that will return a new list, sorted, instead of modifying the original list.
17. Tuples
Tuples are another fundamental Python data structure.
They allow you to create immutable groups of objects. This means that once a
tuple is created, it can't be modified. You can't add or remove items.
They are created in a way similar to lists, but using parentheses instead of
square brackets:
A tuple is ordered, like a list, so you can get its values referencing an index
value:
names[0] # "Roger"
names[1] # "Syd"
items.index("Roger") # 0
items.index("Syd") # 2
As with strings and lists, using a negative index will start searching from the
end:
names[-1] # True
You can count the items in a tuple with the len() function:
len(names) # 2
48
You can check if an item is contained into a tuple with the in operator:
Get the number of items in a tuple using the len() global function, the same
we used to get the length of a string:
len(names) #2
You can create a sorted version of a tuple using the sorted() global
function:
sorted(names)
You can create a new tuple from existing tuples using the + operator:
18. Dictionaries
Dictionaries are a very important Python data structure.
While lists allow you to create collections of values, dictionaries allow you to
create collections of key / value pairs.
49
The key can be any immutable value like a string, a number or a tuple. The
value can be anything you want.
dog['name'] # 'Roger'
dog['age'] # 8
Using the same notation you can change the value stored at a specific index:
dog['name'] = 'Syd'
And another way is using the get() method, which has an option to add a
default value:
dog.get('name') # 'Roger'
dog.get('test', 'default') # 'default'
The pop() method retrieves the value of a key, and subsequently deletes the
item from the dictionary:
dog.pop('name') # 'Roger'
The popitem() method retrieves and removes the last key/value pair
inserted into the dictionary:
dog.popitem()
You can check if a key is contained into a dictionary with the in operator:
50
'name' in dog # True
Get a list with the keys in a dictionary using the keys() method, passing its
result to the list() constructor:
Get the values using the values() method, and the key/value pairs tuples
using the items() method:
print(list(dog.values()))
# ['Roger', 8]
print(list(dog.items()))
# [('name', 'Roger'), ('age', 8)]
Get a dictionary length using the len() global function, the same we used to
get the length of a string or the items in a list:
len(dog) #2
You can add a new key/value pair to the dictionary in this way:
You can remove a key/value pair from a dictionary using the del statement:
dogCopy = dog.copy()
51
19. Sets
Sets are another important Python data structure.
We can say they work like tuples, but they are not ordered, and they are
mutable. Or we can say they work like dictionaries, but they don't have keys.
Sets work well when you think about them as mathematical sets.
52
You can check if a set is a superset of another (and of course if a set is a
subset of another)
You can count the items in a set with the len() global function:
You can get a list from the items in a set by passing the set to the list()
constructor:
You can check if an item is contained into a set with the in operator:
20. Functions
A function lets us create a set of instructions that we can run when needed.
def hello():
print('Hello!')
53
This is the function definition. There is a name ( hello ) and a body, the set
of instructions, which is the part that follows the colon and it's indented one
level on the right.
To run this function, we must call it. This is the syntax to call the function:
hello()
def hello(name):
print('Hello ' + name + '!')
hello('Roger')
An argument can have a default value that's applied if the argument is not
specified:
hello()
#Hello my friend!
54
def hello(name, age):
print('Hello ' + name + ', you are ' + str(age) + ' years old!')
hello('Roger', 8)
Parameters are passed by reference. All types in Python are objects but some
of them are immutable, including integers, booleans, floats, strings, and
tuples. This means that if you pass them as parameters and you modify their
value inside the function, the new value is not reflected outside of the
function:
def change(value):
value = 2
val = 1
change(val)
print(val) #1
If you pass an object that's not immutable, and you change one of its
properties, the change will be reflected outside.
A function can return a value, using the return statement. For example in
this case we return the name parameter name:
def hello(name):
print('Hello ' + name + '!')
return name
When the function meets the return statement, the function ends.
55
def hello(name):
print('Hello ' + name + '!')
return
def hello(name):
if not name:
return
print('Hello ' + name + '!')
If we call the function passing a value that evaluates to False , like an empty
string, the function is terminated before reaching the print() statement.
def hello(name):
print('Hello ' + name + '!')
return name, 'Roger', 8
In this case calling hello('Syd') the return value is a tuple containing those
3 values: ('Syd', 'Roger', 8) .
21. Objects
Everything in Python is an object.
Even values of basic primitive types (integer, string, float..) are objects. Lists
are objects, tuples, dictionaries, everything.
Objects have attributes and methods that can be accessed using the dot
syntax.
56
age = 8
age now has access to the properties and methods defined for all int
objects.
This includes, for example, access to the real and imaginary part of that
number:
print(age.real) # 8
print(age.imag) # 0
print(age.bit_length()) #4
items = [1, 2]
items.append(3)
items.pop()
The id() global function provided by Python lets you inspect the location in
memory for a particular object.
id(age) # 140170065725376
If you assign a different value to the variable, its address will change, because
the content of the variable has been replaced with another value stored in
another location in memory:
57
age = 8
print(id(age)) # 140535918671808
age = 9
print(id(age)) # 140535918671840
But if you modify the object using its methods, the address stays the same:
items = [1, 2]
print(id(items)) # 140093713593920
items.append(3)
print(items) # [1, 2, 3]
print(id(items)) # 140093713593920
Some objects are mutable, some are immutable. This depends on the object
itself. If the object provides methods to change its content, then it's mutable.
Otherwise it's immutable. Most types defined by Python are immutable. For
example an int is immutable. There are no methods to change its value. If
you increment the value using
age = 8
age = age + 1
#or
age += 1
and you check with id(age) you will find that age points to a different
memory location. The original value has not mutated, we switched to another
value.
58
22. Loops
Loops are one essential part of programming.
condition = True
while condition == True:
print("The condition is True")
condition = True
while condition == True:
print("The condition is True")
condition = False
In this case, the first iteration is ran, as the condition test is evaluated to
True , and at the second iteration the condition test evaluates to False , so
the control goes to the next instruction, after the loop.
It's common to have a counter to stop the iteration after some number of
cycles:
59
count = 0
while count < 10:
print("The condition is True")
count = count + 1
items = [1, 2, 3, 4]
for item in items:
print(item)
Or, you can iterate a specific amount of times using the range() function:
range(4) creates a sequence that starts from 0 and contains 4 items: [0, 1,
2, 3] .
To get the index, you should wrap the sequence into the enumerate()
function:
items = [1, 2, 3, 4]
for index, item in enumerate(items):
print(index, item)
60
Both while and for loops can be interrupted inside the block, using two
special keywords: break and continue .
continue stops the current iteration and tells Python to execute the next
one.
break stops the loop altogether, and goes on with the next instruction after
the loop end.
items = [1, 2, 3, 4]
for item in items:
if item == 2:
continue
print(item)
items = [1, 2, 3, 4]
for item in items:
if item == 2:
break
print(item)
23. Classes
In addition to using the Python-provided types, we can declare our own
classes, and from classes we can instantiate objects.
class <class_name>:
# my class
61
class Dog:
# the Dog class
class Dog:
# the Dog class
def bark(self):
print('WOF!')
roger = Dog()
If you run
print(type(roger))
class Dog:
# the Dog class
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
print('WOF!')
62
We use it in this way:
roger = Dog('Roger', 8)
print(roger.name) # 'Roger'
print(roger.age) # 8
roger.bark() # 'WOF!'
class Animal:
def walk(self):
print('Walking..')
class Dog(Animal):
def bark(self):
print('WOF!')
Now creating a new object of class Dog will have the walk() method as
that's inherited from Animal :
roger = Dog()
roger.walk() # 'Walking..'
roger.bark() # 'WOF!'
24. Modules
Every Python file is a module.
You can import a module from other files, and that's the base of any program
of moderate complexity, as it promotes a sensible organization and code
reuse.
63
In the typical Python program, one file acts as the entry point. The other files
are modules and expose functions that we can call from other files.
def bark():
print('WOF!')
We can import this function from another file using import , and once we
do, we can reference the function using the dot notation, dog.bark() :
import dog
dog.bark()
Or, we can use the from .. import syntax and call the function directly:
bark()
Those modules are specific to your program, and importing depends on the
location of the file in the filesystem.
In that folder, you need to create an empty file named __init__.py . This
tells Python the folder contains modules.
Now you can choose, you can import dog from lib :
dog.bark()
64
or you can reference the dog module specific function importing from
lib.dog :
bark()
The standard library is a huge collection of all sort of utilities, ranging from
math utilities to debugging to creating graphical user interfaces.
You can find the full list of standard library modules here:
https://docs.python.org/3/library/index.html
Let's introduce how to use a module of the standard library. You already
know how to use modules you create, importing from other files in the
program folder.
Well that's the same with modules provided by the standard library:
65
import math
math.sqrt(4) # 2.0
or
sqrt(4) # 2.0
If you learn the right naming and formatting conventions right from the
start, it will be easier to read code written by other people, and people will
find your code easier to read.
Python defines its conventions in the PEP8 style guide. PEP stands for
Python Enhancement Proposals and it's the place where all Python language
enhancements and discussions happen. There are a lot of PEP proposals, all
available at https://www.python.org/dev/peps/.
PEP8 is one of the first ones, and one of the most important, too. It defines
the formatting and also some rules on how to write Python in a "pythonic"
way.
66
Indent using 4 spaces.
Python files are encoded in UTF-8
Use maximum 80 columns for your code
Write each statement on its own line
Functions, variable names and file names are lowercase, with
underscores between words (snake_case)
Class names are capitalized, separate words are written with the capital
letter too, (CamelCase)
Package names are lowercase and do not have underscores between
words
Variables that should not change (constants) are written in uppercase
Variable names should be meaningful
Add useful comments, but avoid obvious comments
Add spaces around operators
Do not use unnecessary whitespace
Add a blank line before a function
Add a blank line between methods in a class
Inside functions/methods, blank lines can be used to separate related
blocks of code to help readability
27. Debugging
Debugging is one of the best skills you can learn, as it will help you in many
difficult situations.
Every language has its debugger. Python has pdb , available through the
standard library.
breakpoint()
67
When the Python interpreter hits a breakpoint in your code, it will stop, and
it will tell you what is the next instruction it will run.
You can type the name of any variable to inspect its value.
You can press n to step to the next line in the current function. If the code
calls functions, the debugger does not get into them, and consider them
"black boxes".
You can press s to step to the next line in the current function. If the next
line is a function, the debugger goes into that, and you can then run one
instruction of that function at a time.
You can press c to continue the execution of the program normally, without
the need to do it step-by-step.
If you declare it outside of any function, the variable is visible to any code
running after the declaration, including functions:
68
age = 8
def test():
print(age)
print(age) # 8
test() # 8
def test():
age = 8
print(age)
test() # 8
print(age)
# NameError: name 'age' is not defined
python <filename>.py
You can pass additional arguments and options when you do so, like this:
69
python <filename>.py <argument1>
python <filename>.py <argument1> <argument2>
A basic way to handle those arguments is to use the sys module from the
standard library.
import sys
print(len(sys.argv))
print(sys.argv)
The sys.argv list contains as the first item the name of the file that was ran,
e.g. ['main.py'] .
This is a simple way, but you have to do a lot of work. You need to validate
arguments, make sure their type is correct, you need to print feedback to the
user if they are not using the program correctly.
import argparse
parser = argparse.ArgumentParser(
description='This program prints the name of my dogs'
)
Then you proceed to add arguments you want to accept. For example in this
program we accept a -c option to pass a color, like this: python program.py
-c red
70
import argparse
parser = argparse.ArgumentParser(
description='This program prints a color HEX value'
)
args = parser.parse_args()
print(args.color) # 'red'
You can set an option to have a specific set of values, using choices :
And there are community packages that provide this functionality, too, like
Click and Python Prompt Toolkit.
71
Lambda functions (also called anonymous functions) are tiny functions that
have no name and only have one expression as their body.
lambda a, b : a * b
Lambda functions cannot be invoked directly, but you can assign them to
variables:
multiply = lambda a, b : a * b
print(multiply(2, 2)) # 4
The utility of lambda functions comes when combined with other Python
functionality, for example in combination with map() , filter() and
reduce() .
31. Recursion
72
A function in Python can call itself. That's what recursion is. And it can be
pretty useful in many scenarios.
3! = 3 * 2 * 1 = 6
4! = 4 * 3 * 2 * 1 = 24
5! = 5 * 4 * 3 * 2 * 1 = 120
Using recursion we can write a function that calculates the factorial of any
number:
def factorial(n):
if n == 1: return 1
return n * factorial(n-1)
print(factorial(3)) # 6
print(factorial(4)) # 24
print(factorial(5)) # 120
Recursion is helpful in many places, and it helps us simplify our code when
there's no other optimal way to do it, so it's good to know this technique.
73
This is useful to create utilities that are useful to a function, but not useful
outside of it.
You might ask: why should I be "hiding" this function, if it does not harm?
One, because it's always best to hide functionality that's local to a function,
and not useful elsewhere.
Here is an example:
def talk(phrase):
def say(word):
print(word)
If you want to access a variable defined in the outer function from the inner
function, you first need to declare it as nonlocal :
def count():
count = 0
def increment():
nonlocal count
count = count + 1
print(count)
increment()
count()
33. Closures
74
If you return a nested function from a function, that nested function has
access to the variables defined in that function, even if that function is not
active any more.
def counter():
count = 0
def increment():
nonlocal count
count = count + 1
return count
return increment
increment = counter()
print(increment()) # 1
print(increment()) # 2
print(increment()) # 3
We return the increment() inner function, and that has still access to the
state of the count variable even though the counter() function has ended.
34. Decorators
Decorators are a way to change, enhance or alter in any way how a function
works.
Decorators are defined with the @ symbol followed by the decorator name,
just before the function definition.
Example:
@logtime
def hello():
print('hello!')
75
This hello function has the logtime decorator assigned.
def logtime(func):
def wrapper():
# do something before
val = func()
# do something after
return val
return wrapper
35. Docstrings
Documentation is hugely important, not just to communicate to other people
what is the goal of a function/class/method/module, but also to yourself.
When you'll come back to your code 6 or 12 months from now, you might not
remember all the knowledge you are holding in your head, and reading your
code and understanding what it is supposed to do, will be much more
difficult.
# this is a comment
The utility of docstrings is that they follow conventions and as such they can
be processed automatically.
76
def increment(n):
"""Increment a number"""
return n + 1
class Dog:
"""A class representing a dog"""
def __init__(self, name, age):
"""Initialize a new dog"""
self.name = name
self.age = age
def bark(self):
"""Let the dog bark"""
print('WOF!')
Document a module by placing a docstring at the top of the file, for example
supposing this is dog.py :
"""Dog module
This module does ... bla bla bla and provides the following classes:
- Dog
...
"""
class Dog:
"""A class representing a dog"""
def __init__(self, name, age):
"""Initialize a new dog"""
self.name = name
self.age = age
def bark(self):
"""Let the dog bark"""
print('WOF!')
77
def increment(n):
"""Increment
a number
"""
return n + 1
Python will process those and you can use the help() global function to get
the documentation for a class/method/function/module.
increment(n)
Increment
a number
There are many different standards to format docstrings, and you can choose
to adhere to your favorite one.
36. Introspection
Functions, variables and objects can be analyzed using introspection.
First, using the help() global function we can get the documentation if
provided in form of docstrings.
78
def increment(n):
return n + 1
print(increment)
or an object:
class Dog():
def bark(self):
print('WOF!')
roger = Dog()
print(roger)
print(type(increment))
# <class 'function'>
print(type(roger))
# <class '__main__.Dog'>
print(type(1))
# <class 'int'>
print(type('test'))
# <class 'str'>
The dir() global function lets us find out all the methods and attributes of
an object:
79
print(dir(roger))
The id() global function shows us the location in memory of any object:
print(id(roger)) # 140227518093024
print(id(1)) # 140227521172384
The inspect standard library module gives us more tools to get information
about objects, and you can check it out here:
https://docs.python.org/3/library/inspect.html
37. Annotations
Python is dynamically typed. We do not have to specify the type of a variable
or function parameter, or a function return value.
def increment(n):
return n + 1
80
You can also annotate variables:
count: int = 0
Python will ignore those annotations. A separate tool called mypy can be run
standalone, or integrated by IDE like VS Code or PyCharm to automatically
check for type errors statically, while you are coding, and it will help you
catch type mismatch bugs before even running the code.
A great help especially when your software becomes large and you need to
refactor your code.
38. Exceptions
It's important to have a way to handle errors.
try:
# some lines of code
If an error occurs, Python will alert you and you can determine which kind of
error occurred using a except blocks:
try:
# some lines of code
except <ERROR1>:
# handler <ERROR1>
except <ERROR2>:
# handler <ERROR2>
To catch all exceptions you can use except without any error type:
81
try:
# some lines of code
except <ERROR1>:
# handler <ERROR1>
except:
# catch all other exceptions
try:
# some lines of code
except <ERROR1>:
# handler <ERROR1>
except <ERROR2>:
# handler <ERROR2>
else:
# no exceptions were raised, the code ran successfully
A finally block lets you perform some operation in any case, regardless if
an error occurred or not
try:
# some lines of code
except <ERROR1>:
# handler <ERROR1>
except <ERROR2>:
# handler <ERROR2>
else:
# no exceptions were raised, the code ran successfully
finally:
# do something in any case
The specific error that's going to occur depends on the operation you're
performing.
For example if you are reading a file, you might get an EOFError . If you
divide a number by zero you will get a ZeroDivisionError . If you have a type
conversion issue you might get a TypeError .
82
result = 2 / 0
print(result)
and the lines of code after the error will not be executed.
Adding that operation in a try: block lets us recover gracefully and move
on with the program:
try:
result = 2 / 0
except ZeroDivisionError:
print('Cannot divide by zero!')
finally:
result = 1
print(result) # 1
You can raise exceptions in your own code, too, using the raise statement:
try:
raise Exception('An error occurred!')
except Exception as error:
print(error)
You can also define your own exception class, extending from Exception:
83
class DogNotFoundException(Exception):
pass
pass here means "nothing" and we must use it when we define a class
without methods, or a function without code, too.
try:
raise DogNotFoundException()
except DogNotFoundException:
print('Dog not found!')
For example when working with files, each time we open a file, we must
remember to close it.
Instead of writing:
filename = '/Users/flavio/test.txt'
try:
file = open(filename, 'r')
content = file.read()
print(content)
finally:
file.close()
84
filename = '/Users/flavio/test.txt'
with is not just helpful to work with files. The above example is just meant
to introduce its capabilities.
That's why individuals and companies create packages, and make them
available as open source software for the entire community.
Those modules are all collected in a single place, the Python Package
Index available at https://pypi.org, and they can be installed on your system
using pip .
There are more than 270.000 packages freely available, at the time of
writing.
You should have pip already installed if you followed the Python
installation instructions.
or, if you do have troubles, you can also run it through python -m :
85
python -m pip install <package>
For example you can install the requests package, a popular HTTP library:
and once you do, it will be available for all your Python scripts, because
packages are installed globally.
86
List comprehensions are a way to create lists in a very concise way.
numbers = [1, 2, 3, 4, 5]
You can create a new list using a list comprehension, composed by the
numbers list elements, power 2:
numbers_power_2 = []
for n in numbers:
numbers_power_2.append(n**2)
42. Polymorphism
Polymorphism generalizes a functionality so it can work on different types.
It's an important concept in object-oriented programming.
87
class Dog:
def eat():
print('Eating dog food')
class Cat:
def eat():
print('Eating cat food')
Then we can generate objects and we can call the eat() method regardless
of the class the object belongs to, and we'll get different results:
animal1 = Dog()
animal2 = Cat()
animal1.eat()
animal2.eat()
class Dog:
# the Dog class
def __init__(self, name, age):
self.name = name
self.age = age
88
roger = Dog('Roger', 8)
syd = Dog('Syd', 7)
class Dog:
# the Dog class
def __init__(self, name, age):
self.name = name
self.age = age
def __gt__(self, other):
return True if self.age > other.age else False
Now if you try running print(roger > syd) you will get the result True .
In the same way we defined __gt__() (which means greater than), we can
define the following methods:
89
__and__() respond to the & operator
__or__() respond to the | operator
__xor__() respond to the ^ operator
There are a few more methods to work with other operators, but you got the
idea.
When applications require the same module, at some point you will reach a
tricky situation where an app needs a version of a module, and another app a
different version of that same module.
in the folder where you want to start the project, or where you already have
an existing project.
Then run
source .venv/bin/activate
90
➜ folder
to
(.venv) ➜ folder
Now running pip will use this virtual environment instead of the global
environment.
91
Conclusion
Thanks a lot for reading this book.
92