Building your first Qollab projectLesson 2 of 7

Learn Python language basics

Python has become the de facto programming language of quantum computing. This guide highlights some basic Python commands and logic; foundational elements to build from.

Learn Python language basics

This guide assumes that you have already installed the UV package manager and latest version of Python on your machine. It also assumes that you are able to open a shell application, and are comfortable editing code. If any of that sounds unfamiliar or outside your expertise, don’t fret! We have a simple guide for that: Setup Python on your machine. Give that guide a thorough look over first, then come back here to start flexing your code muscles.

Running Python

Python runs line-by-line, like a Read-Evaluate-Print Loop (REPL). Python scripts are just plain text files, often with file names ending in a .py suffix. Our code examples rely on the UV package manager. (See our guide for Setting up Python and UV on your machine.) A simple one-line Python script can be run from the command-line shell via uv like so:

uv run python -c "print('Hello, World!')"

To enter a full Python REPL via uv, enter the following into your shell:

uv run python

To exit the Python REPL, type exit() or quit() and press Enter. For Python 3.13 and later, those parenthesis may be omitted.

To run a Python script file via uv, enter the following, replacing main.py with the name of the script you wish to execute.

uv run python main.py

Basic Syntax

Variable creation is implicit. Unlike other scripting languages, there’s no var, let, or const keywords required in order to create a variable handle. Assignment binds a name to a value:

x = 10
y = "hello"
z = [1, 2, 3]

Everything is an object. Variables are labels, not boxes.

Indentation (not curly braces or other visible characters) defines scope blocks:

if cond:
    do_something()
else:
    do_other()

Indentation is semantic, that is, your negative space carries logical meaning. Therefore, your code must consistenly use either tabs or spaces for indentation without deviation. The offical style guide for Python (“PEP 8”) specifies an indentation level as 4 spaces wide.

Functions

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

Python functions capture variables lexically (like JS closures), but the write rules differ, explained in scoping below. Default arguments are evaluated once (careful!):

def append_to_list(value, lst=[]):  # BAD
    lst.append(value)
    return lst
Use:
def append_to_list(value, lst=None):
    if lst is None:
        lst = []
    return lst

Classes (Python-style OOP)

class Car:
    def __init__(self, make):
        self.make = make

    def honk(self):
        print("beep")

c = Car("Tesla")
c.honk()
Methods need self explicitly. Inheritance uses the class name:
class SportsCar(Car):
    pass

Collections

List (array)

a = [1, 2, 3]

Tuple (immutable)

t = (1, 2, 3)

Dict

d = {"name": "Stewart", "role": "CTO"}

Set

s = {1, 2, 3}

List comprehensions:

squares = [x*x for x in range(10)]

Dictionary comprehensions:

doubles = {x: x*2 for x in range(5)}

Modules / Imports

import math
from pathlib import Path
from mymodule import helper

Scripts are modules. Packages are directories with __init__.py.

Exceptions

try:
    risky()
except ValueError:
    print("Nope")
except Exception as e:
    print("Other error:", e)
finally:
    cleanup()

File I/O

with open("data.txt") as f:
    text = f.read()

The with block ensures cleanup (context manager).

Python Variable Scoping (LEGB Rule)

Python scoping follows L-E-G-B: - Local, inside current function - Enclosing, outer function scopes (closures) - Global, module-level - Built-in, len, print, etc This is very similar to JavaScript lexical scoping except Python treats assignment differently.

Important Rule

Any variable you assign to inside a function is considered local unless you explicitly declare otherwise. This catches people:

x = 10

def f():
    print(x)     # ERROR? Actually, UnboundLocalError!
    x = 20

Why? Because the presence of x = 20 makes x local to f. Python sees “you assign to x somewhere in the function” → therefore x is local everywhere in that function. To mutate the module-level x:

x = 10

def f():
    global x
    print(x)
    x = 20

Closures (Enclosing scope)

def outer():
    x = 10
    def inner():
        print(x)     # OK
    inner()

But trying to assign inside the closure fails:

def outer():
    x = 10
    def inner():
        x = 20      # This creates NEW local x
    inner()
    print(x)        # still 10

To mutate the enclosing scope’s variable use nonlocal:

def outer():
    x = 10
    def inner():
        nonlocal x
        x = 20
    inner()
    print(x)   # 20

Pythonic Style Cheat Sheet

Idiomatic ways to write things:

Iteration

for x in items:
    ...

Enumerate with index

for i, x in enumerate(items):
    ...

Iterate dict

for k, v in d.items():
    ...

Ternary

msg = "ok" if status else "bad"

With-open file handling

with open("file.txt") as f:
    data = f.read()

Write generator expression

total = sum(x*x for x in nums)

Tiny “gotchas” you should know immediately

Mutability matters. Lists, dicts, sets = mutable. Tuples, ints, strings = immutable. Everything is reference by default.

a = [1,2]
b = a
b.append(3)
a == [1,2,3]// True!

Truthiness:

0, "", [], {}, None, False → falsey

Everything else is truthy.

10-line example that uses everything above

def make_counter():
    count = 0
    def inc():
        nonlocal count
        count += 1
        return count
    return inc

counter = make_counter()
print(counter())  # 1
print(counter())  # 2

Stay in the loop.

Get the latest tutorials, demos, and project showcases straight to your inbox. No noise, just the good stuff.