Basics
Variables and Data Types
Assignment
# Example: Variable assignment
x = 10
name = "Alice"
is_true = True
Comments
Single-line
# This is a single-line comment
Multi-line
"""
This is a multi-line comment
or a docstring for a function/class.
"""
Built-in Functions
Common Functions
print()
print("Hello, world!")
print(123)
len()
my_list = [1, 2, 3]
print(len(my_list)) # Output: 3
type()
print(type("hello")) # Output: <class 'str'>
print(type(123)) # Output: <class 'int'>
Type Conversion
int()
str_num = "123"
int_num = int(str_num)
print(int_num) # Output: 123
str()
num = 456
str_val = str(num)
print(str_val) # Output: "456"
float()
str_float = "3.14"
float_val = float(str_float)
print(float_val) # Output: 3.14
Control Flow
Conditional Statements
if
, elif
, else
x = 10
if x > 10:
print("x is greater than 10")
elif x == 10:
print("x is 10")
else:
print("x is less than 10")
Loops
for
loop
# Iterate over a list
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# Iterate using range
for i in range(5): # 0, 1, 2, 3, 4
print(i)
while
loop
count = 0
while count < 3:
print(count)
count += 1
break
and continue
# break
for i in range(5):
if i == 3:
break
print(i) # Output: 0, 1, 2
# continue
for i in range(5):
if i == 3:
continue
print(i) # Output: 0, 1, 2, 4
Functions
Defining Functions
Basic function
def greet(name):
"""This function greets the given name."""
print(f"Hello, {name}!")
greet("Alice")
Return values
def add(a, b):
return a + b
result = add(5, 3)
print(result) # Output: 8
Arguments
Default arguments
def greet_default(name="Guest"):
print(f"Hello, {name}!")
greet_default() # Output: Hello, Guest!
greet_default("Bob") # Output: Hello, Bob!
Keyword arguments
def describe_pet(animal_type, pet_name):
print(f"I have a {animal_type} named {pet_name}.")
describe_pet(animal_type="dog", pet_name="Buddy")
describe_pet(pet_name="Max", animal_type="cat")
Lists and Tuples
Lists
Creating lists
my_list = [1, 2, "hello", 4.0]
empty_list = []
Accessing elements
fruits = ["apple", "banana", "cherry"]
print(fruits) # Output: apple, banana, cherry
print(fruits[-1]) # Output: cherry (last element)
Slicing lists
numbers = [1, 2, 3, 4]
print(numbers[1:4]) # Output: [2, 3, 4]
print(numbers[:3]) # Output: [1, 2, 3]
print(numbers[2:]) # Output: [3, 4]
# slice step is `2`
print(numbers[::2]) # Output: [1, 3]
Modifying lists
my_list = []
my_list.append(4) # my_list is: [4]
my_list.insert(1, 5) # my_list is: [4, 5]
my_list.remove(2) # ValueError: list.remove(x): x not in list
popped_item = my_list.pop() # popped_item = 5
Tuples
Creating tuples
my_tuple = (1, 2, "hello")
single_item_tuple = (5,) # Comma is crucial for single item tuple
Accessing elements
colors = ("red", "green", "blue")
print(colors[0]) # Output: red
Slicing also works on Tuple.
Immutability
# Tuples cannot be modified after creation
my_tuple[0] = 10 # This would raise a TypeError: 'tuple' object does not support item assignment
Dictionaries
Creating dictionaries
Basic creation
my_dict = {"name": "Alice", "age": 30, "city": "New York"}
empty_dict = {}
Accessing elements
By key
print(my_dict["name"]) # Output: Alice
Using get()
print(my_dict.get("age")) # Output: 30
print(my_dict.get("country")) # Output: None
print(my_dict.get("country", "USA")) # Output: USA (default value)
Use indexing to retrive a key’s value will raise KeyError
while the key not exists.
Modifying dictionaries
Adding/updating
my_dict["email"] = "alice@example.com" # Add new key-value
my_dict["age"] = 31 # Update existing key
Removing elements
del my_dict["city"]
popped_value = my_dict.pop("age") # Removes 'age' and returns its value
Sets
Creating sets
Basic creation
my_set = {1, 2, 3, 3, 2} # Duplicates are ignored
print(my_set) # Output: {1, 2, 3}
empty_set = set() # Use set() for an empty set, not {}
Set operations
Union
set1 = {1, 2, 3}
set2 = {3, 4, 5}
print(set1.union(set2)) # Output: {1, 2, 3, 4, 5}
print(set1 | set2) # Output: {1, 2, 3, 4, 5}
Intersection
print(set1.intersection(set2)) # Output: {3}
print(set1 & set2) # Output: {3}
Difference
print(set1.difference(set2)) # Output: {1, 2}
print(set1 - set2) # Output: {1, 2}
Symmetric difference
print(set1.symmetric_difference(set2)) # Output: {1, 2, 4, 5}
print(set1 ^ set2) # Output: {1, 2, 4, 5}
Comprehensions
List Comprehensions
Basic usage
# Create a list of squares
squares = [x**2 for x in range(5)]
print(squares) # Output: [0, 1, 4, 9, 16]
With conditions
# List of even numbers
evens = [x for x in range(10) if x % 2 == 0]
print(evens) # Output: [0, 2, 4, 6, 8]
Dictionary Comprehensions
Basic usage
# Create a dictionary from a list of words
words = ["apple", "banana", "cherry"]
word_lengths = {word: len(word) for word in words}
print(word_lengths) # Output: {'apple': 5, 'banana': 6, 'cherry': 6}
Set Comprehensions
Basic usage
# Create a set of unique squares
unique_squares = {x**2 for x in [-1, 0, 1, 2]}
print(unique_squares) # Output: {0, 1, 4}
Manipulating Strings
Basic String Operations
Concatenation
s1 = "Hello"
s2 = "World"
full_string = s1 + " " + s2
print(full_string) # Output: "Hello World"
Repetition
print("-" * 10) # Output: "----------"
Length
print(len("Python")) # Output: 6
String Methods
upper()
, lower()
, capitalize()
, title()
s = "hello world"
print(s.upper()) # Output: "HELLO WORLD"
print(s.lower()) # Output: "hello world"
print(s.capitalize()) # Output: "Hello world"
print(s.title()) # Output: "Hello World"
strip()
, lstrip()
, rstrip()
s = " hello "
print(s.strip()) # Output: "hello"
print(s.lstrip()) # Output: "hello "
print(s.rstrip()) # Output: " hello"
split()
sentence = "Python is fun"
words = sentence.split(" ")
print(words) # Output: ['Python', 'is', 'fun']
join()
words = ["Python", "is", "awesome"]
joined_string = "-".join(words)
print(joined_string) # Output: "Python-is-awesome"
replace()
s = "I like apples"
new_s = s.replace("apples", "bananas")
print(new_s) # Output: "I like bananas"
find()
, index()
s = "hello world"
print(s.find("world")) # Output: 6 (index of 'w')
print(s.find("xyz")) # Output: -1 (not found)
# print(s.index("xyz")) # Raises ValueError if not found
startswith()
, endswith()
s = "filename.txt"
print(s.startswith("file")) # Output: True
print(s.endswith(".txt")) # Output: True
String Formatting
F-strings (Formatted String Literals)
Basic usage
name = "Bob"
age = 30
print(f"My name is {name} and I am {age} years old.")
Expressions
price = 19.99
quantity = 3
print(f"Total: ${price * quantity:.2f}") # Output: Total: $59.97
.format()
method
Positional arguments
print("My name is {} and I am {} years old.".format("Bob", 25))
Keyword arguments
print("My name is {name} and I am {age} years old.".format(name="Carol", age=28))
Old-style %
formatting
Basic usage
print("My name is %s and I am %d years old." % ("David", 35))
Regular Expressions
Common functions
re.search()
import re
text = "The quick brown fox"
match = re.search(r"quick", text)
if match:
print("Found:", match.group()) # Output: Found: quick
re.match()
# Matches only at the beginning of the string
match = re.match(r"The", text)
if match:
print("Matched at start:", match.group()) # Output: Matched at start: The
re.findall()
text = "apple banana apple cherry"
all_apples = re.findall(r"apple", text)
print(all_apples) # Output: ['apple', 'apple']
re.sub()
text = "My phone number is 123-456-7890."
new_text = re.sub(r"\d{3}-\d{3}-\d{4}", "XXX-XXX-XXXX", text)
print(new_text) # Output: My phone number is XXX-XXX-XXXX.
Metacharacters and Sequences
.
(any character except newline)
print(re.findall(r"a.c", "abc adc axc")) # Output: ['abc', 'adc', 'axc']
\d
(digit), \w
(word char), \s
(whitespace)
print(re.findall(r"\d", "abc123def")) # Output: ['1', '2', '3']
print(re.findall(r"\w", "a_b-c")) # Output: ['a', '_', 'b', 'c']
print(re.findall(r"\s", "a b c")) # Output: [' ', ' ']
Quantifiers (*
, +
, ?
, {m,n}
)
print(re.findall(r"a*", "aaaaa")) # Output: ['aaaaa', '']
print(re.findall(r"a+", "aaab")) # Output: ['aaa']
print(re.findall(r"a?", "aabc")) # Output: ['a', 'a', '', '']
print(re.findall(r"a{2,4}", "aaabbbb")) # Output: ['aaa']
Files and Directory Paths
Path manipulation
Current working directory
import os
print(os.getcwd())
Joining paths
path = os.path.join("my_folder", "sub_folder", "file.txt")
print(path) # Output: my_folder/sub_folder/file.txt (on Linux/macOS)
Getting directory/basename
path = "/home/user/document.txt"
dirname = os.path.dirname(path) # /home/user
basename = os.path.basename(path) # document.txt
print(dirname, basename)
Checking existence
print(os.path.exists("non_existent_file.txt")) # Output: False
Is file/directory
# Assuming 'my_file.txt' is a file and 'my_dir' is a directory
print(os.path.isfile("my_file.txt"))
print(os.path.isdir("my_dir"))
Directory operations
Creating directories
os.mkdir("new_directory") # Creates a single directory
os.makedirs("path/to/new_dir") # Creates all intermediate directories
Listing directory contents
print(os.listdir(".")) # List contents of current directory
Reading and Writing Files
Basic file operations
Opening files
# 'r' for read, 'w' for write (creates/overwrites), 'a' for append
# 'x' for exclusive creation, 'b' for binary, 't' for text (default)
with open("my_file.txt", "r") as f:
content = f.read()
print(content)
Reading from files
read()
with open("example.txt", "r") as f:
content = f.read() # Reads entire file
print(content)
readline()
with open("example.txt", "r") as f:
line1 = f.readline() # Reads one line
print(line1)
readlines()
with open("example.txt", "r") as f:
lines = f.readlines() # Reads all lines into a list
for line in lines:
print(line.strip()) # strip() to remove newline characters
Iterating lines
for line in open("example.txt", "r"):
print(line.strip())
Writing to files
write()
with open("output.txt", "w") as f:
f.write("Hello, Python!\n")
f.write("This is a new line.")
writelines()
lines_to_write = ["First line\n", "Second line\n"]
with open("output.txt", "w") as f:
f.writelines(lines_to_write)
JSON and YAML
JSON
Importing
import json
Encoding (Python to JSON)
data = {
"name": "Alice",
"age": 30,
"isStudent": False,
"courses": ["Math", "Science"]
}
json_string = json.dumps(data, indent=4)
print(json_string)
# Output:
# {
# "name": "Alice",
# "age": 30,
# "isStudent": false,
# "courses": [
# "Math",
# "Science"
# ]
# }
Decoding (JSON to Python)
json_data = '{"name": "Bob", "age": 25}'
python_dict = json.loads(json_data)
print(python_dict["name"]) # Output: Bob
Reading/Writing JSON files
with open("data.json", "w") as f:
json.dump(data, f, indent=4)
with open("data.json", "r") as f:
loaded_data = json.load(f)
print(loaded_data)
YAML (Requires pyyaml
library)
Importing
import yaml
Encoding (Python to YAML)
data = {"name": "Alice", "age": 30}
yaml_string = yaml.dump(data)
print(yaml_string)
Decoding (YAML to Python)
yaml_data = "name: Bob\nage: 25"
python_dict = yaml.safe_load(yaml_data)
print(python_dict["name"])
Exception Handling
try
, except
Basic handling
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero!")
Multiple exceptions
try:
# x = int("abc")
# y = 10 / 0
pass
except ValueError:
print("Invalid conversion!")
except ZeroDivisionError:
print("Cannot divide by zero!")
except Exception as e: # Catch all other exceptions
print(f"An unexpected error occurred: {e}")
else
, finally
else
block
try:
result = 10 / 2
except ZeroDivisionError:
print("Error!")
else:
print(f"Result is {result}") # Executes if no exception occurred
finally
block
try:
f = open("non_existent.txt", "r")
except FileNotFoundError:
print("File not found.")
finally:
print("This block always executes.")
# f.close() # Close file even if error occurs
Raising Exceptions
raise
statement
def check_age(age):
if age < 0:
raise ValueError("Age cannot be negative.")
print(f"Age is {age}")
# check_age(-5) # This would raise a ValueError
Debugging
pdb
(Python Debugger)
Basic usage
import pdb
def sum_numbers(a, b):
pdb.set_trace() # Set a breakpoint
result = a + b
return result
# print(sum_numbers(5, 3))
Common pdb commands
n
(next line)s
(step into function)c
(continue)p <variable>
(print variable value)q
(quit)
print()
for debugging
Simple print statements
def calculate_something(x, y):
temp = x * 10
print(f"DEBUG: temp value is {temp}") # Simple print statement
result = temp + y
return result
Args and Kwargs (*args
, **kwargs
)
Arbitrary Positional Arguments (*args
)
Usage
def sum_all(*args):
total = 0
for num in args:
total += num
return total
print(sum_all(1, 2, 3)) # Output: 6
print(sum_all(10, 20, 30, 40)) # Output: 100
*args
collects all extra positional arguments into a tuple.
Arbitrary Keyword Arguments (**kwargs
)
Usage
def show_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
show_info(name="Alice", age=30, city="New York")
# Output:
# name: Alice
# age: 30
# city: New York
**kwargs
collects all extra keyword arguments into a dictionary.
Decorators
Basic Decorator
Definition
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
# Output:
# Something is happening before the function is called.
# Hello!
# Something is happening after the function is called.
Without @
syntax
def say_hello():
print("Hello!")
say_hello = my_decorator(say_hello)
say_hello()
Decorator with arguments
Example
def repeat(num_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
return wrapper
return decorator_repeat
@repeat(num_times=3)
def greet(name):
print(f"Hello, {name}!")
greet("World")
# Output:
# Hello, World!
# Hello, World!
# Hello, World!
Context Manager
with
statement
File handling
# This ensures the file is closed automatically
with open("my_file.txt", "r") as f:
content = f.read()
print("File is now closed.")
Custom context manager (__enter__
, __exit__
)
class MyContextManager:
def __enter__(self):
print("Entering the context.")
return self # Value returned by 'as' clause
def __exit__(self, exc_type, exc_val, exc_tb):
print("Exiting the context.")
if exc_type:
print(f"An exception of type {exc_type} occurred: {exc_val}")
return False # False to propagate exception, True to suppress
with MyContextManager() as manager:
print("Inside the context.")
# 1 / 0 # Uncomment to see exception handling
# Output (without exception):
# Entering the context.
# Inside the context.
# Exiting the context.
Using contextlib
module
@contextmanager
decorator
from contextlib import contextmanager
@contextmanager
def managed_resource(name):
print(f"Acquiring resource: {name}")
yield name # This is where the 'as' value comes from
print(f"Releasing resource: {name}")
with managed_resource("database_connection") as res:
print(f"Using resource: {res}")
# Output:
# Acquiring resource: database_connection
# Using resource: database_connection
# Releasing resource: database_connection
Object-Oriented Programming (OOP)
Classes and Objects
Defining a class
class Dog:
# Class attribute
species = "Canis familiaris"
def __init__(self, name, age):
# Instance attributes
self.name = name
self.age = age
# Instance method
def bark(self):
return f"{self.name} says Woof!"
# Creating objects (instances)
my_dog = Dog("Buddy", 3)
your_dog = Dog("Lucy", 5)
print(my_dog.name) # Output: Buddy
print(my_dog.species) # Output: Canis familiaris
print(your_dog.bark()) # Output: Lucy says Woof!
Inheritance
Subclassing
class Labrador(Dog): # Labrador inherits from Dog
def __init__(self, name, age, color):
super().__init__(name, age) # Call parent constructor
self.color = color
def retrieve(self):
return f"{self.name} is retrieving the ball."
lab = Labrador("Max", 2, "golden")
print(lab.name) # Output: Max
print(lab.species) # Output: Canis familiaris (inherited)
print(lab.bark()) # Output: Max says Woof! (inherited)
print(lab.retrieve())# Output: Max is retrieving the ball.
Polymorphism
Method Overriding
class Cat(Dog): # Just for demonstration of overriding
def bark(self): # Overrides the bark method
return f"{self.name} says Meow!"
my_cat = Cat("Whiskers", 4)
print(my_cat.bark()) # Output: Whiskers says Meow!
Dataclasses
Basic Dataclass
Definition
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
# Creating instances
p1 = Point(10, 20)
p2 = Point(10, 20)
print(p1) # Output: Point(x=10, y=20) (repr automatically generated)
print(p1 == p2) # Output: True (eq automatically generated)
print(p1.x) # Output: 10
Features
Default values
@dataclass
class User:
name: str
id: int = 0 # Default value
active: bool = True
Immutability (frozen=True
)
@dataclass(frozen=True)
class ImmutablePoint:
x: int
y: int
# p = ImmutablePoint(1, 2)
# p.x = 5 # This would raise a FrozenInstanceError
setup.py
(Packaging)
Basic setup.py
structure
Example
from setuptools import setup, find_packages
setup(
name='my_package',
version='0.1.0',
packages=find_packages(), # Automatically finds packages in current dir
install_requires=[
# List your project's dependencies here
# 'requests>=2.20.0',
# 'numpy',
],
author='Your Name',
author_email='your.email@example.com',
description='A short description of my package',
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
url='https://github.com/yourusername/my_package',
classifiers=[
'Programming Language :: Python :: 3',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
],
python_requires='>=3.6',
)
Creating a distributable package
Build commands
python setup.py sdist # Source distribution
python setup.py bdist_wheel # Wheel distribution
Installing in editable mode
pip install -e . # For local development
Main: Top-Level Script
The if __name__ == "__main__":
block
Usage
# my_module.py
def main():
print("Running main function.")
def another_function():
print("Running another function.")
if __name__ == "__main__":
# This code runs only when the script is executed directly
print("This script is being run directly.")
main()
else:
# This code runs when the script is imported as a module
print("This script is being imported as a module.")
Explanation
Code inside this block only executes when the script is run directly (e.g., python my_module.py
), not when it’s imported as a module into another script. This is standard practice for separating reusable code from execution code.
Virtual Environments
venv
module
Creating an environment
python3 -m venv myenv
Activating an environment
# On macOS/Linux:
source myenv/bin/activate
# On Windows (Cmd):
myenv\Scripts\activate.bat
# On Windows (PowerShell):
myenv\Scripts\Activate.ps1
Deactivating an environment
deactivate
Installing packages
Within a virtual environment
pip install requests numpy
Generating requirements
pip freeze > requirements.txt
Installing from requirements
pip install -r requirements.txt
Standard Library
copy
Shallow copy
import copy
list1 = [1, 2]
list2 = copy.copy(list1) # Shallow copy
list1 = 99
print(list1) # Output: 99
print(list2) # Output: [1, 2] (nested list changed in both)
Deep copy
import copy
list1 = [1, 2]
list3 = copy.deepcopy(list1) # Deep copy
list1 = 88
print(list1) # Output: 88
print(list3) # Output: [1, 2] (independent)
datetime
Current date and time
from datetime import datetime, date, timedelta
now = datetime.now()
print(now) # e.g., 2023-10-27 10:30:00.123456
Specific date/time
dt = datetime(2023, 1, 1, 12, 30, 0)
print(dt) # Output: 2023-01-01 12:30:00
Formatting
print(now.strftime("%Y-%m-%d %H:%M:%S")) # e.g., 2023-10-27 10:30:00
Parsing
date_str = "2023-01-15"
parsed_date = datetime.strptime(date_str, "%Y-%m-%d")
print(parsed_date) # Output: 2023-01-15 00:00:00
Date arithmetic (timedelta
)
today = date.today()
tomorrow = today + timedelta(days=1)
last_week = today - timedelta(weeks=1)
print(today, tomorrow, last_week)
itertools
count()
from itertools import count, cycle, repeat
for i in count(10): # Prints 10, 11, 12
if i > 12:
break
print(i)
cycle()
count = 0
for item in cycle(['A', 'B', 'C']): # A, B, C, A, B, C
if count > 5:
break
print(item)
count += 1
repeat()
for item in repeat('Hello', 3): # Prints 'Hello' 3 times
print(item)
chain()
from itertools import chain
list1 = [1, 2]
list2 = ['a', 'b']
print(list(chain(list1, list2))) # Output: [1, 2, 'a', 'b']
permutations()
from itertools import permutations
print(list(permutations([1, 2, 3])))
# Output: [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]
combinations()
from itertools import combinations
print(list(combinations([1, 2, 3], 2))) # Output: [(1, 2), (1, 3), (2, 3)]
os
(covered partly in Files and Directory Paths)
Environment variables
import os
print(os.getenv("HOME")) # Get value of HOME environment variable
os.environ["MY_VAR"] = "my_value" # Set environment variable
pathlib
Basic usage
from pathlib import Path
# Current directory
current_dir = Path.cwd()
print(current_dir)
# Constructing paths
file_path = Path("my_folder") / "sub_folder" / "file.txt"
print(file_path) # Output: my_folder/sub_folder/file.txt
# Path properties
print(file_path.name) # file.txt
print(file_path.stem) # file
print(file_path.suffix) # .txt
print(file_path.parent) # my_folder/sub_folder
File system operations
# Create a new directory
Path("new_pathlib_dir").mkdir(exist_ok=True)
# Write to a file
Path("pathlib_output.txt").write_text("Hello from Pathlib!")
# Read from a file
content = Path("pathlib_output.txt").read_text()
print(content)
random
Random integer
import random
print(random.randint(1, 10)) # Inclusive: 1 to 10
Random float
print(random.random()) # Float between 0.0 and 1.0
Choice from a sequence
items = ["apple", "banana", "cherry"]
print(random.choice(items))
Shuffle a list (in-place)
my_list =
random.shuffle(my_list)
print(my_list)
Sample without replacement
print(random.sample(range(1, 100), 5)) # 5 unique numbers from 1-99
shelve
Persist Python objects
import shelve
with shelve.open('mydata') as db:
db['name'] = 'Alice'
db['age'] = 30
db['data'] = {'city': 'New York', 'zip': '10001'}
with shelve.open('mydata') as db:
print(db['name'])
print(db['data']['city'])
Note
Shelve stores Python objects using pickle
and works like a dictionary that persists to a file.
zipfile
Creating a zip file
import zipfile
# Create a dummy file to zip
with open("file_to_zip.txt", "w") as f:
f.write("This is content of the file.")
with zipfile.ZipFile('my_archive.zip', 'w') as zipf:
zipf.write('file_to_zip.txt', arcname='renamed_file.txt') # arcname for name inside zip
zipf.write('file_to_zip.txt') # Adds with original name
print("my_archive.zip created.")
Extracting from a zip file
import zipfile
with zipfile.ZipFile('my_archive.zip', 'r') as zipf:
zipf.extractall('extracted_content') # Extract all to a directory
# Or extract a specific file:
# zipf.extract('renamed_file.txt', 'single_extracted_file')
print("Content extracted.")
Listing contents
import zipfile
with zipfile.ZipFile('my_archive.zip', 'r') as zipf:
print(zipf.namelist())
# Output: ['renamed_file.txt', 'file_to_zip.txt']
References
Official resources
- Docs (docs.python.org)
Other links
- Python Cheatsheet (www.pythoncheatsheet.org)