Python Basics

Getting Started

Hello World

> print("Hello World")
Hello World

Get input data from console

input_string_var = input("Enter some data: ")

Comment

# Single line comments start with a number symbol.
""" Multiline strings can be written
using three "s, and are often used
as documentation.
"""

Variables and Data Types

Variables

There are no declarations, only assignments. Convention is to use lower_case_with_underscores.

some_var = 5

Data Types

Category Type
Text Type str
Numeric Types int, float, complex
Sequence Types list, tuple, range
Mapping Type dict
Set Types set, frozenset
Boolean Type bool
Binary Types bytes, bytearray, memoryview
None Type NoneType
> x = 5
> print(type(x))
<class 'int'>
Example Data Type
x = “Hello World” str
x = 20 int
x = 20.5 float
x = 1j complex
x = [“apple”, “banana”, “cherry”] list
x = (“apple”, “banana”, “cherry”) tuple
x = range(6) range
x = {“name” : “John”, “age” : 36} dict
x = {“apple”, “banana”, “cherry”} set
x = frozenset({“apple”, “banana”, “cherry”}) frozenset
x = True bool
x = b”Hello” bytes
x = bytearray(5) bytearray
x = memoryview(bytes(5)) memoryview
x = None NoneType
enumerate
seasons = ['Spring', 'Summer', 'Fall', 'Winter']
for index, ele in enumerate(seasons):
print(index, ele)

Type Conversion

str to int: int()

num: int = int("123")
print(type(num)) # <class 'int'>

int to str: str()

a: str = str(123)
print(type(a)) # <class 'str'>

String and Array

String

Strings are created with “ or ‘

str1 = "This is a string."
str2 = 'This is also a string.'

Multiple line string

str1 = """hello
world"""
print(str1)

Properties of Strings

len("This is a string")

Lookup

chatAt. the n character of the string

"hello"[0] # h

indexOf, lastIndexOf

"hello world".find("o") # 4
"hello world".rfind("o") # 7

Both index() and find() are identical in that they return the index position of the first occurrence of the substring from the main string.
The main difference is that Python find() produces -1 as output if it is unable to find the substring, whereas index() throws a ValueError exception.

String check

equals

'hello' == 'hello' # True

isEmpty

s is None or s == ''

contains

s = 'abc'
'a' in s # True
'aa' not in s # True

startsWith, endsWith

s = 'hello world'
s.startswith('hello')
s.endswith('world')

String conversion

To lowercase

"HELLO".lower()

To uppercase

"hello".upper()

String Handling

String concatenation

"Hello " + "world!"

Substring

string_val[start:end:step]
s = "abcdef"
print(s[:2]) # ab

Replace

Replace

str1 = "Hello World"
new_string = str1.replace("Hello", "Good Bye")

Replace with regex

original_string = "hello world"
replacement = '*'
new_string = re.sub(r'[aeiou]', replacement, original_string)

Trim

" abc ".strip()

Split

Split a string by delimiter: split()

print("hello world".split(" ")) # ['hello', 'world']

Split a string by regex: re.split()

import re

print(re.split(r"\s", "hello world"))

Join

Join string list

my_list = ['a', 'b', 'c', 'd']
my_string = ','.join(my_list)

String formatting

name = "Reiko"
format_str = f"She said her name is {name}."
format_str2 = f"{name} is {len(name)} characters long."
format_str = "She said her name is {}.".format("Reiko")
format_str = "She said her name is {name}.".format(name="Reiko")

Array / List

li = []
other_li = [4, 5, 6]
# Examine the length with "len()"
len(li)

Lookup

Access

# Access a list like you would any array
li[0]
# Look at the last element
li[-1]

indexOf

# Get the index of the first item found matching the argument
["a", "b", "c"].index("a") # 0

Contains

# Check for existence in a list with "in"
1 in [1,2,3] # => True

Operations

Insert / Append

# Add stuff to the end of a list with append
li.append(1)
# Insert an element at a specific index
li.insert(1, 2)

Update

li[1] = 11

Remove

# Remove from the end with pop
li.pop()
# Remove by index
del li[2] # delete the 2th element
# Remove by value
li.remove(2) # Remove first occurrence of a value

Handling

Deep copy (one layer)

li2 = li[:]

Sublist / Slice

li[start:end:step]
li[1:3]   # Return list from index 1 to 3
li[2:] # Return list starting from index 2
li[:3] # Return list from beginning until index 3
li[::2] # Return list selecting every second entry
li[::-1] # Return list in reverse order

Concatenate

li + other_li
li.extend(other_li)

Filter / Map / Reduce (sum, min, max) / Predicate (some, every)

Filter - list comprehension [x for x in X if P(f(x))] or [f(x) for x in X if P(f(x))]

list = [1, 2, 3]
new_list = [x for x in list if x > 1]
print(new_list)

Filter - lambda

list = [1, 2, 3]
filtered = filter(lambda x: x > 1, list)
for x in filtered:
print(x)

Map - list comprehension [x.field for x in S if P(x)]

list = [{"id": 1, "name": "Tom"}, {"id": 2, "name": "Jack"}]
name_list = [x['name'] for x in list]

Map - lambda

list = [{"id": 1, "name": "Tom"}, {"id": 2, "name": "Jack"}]
map(lambda x: x.name, list)

Reduce

list = [1, 2, 3, 4, 5]
sum(list)

Reduce - lambda

import functools 

list = [1, 2, 3, 4, 5]
# sum
functools.reduce(lambda a, b: a + b, list)
# min
functools.reduce(lambda a, b: a if a < b else b, list)
# max
functools.reduce(lambda a, b: a if a > b else b, list)

Predicate

predicate - some

list = [{"id": 1, "name": "Tom"}, {"id": 2, "name": "Jack"}]
bool(next((x for x in list if x['id'] == 1), None)) # True
bool(next((x for x in list if x['id'] == 3), None)) # Flase

Join

Sorting

Reversion

Deduplication

Tuple

Tuples are like lists but are immutable. You can’t insert, update, remove elements.

tup = (1, 2, 3)
# Tuples are created by default if you leave out the parentheses
tup2 = 11, 22, 33
tup[0] # => 1
tup[0] = 3 # Raises a TypeError

Access

tup[0]
len(tup)

Lookup

1 in tup  # => True
li.index("a")

Slice

tup[:2]

Concatenate

tup + (4, 5, 6) 

Unpack tuples (or lists) into variables

a, b, c = (1, 2, 3)
d, e, f = 4, 5, 6
# swap two values
e, d = d, e

Dict

empty_dict = {}
filled_dict = {"one": 1, "two": 2, "three": 3}

Note keys for dictionaries have to be immutable types. This is to ensure that the key can be converted to a constant hash value for quick look-ups. Immutable types include ints, floats, strings, tuples.

invalid_dict = {[1,2,3]: "123"}  # => Yield a TypeError: unhashable type: 'list'
valid_dict = {(1,2,3):[1,2,3]} # Values can be of any type, however.

Access

filled_dict["one"]
# Looking up a non-existing key is a KeyError
filled_dict["four"] # KeyError
# Use "get()" method to avoid the KeyError
filled_dict.get("one")
# The get method supports a default argument when the value is missing
filled_dict.get("one", 4)

Put

# Adding to a dictionary
filled_dict.update({"four":4}) # => {"one": 1, "two": 2, "three": 3, "four": 4}
filled_dict["four"] = 4 # another way to add to dict
# "setdefault()" inserts into a dictionary only if the given key isn't present
filled_dict.update({"four":4}) # => {"one": 1, "two": 2, "three": 3, "four": 4}
filled_dict["four"] = 4 # another way to add to dict

Delete

# Remove keys from a dictionary with del
del filled_dict["one"] # Removes the key "one" from filled dict

Lookup

"one" in filled_dict
list(filled_dict.keys())
list(filled_dict.values())

Get all keys as an iterable with “keys()”. We need to wrap the call in list() to turn it into a list. Note - for Python versions <3.7, dictionary key ordering is not guaranteed. Your results might not match the example below exactly. However, as of Python 3.7, dictionary items maintain the order at which they are inserted into the dictionary.

Traverse

my_dict = {"key1": "value1", "key2": "value2"}
for key in my_dict:
print(f"{key}: {my_dict[key]}")

Set

empty_set = set()
# Initialize a set with a bunch of values.
some_set = {1, 1, 2, 2, 3, 4} # some_set is now {1, 2, 3, 4}
# Similar to keys of a dictionary, elements of a set have to be immutable.
invalid_set = {[1], 1} # => Raises a TypeError: unhashable type: 'list'
valid_set = {(1,), 1}

Insert

my_set.add(5)

Delete

my_set.remove(1)

Lookup

2 in filled_set

Intersection/union/difference/subset

filled_set = {1, 2, 3, 4, 5}
other_set = {3, 4, 5, 6}
# Do set intersection with &
filled_set & other_set # => {3, 4, 5}
# Do set union with |
filled_set | other_set # => {1, 2, 3, 4, 5, 6}
# Do set difference with -
{1, 2, 3, 4} - {2, 3, 5} # => {1, 4}
# Do set symmetric difference with ^
{1, 2, 3, 4} ^ {2, 3, 5} # => {1, 4, 5}
# Check if set on the left is a superset of set on the right
{1, 2} >= {1, 2, 3} # => False
# Check if set on the left is a subset of set on the right
{1, 2} <= {1, 2, 3} # => True

Copy

# Make a one layer deep copy
filled_set = some_set.copy() # filled_set is {1, 2, 3, 4, 5}
filled_set is some_set # => False

Expressions

Arithmetic Operators

  • +: add
  • -: subtract
  • *: multiply
  • /: divide
  • //: integer division rounds down
  • %: modulo
  • **: exponentiation

Logical Operators

  • and
  • or
  • not

Note “and” and “or” are case-sensitive

Comparison operators

==, !=, >, <, >=, <=

Statements

Simple statements

Assignment

Call

return

Control Flow Statements

If Conditions

if…else

if some_var > 10:
print("some_var is totally bigger than 10.")
elif some_var < 10: # This elif clause is optional.
print("some_var is smaller than 10.")
else: # This is optional too.
print("some_var is indeed 10.")

case/switch

For loop

for

for animal in ["dog", "cat", "mouse"]:
print("{} is a mammal".format(animal))
for i, value in enumerate(["dog", "cat", "mouse"]):
print(i, value)
# "range(number)" returns an iterable of numbers from zero up to (but excluding) the given number
for i in range(4):
print(i)
# "range(lower, upper)" returns an iterable of numbers
from the lower number to the upper number
for i in range(4, 8):
print(i)
# "range(lower, upper, step)"
for i in range(4, 8, 2):
print(i)

while

x = 0
while x < 4:
print(x)
x += 1

do…while

Exception handling

# Handle exceptions with a try/except block
try:
# Use "raise" to raise an error
raise IndexError("This is an index error")
except IndexError as e:
pass # Refrain from this, provide a recovery (next example).
except (TypeError, NameError):
pass # Multiple exceptions can be processed jointly.
else: # Optional clause to the try/except block. Must follow
# all except blocks.
print("All good!") # Runs only if the code in try raises no exceptions
finally: # Execute under all circumstances
print("We can clean up resources here")

Functions

def add(x, y):
print("x is {} and y is {}".format(x, y))
return x + y

add(5, 6)

# Another way to call functions is with keyword arguments
add(y=6, x=5) # Keyword arguments can arrive in any order.
# You can define functions that take a variable number of positional arguments
def varargs(*args):
return args

varargs(1, 2, 3)
# You can define functions that take a variable number of keyword arguments, as well
def keyword_args(**kwargs):
return kwargs

keyword_args(big="foot", loch="ness")

Expand arguments

all_the_args(*args)            # equivalent: all_the_args(1, 2, 3, 4)
all_the_args(**kwargs) # equivalent: all_the_args(a=3, b=4)
all_the_args(*args, **kwargs) # equivalent: all_the_args(1, 2, 3, 4, a=3, b=4)
# global scope
x = 5

def set_global_x(num):
# global indicates that particular var lives in the global scope
global x
print(x) # => 5
x = num # global var x is now set to 6
print(x)

Nested function

def create_adder(x):
def adder(y):
return x + y
return adder

add_10 = create_adder(10)
add_10(3) # => 13

Anonymous functions

# There are also anonymous functions
(lambda x: x > 2)(3) # => True
(lambda x, y: x ** 2 + y ** 2)(2, 1) # => 5

Modules

Python modules are just ordinary Python files. You can write your own, and import them. The name of the module is the same as the name of the file.

If you have a Python script named math.py in the same folder as your current script, the file math.py will be loaded instead of the built-in Python module. This happens because the local folder has priority over Python’s built-in libraries.

# You can import modules
import math
print(math.sqrt(16)) # => 4.0

# You can get specific functions from a module
from math import ceil, floor
print(ceil(3.7)) # => 4.0
print(floor(3.7)) # => 3.0

# You can import all functions from a module.
# Warning: this is not recommended
from math import *

# You can shorten module names
import math as m
math.sqrt(16) == m.sqrt(16)

Classes

Classes

Class members

  • attribute
    • class attribute (set by class_name.class_attribute = value)
    • instance attribute (initialized by initializer)
    • instance properties (Properties are special kind of attributes which have getter, setter and delete methods like get, set and delete methods.)
  • Methods
    • initializer
    • instance method (called by instances)
    • class method (called by instances)
    • static method (called by class_name.static_method())
    • getter
    • setter

Note that the double leading and trailing underscores denote objects or attributes that are used by Python but that live in user-controlled namespaces. Methods(or objects or attributes) like: __init__, __str__, __repr__ etc. are called special methods (or sometimes called dunder methods). You should not invent such names on your own.

# We use the "class" statement to create a class
class Human:

# A class attribute. It is shared by all instances of this class
species = "H. sapiens"

# Basic initializer
def __init__(self, name):
# Assign the argument to the instance's name attribute
self.name = name

# Initialize property
self._age = 0

# An instance method. All methods take "self" as the first argument
def say(self, msg):
print("{name}: {message}".format(name=self.name, message=msg))

# Another instance method
def sing(self):
return 'yo... yo... microphone check... one two... one two...'

# A class method is shared among all instances
# They are called with the calling class as the first argument
@classmethod
def get_species(cls):
return cls.species

# A static method is called without a class or instance reference
@staticmethod
def grunt():
return "*grunt*"

# A property is just like a getter.
@property
def age(self):
return self._age

# This allows the property to be set
@age.setter
def age(self, age):
self._age = age

# This allows the property to be deleted
@age.deleter
def age(self):
del self._age
# Instantiate a class
i = Human(name="Ian")
# Call instance method
i.say("hi") # "Ian: hi"

j = Human("Joel")
j.say("hello")
# Call our class method
i.say(i.get_species()) # "Ian: H. sapiens"
# Change the class attribute (shared attribute)
Human.species = "H. neanderthalensis"
i.say(i.get_species()) # => "Ian: H. neanderthalensis"
j.say(j.get_species()) # => "Joel: H. neanderthalensis"
# Call the static method
print(Human.grunt()) # => "*grunt*"

# Static methods can be called by instances too
print(i.grunt())
# Update the property for this instance
i.age = 42
# Get the property
i.say(i.age) # => "Ian: 42"
j.say(j.age) # => "Joel: 0"
# Delete the property
del i.age
# i.age

Inheritance

# Define Batman as a child that inherits from both Superhero and Bat
class Batman(Superhero, Bat):

Standard Library

I/O Streams and Files

Read

# Instead of try/finally to cleanup resources you can use a with statement
with open("myfile.txt") as f:
for line in f:
print(line)
# Reading from a file
with open('myfile1.txt', "r+") as file:
contents = file.read() # reads a string from a file
print(contents)
with open('myfile2.txt', "r+") as file:
contents = json.load(file) # reads a json object from a file
print(contents)

Read a text file as string

content = Path('myfile.txt').read_text()

Write

# Writing to a file
contents = {"aa": 12, "bb": 21}
with open("myfile1.txt", "w+") as file:
file.write(str(contents))

Advanced Topics

Regex

Match string with pattern

import re
pattern = re.compile(r"^([A-Z][0-9]+)+$")
bool(pattern.match("A1")) # True
bool(pattern.match("a1")) # False
# or
re.match(r"^([A-Z][0-9]+)+$", "A1") # True

Find first match substrings and groups

import re
s = "A1B2"
pattern = re.compile(r"[A-Z][0-9]")
pattern.search(s).group(0) # A1
# or
re.search(r"[A-Z][0-9]", s).group(0) # A1

Find all match substrings and groups

import re
s = "A1B2"
pattern = re.compile(r"[A-Z][0-9]")
for m in pattern.finditer(s):
print(m.start(), m.end(), m.group(0))
0 2 A1
2 4 B2

Replace group

import re

def replace_group(source: str, pattern, group_to_replace: int, replacement: str):
length_adjust = 0;
result = source
for m in pattern.finditer(source):
result = replace(result, m.start(group_to_replace) + length_adjust, m.end(group_to_replace) + length_adjust,
replacement)
length_adjust = length_adjust + len(replacement) - len(m.group(group_to_replace))
return result

def replace(s, start, end, replacement):
return s[:start] + replacement + s[end:]

group_to_replace = 1;
s = "A1abc123B2"
pattern = re.compile(r"[A-Z]([0-9])")
replacement = '*'
print(replace_group(s, pattern, group_to_replace, replacement))
# A*abc123B*

Regex API

  • search(_string_[, _pos_[, _endpos_]])-> Match: checks for a match anywhere in the string
  • match(_string_[, _pos_[, _endpos_]]) -> Match: checks for a match only at the beginning of the string
  • findall(_string_[, _pos_[, _endpos_]]) -> list[string]: Return all non-overlapping matches of pattern in string, as a list of strings.
  • finditer(string[, pos[, endpos]]): Return an iterator yielding MatchObject instances over all non-overlapping matches for the RE pattern in string. The string is scanned left-to-right
  • groups([default]) -> tuple: Return a tuple containing all the subgroups of the match, from 1 up to however many groups are in the pattern.

References

[1] Learn Python in Y minutes

[2] Python Tutorial