Skip to content


python is an interpreted language, that looks a lot like pseudocode.


The formal definition via BNF grammar is the following (from the official documentation)

compound_stmt ::=  if_stmt
                   | while_stmt
                   | for_stmt
                   | try_stmt
                   | with_stmt
                   | match_stmt
                   | funcdef
                   | classdef
                   | async_with_stmt
                   | async_for_stmt
                   | async_funcdef
suite         ::=  stmt_list NEWLINE | NEWLINE INDENT statement+ DEDENT
statement     ::=  stmt_list NEWLINE | compound_stmt
stmt_list     ::=  simple_stmt (";" simple_stmt)* [";"]

that in practice means that a compound statement is composed of a header starting with a specific keyword and ending with a colon, followed by a line of statements separated by semicolons or a list of statements indented with respect the correspective header. These two last cases form a suite.

Data Types

The standard type hierarchy

Name Description
None particular type used as a null that is only representative of its type
numbers.Number representation of numerical entities

container here means that it's not limited to a single data type but can have mixed types together. The opposite of container is flat.

hashable: object that has a hash value which never changes during its lifetime

Name Description Mutable Container
int, float and complex unlimited precision numerical types
range sequence of numbers
tuple built-in sequence
list built-in sequence
str text sequence
bytes bytes sequence
bytearray bytes sequence
set unordered collection of hashable objects
frozenset unordered collection of hashable objects that is hashable
dict mapping from hashable objects to an arbitrary object
memoryview view to internal data of objects

From a practical point of view, see here the time complexity associated with the data types.

Sequences/containers can implement a particular sub-type that is the iterator: in practice you tell the external world that your object supports iteration via the __iter__() method that returns the actual iterator.

The iterator must implement the __next__() method that returns the next element in the sequence. When the sequence has not more element, this method must raise StopIteration.

Related to this exists the generator type, roughly speaking a function that using the yield keyword allows to build an iterator. Take in mind that has other methods other the ones from the iterator protocols, like send(), throw() and close().

For example, directly from the documentation

>>> def echo(value=None):
...     print("Execution starts when 'next()' is called for the first time.")
...     try:
...         while True:
...             try:
...                 value = (yield value)
...             except Exception as e:
...                 value = e
...     finally:
...         print("Don't forget to clean up when 'close()' is called.")
>>> generator = echo(1)
>>> print(next(generator))
Execution starts when 'next()' is called for the first time.
>>> print(next(generator))
>>> print(generator.send(2))
>>> generator.throw(TypeError, "spam")
>>> generator.close()
Don't forget to clean up when 'close()' is called.

It exists also a generator expression

>>> sum(i*i for i in range(10))         # sum of squares 0, 1, 4, ... 81


A particular to keep in mind when interacting with dictionary is that the objects returned by dict.keys(), dict.values() and dict.items() are view objects. They provide a dynamic view on the dictionary’s entries, which means that when the dictionary changes, the view reflects these changes.


Classes are not "subclasses" of type but instances of it

Name Description
__bytes__() Called by bytes to compute a byte-string representation of an object. This should return a bytes object.
__format__() Called by the format() built-in function, and by extension, evaluation of formatted string literals and the str.format() method, to produce a “formatted” string representation of an object.
__copy__() Used to define the implementation of a copy used by the copy module

Take in mind that exists two convention for internal attributes on an object

  • if the name starts with _ is considered "internal"
  • if the name starts with __ is considered "private" but also the interpreter mangles the name so that __<name> becomes _<class name>__<name>

Structural pattern matching

Introduced in python 3.10 via PEP-638 PEP-636 and PEP-634

match <expression>:
    case <pattern> [guard]:

where <expression> is whatever python expression returns something that might match with the <pattern> and optionally must "pass" the guard expression.

The simplest match is the "literal" matching, where you are trying to match a constant, a value; a more complex pattern matching is one that cause name bindings. When you use an identifier as pattern then on matching the value will be bounded to that name for the scope of the subsequent block.

Note: if you want to use a value coming from an attribute, to avoid the name binding you need to use a qualified name (an unqualified name is a name without dots).

Note: there is difference between (<pattern>) and [<pattern>] or (<pattern>,). The first is a group pattern, the second a sequence pattern.

Here some practical examples

    match op.opcode, *args:
        case Operatore.Store, Constant() as offset, Click(offset=(x, y)):

Operatore.Store is matching with a literal, Constant() is matching with a type and binding the parameter is matching with the name offset; the last one looks for an element of type Click that has a tuple of two elements associated with the attribute offset and binds this two elements to the name x and y.


Introduced with PEP 492, the syntax as indicated from the official documentation

async_funcdef ::=  [decorators] "async" "def" funcname "(" [parameter_list] ")"
                   ["->" expression] ":" suite
async_for_stmt ::=  "async" for_stmt
async_with_stmt ::=  "async" with_stmt

In the following code

async def read_data(db):
    data = await db.fetch('SELECT ...')

await suspends execution like yield from; it accepts only an "awaitable" (raises a TypeError doing otherwise)


  • Value error Attempted relative import in non-package


For python3.7+, you can indicate that the function returns an istance of the enclosing class

from __future__ import annotations

class Position:
    def __add__(self, other: Position) -> Position:


Metaclasses and introspection



@pytest.mark.parametrize('count', [
    0, 1, 6, 17,
def test_tree42(count):
    values = list(range(count))

    bt = XBinarySearchTree.from_array(values)

    assert list(bt.inorder_traversal()) == values
def test_myoutput(capsys):  # or use "capfd" for fd-level
    captured = capsys.readouterr()
    assert captured.out == "hello\n"
    assert captured.err == "world\n"
    captured = capsys.readouterr()
    assert captured.out == "next\n"
@pytest.mark.skip(reason="no way of currently testing this")
def test_the_unknown():


  • PEP8: Style Guide for Python Code
  • Design pattern in python
  • dict() vs {} (hint: {} is better)
  • Things you didn't know about Python: interesting presentation about Python internal and stuff.
  • Copying list, the right way
  • Make one archive python executable
  • HOWTO Create Python GUIs using HTML
  • Slides about functional versus imperative programming
  • MRO: from official documentation and a post about multiple inheritance (look at also the comments)
  • Lazy evaluation
  • Python’s super() considered super!
  • format()



From the official documentation

try_stmt  ::=  try1_stmt | try2_stmt | try3_stmt
try1_stmt ::=  "try" ":" suite
               ("except" [expression ["as" identifier]] ":" suite)+
               ["else" ":" suite]
               ["finally" ":" suite]
try2_stmt ::=  "try" ":" suite
               ("except" "*" expression ["as" identifier] ":" suite)+
               ["else" ":" suite]
               ["finally" ":" suite]
try3_stmt ::=  "try" ":" suite
               "finally" ":" suite

The optional else clause is executed if the control flow leaves the try suite, no exception was raised, and no return, continue, or break statement was executed. Exceptions in the else clause are not handled by the preceding except clauses.

The finally clause is always executed, also in case the try has a return, break or continue and since the last return is what counts in a function, a return in the finally superseed the previous encountered one.


  • Docopts command line arguments parser for Human Beings.
  • Get started with the Natural Language Toolkit
  • pdb++ pdb++, a drop-in replacement for pdb (the Python debugger)
  • napari/napari a fast, interactive, multi-dimensional image viewer for python
  • pydantic Data validation and settings management using python type annotations.
  • pySDR






Interesting Stuffs



  • Example of pypy-c-sandbox for launching random scripts
  • Python "sandbox" escape

Instructions for pypy-2.1

$ cd pypy/goal
$ python ../../rpython/bin/rpython  -O2 --sandbox
$ PYTHONPATH=$PYTHONPATH:$PWD/../../ ../..//pypy/sandbox/ pypy-c


  • Performance analysis
  • CProfile
  • scalene is a high-performance CPU and memory profiler for Python that does a number of things that other Python profilers do not and cannot do


$ python -m shlex
Token: 'kdkd'
34 5455
Token: '34'
Token: '5455'
Token: '$'
Token: 'edx'
Token: '='
Token: '34'




>>> a = [1,4,-1,0,13]
>>> a.sort()
>>> a
[-1, 0, 1, 4, 13]
>>> import operator
>>> x = {1: 2, 3: 4, 4:3, 2:1, 0:0}
>>> sorted_x = sorted(x.iteritems(), key=operator.itemgetter(1))

Two's complement

>>> value = 0xb59395a9
>>> f"{ctypes.c_uint32(value).value:032b}"
>>> f"{ctypes.c_uint32(~value).value:032b}"


import getopt, sys

def main():
        opts, args = getopt.getopt(sys.argv[1:], "ho:v", ["help", "output="])
    except getopt.GetoptError as err:
        # print help information and exit:
        print(err) # will print something like "option -a not recognized"
    output = None
    verbose = False
    for o, a in opts:
        if o == "-v":
            verbose = True
        elif o in ("-h", "--help"):
        elif o in ("-o", "--output"):
            output = a
            assert False, "unhandled option"
    # ...

if __name__ == "__main__":


def argparse_vendor_product(value):
    vendor, product = tuple(value.split(":"))

    return int(vendor, 16), int(product, 16)

def parse_args():
    args = argparse.ArgumentParser(description='upload and run some code')

        help="vendor:product of the device you want to interact with")
    args.add_argument('--binary', required=True)
    args.add_argument('--address', type=functools.partial(int, base=0))

    return args.parse_args()


import serial
ser = serial.Serial('/dev/ttyUSB0')  # open serial port
print(         # check which port was really used
ser.write(b'hello')     # write a string


def trace(f):
    def _inner(*args, **kwargs):
        print ' # ', f.func_name
        return f(*args, **kwargs)
    return _inner

def challenge(count):
    def _challenge(x):
        def _inner(*args, **kwargs):
            print('[+] challenge %d' % count)
            return x(*args, **kwargs)
        return _inner
    return _challenge


def decript(cipher, key):
    >>> a = [0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1]
    >>> b = [1, 1, 1, 1, 1, 1, 1]
    >>> decript(a, b) #doctest: +NORMALIZE_WHITESPACE
    [[1, 0, 1, 0, 1, 0, 1],
    [0, 0, 0, 0, 0, 0, 0]]
    r = []
    for i in xrange(0, len(cipher) - len(key) + 1, 7):
        r.append(XOR(cipher[i:i + len(key)], key))

    return r
$ python -m doctest


It's possible to write the documentation along with the code.


Maximum float


>>> infinity = float("inf")
>>> infinity
>>> infinity / 10000

Print out some docstring for documentation purpose

python -c 'from macro import matrixify;print(matrixify.__doc__.replace("\n    ", "\n"))' | rst2html


  • Documentation
  • Multi line formatting

Remember that logger.basicConfig() attaches the stream handler by default, if you want to fine tune the logging you have to set it by yourself.

import logging
import os

logger = logging.getLogger(__name__)
logger.setLevel(os.getenv("LOG") or "INFO")

It's possible to define a custom level like SUBDEBUG (

import logging

logging.addLevelName(SUBDEBUG, 'SUBDEBUG')

def subdebug(self, message, *args, **kws):
    self.log(SUBDEBUG, message, *args, **kws) 
logging.Logger.subdebug = subdebug

l = logging.getLogger()

stream = logging.StreamHandler()
formatter = logging.Formatter('%(levelname)s - %(filename)s:%(lineno)d - %(message)s')

logger = logging.getLogger(__file__)

If you want that your logging string impact performance when the level is not used you should let the logger itself doing the formatting: the various logging functions accept a format string with the % style and a list of positional arguments like

logger.debug("this is a string: '%s'", string_to_log)

Flatten list

>>> chain = itertools.chain.from_iterable([[1,2],[3],[5,89],[],[6]])
>>> print(list(chain))
>>> [1, 2, 3, 5, 89, 6]
for x in s:
  if x:
      return True
return False

return any(x)


        _manage_object(pk, *args, **kwargs)
        obj = Object.objects.get(pk=pk)
        # get the exception context to reuse later
        exc_info = sys.exc_info()
        import traceback
        print traceback.print_tb(exc_info[2])

Read/write UTF8 files

Seems like that the builtin open() in python manage only ascii files

import codecs

def create_post(filepath, content):
    with, 'w+', encoding='utf-8') as f:

Get first item of a nested list

>>> from operator import itemgetter
>>> rows = [(1, 2), (3, 4), (5, 6)]
>>> map(itemgetter(1), rows)
[2, 4, 6]

Extract URL from string

import re

myString = "This is my tweet check it out"

print"(?P<url>https?://[^\s]+)", myString).group("url")

Routing from REGEXs

In [1]: import re

In [2]: c = re.compile(r'^w::(?P<type>\w+)::(?P<id>\d*)::')

In [3]: s = 'w::w::1::'

In [5]: m = c.match(s)

In [6]: m.groupdict()
Out[6]: {'id': '1', 'type': 'w'}

Add file into a tarfile from a string

def elaborate_archive(filepath, **kwargs):
    tar_src =, mode='a')

    version_file = StringIO.StringIO(kwargs['version'])

    version_tarinfo = tarfile.TarInfo('VERSION')
    version_tarinfo.size = len(version_file.buf)
    tar_src.addfile(version_tarinfo, version_file)


$ pip install pandas
import pandas as pd

You can read data from a CSV

df = pd.read_csv("/path/to/data")

or create manually one

df = pd.DataFrame({
    "column 1": [data1, data2, ..., dataN],
    "column 2": [...],

To have general information about the DataFrame

A nice feature is the filtering

df[(df.duration > = 200) & (df.genre == "Drama")]

It's possible to plot directly

df.plot(x='GE', y=['TOTALE_19', 'TOTALE_20'], figsize=(20, 10))