Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Python bàsic

Programes

Els programes consten de mòduls. Els mòduls, al seu torn, poden contenir instruccions, definicions de funcions i/o definicions de classe. Cada mòdul està associat a un fitxer font (extensió .py) i possiblement a un fitxer de bytecode (extensió .pyc).

Un compilador/intèrpret anomenat màquina virtual de Python (PVM) tradueix els fitxers font de Python a bytecode abans de l’execució. La PVM pot desar el bytecode corresponent en fitxers per a execucions posteriors.

Execució

Un programa en Python es pot executar com a script des d’una línia d’ordres:

python helloworld.py

Errors de sintaxi es detecten abans d’executar el programa. Errors de tipus (TypeError) es detecten en temps d’execució.

Hola, món

La versió més senzilla d’aquest programa consisteix en una sola instrucció:

print("Hello, world!")

L’ordre print converteix automàticament les dades en text, les mostra i mou el cursor a la línia següent:

print("Hello, world!")
print(34)
print("Hello, world!", end="")  # evita el salt de línia

Una altra versió integra aquesta instrucció en una funció principal que es crida al final del mòdul:

def main():
    print("Hello, world!")

main()

També tenim entrada des del teclat:

name = input("Enter your name: ")       # cadena
age = int(input("Enter your age: "))    # sencer

Estructura i sintaxi

  • Els literals inclouen números, cadenes, tuples, llistes i diccionaris.

  • Els identificadors inclouen els noms de variables, classes, funcions i mètodes.

  • Les paraules reservades inclouen les de les principals sentències de control (if, while, for, import, etc.), operadors (in, is, etc.), definicions (def, class, etc.) i valors especials (True, False, None, etc.).

  • Els elements lèxics d’una sola línia estan separats per zero o més espais.

  • La sagnia és significativa i s’utilitza per marcar estructures sintàctiques, com ara blocs d’instruccions i codi dins de definicions de funcions, classes i mètodes.

  • Una frase es pot trencar i continuar a la línia següent després d’una coma o mitjançant el símbol \.

  • Les capçaleres de les instruccions de control i les definicions de funcions, classes i mètodes acaben amb dos punts (:).

  • Un comentari de final de línia comença amb el símbol #:

    # This is a comment
    # spanning multiple lines.
    

    Els docstring, que comencen amb """ i acaben amb """, documenten funcions, classes o mòduls:

    """
    This is a docstring
    spanning multiple lines.
    """
    

Tipus de dades

Tots els valors de dades, incloses les funcions, són objectes. I totes les variables són referències a aquests objectes.

  • int representa nombres sencers: 42, -7, 0
  • float representa nombres de coma flotant amb precisió doble: 3.14, -0.5
  • bool representa valors booleans: True o False. És una subclasse de int, on True equival a 1 i False a 0.
  • str representa cadenes de text: "hello", 'world'
  • None és un valor especial que indica l’absència de valor. És l’únic valor del tipus NoneType.
print(type(42))      # <class 'int'>
print(type(3.14))    # <class 'float'>
print(type(True))    # <class 'bool'>
print(type("hi"))    # <class 'str'>
print(type(None))    # <class 'NoneType'>

Variables

Una variable s’introdueix i s’estableix a un valor inicial mitjançant una instrucció d’assignació:

<variable> = <expressió>

x = 1
x = x + 3.14
  • Una variable s’introdueix i s’estableix a un valor inicial mitjançant una instrucció d’assignació.
  • Qualsevol variable pot anomenar qualsevol objecte i es pot reiniciar a qualsevol objecte.
  • La variable selecciona el tipus de l’objecte al qual està vinculada.
  • La comprovació de tipus i la comprovació de referències a variables no inicialitzades es realitzen en temps d’execució.

Les variables en si mateixes no tenen tipus. L’objecte al qual fa referència una variable té un tipus.

Aritmètica i condicions

Els operadors aritmètics inclouen +, -, *, /, %, // i ** (exponenciació).

L’operació / retorna sempre un float, i l’operació // un sencer, sempre que els operands siguin sencers.

max, min, abs i round són funcions estàndard (round retorna un nombre sencer).

El mòdul math inclou funcions per a trigonometria, logaritmes, arrels quadrades, etc.

round(3.14)      # 3
math.sqrt(2)     # 1.4142...

Els operadors de comparació són ==, !=, <, >, <= i >=. Tots retornen True o False.

print("AAB" > "AAA")   # True

El tipus booleà bool inclou els valors constants True i False (amb majúscules).

Altres valors, com ara 0, '', [] i None, també signifiquen False. Pràcticament qualsevol altre valor significa True.

Els operadors lògics són not, and i or. L’avaluació en curtcircuit s’atura quan hi ha prou informació disponible per retornar un valor. not s’avalua abans de and, que s’avalua abans de or.

Conversions

Els tipus numèrics es poden convertir a altres tipus numèrics amb les funcions de conversió:

int(3.14)    # 3
float(3)     # 3.0

Les funcions ord i chr s’utilitzen per convertir entre sencers i caràcters:

ord('A')     # 65
chr(65)      # 'A'

La funció str converteix qualsevol objecte Python a la seva representació de cadena:

str(45)      # '45'
str(3.14)    # '3.14'

Cadenes

  • Una cadena és una seqüència de 0 o més caràcters.
  • Les cadenes són instàncies de la classe str i són objectes immutables.
  • La funció len retorna el nombre de caràcters d’una cadena.
  • L’operador de subíndex [] accedeix a un caràcter en una posició determinada.
greeting = "Hello, world!"
print(len(greeting))     # 13
print(greeting[0])       # 'H'
print(greeting[-1])      # '!'
  • Les cadenes es poden comparar amb els operadors de comparació estàndard ==, <, etc.
  • Els literals de cadena es formen amb cometes simples o dobles com a delimitadors.
  • Les seqüències d’escapada es formen amb \ seguit d’una lletra adequada com ara n o t.

L’operador de concatenació + uneix dues cadenes per formar una tercera cadena nova:

str(35) + " pages long."

Mètodes de cadena

La classe str inclou molts mètodes útils:

MètodeDescripcióExemple
upper()Converteix a majúscules"hello".upper()"HELLO"
lower()Converteix a minúscules"HELLO".lower()"hello"
strip()Elimina espais dels extrems" hi ".strip()"hi"
split(sep)Divideix en una llista"a,b,c".split(",")["a","b","c"]
join(iterable)Uneix elements amb la cadena com a separador",".join(["a","b"])"a,b"
replace(old, new)Substitueix totes les ocurrències"aab".replace("a","x")"xxb"
startswith(s)Comprova si comença per s"hello".startswith("he")True
find(s)Retorna la posició de la primera ocurrència"hello".find("ll")2

F-strings

Les f-strings permeten incrustar expressions directament dins de literals de cadena:

name = "Maria"
age = 25
message = f"Hello, my name is {name} and I am {age} years old."

A dins de les claus pot haver-hi expressions. Si són números, es poden formatar després dels dos punts:

pi = 3.14159
print(f"Pi is approximately {pi:.2f}")   # Pi is approximately 3.14

Control de flux

if

if <boolean expression>:
    <statement>
    ...
elif <boolean expression>:
    <statement>
    ...
else:
    <statement>
    ...

Les sentències del conseqüent i de cada alternativa estan marcades amb sagnat.

match

La instrucció match (Python 3.10+) compara un valor contra una sèrie de patrons i executa la branca corresponent:

match command:
    case "quit":
        print("Quitting.")
    case "help":
        print("Showing help.")
    case _:
        print(f"Unknown command: {command}")

Els patrons poden incloure valors literals, tuples, llistes, classes i guards (if):

match point:
    case (0, 0):
        print("Origin")
    case (x, 0):
        print(f"On x-axis at {x}")
    case (x, y):
        print(f"At ({x}, {y})")

while

while <boolean expression>:
    <statement>
    ...

L’instrucció break surt d’un bucle:

while True:
    break

L’instrucció pass no fa res:

while True:
    pass

for

Només hi ha un tipus de bucle for, que visita cada element d’un objecte iterable:

for <variable> in <iterable>:
    <statement>
    ...
words = ["apple", "banana", "cherry"]
for word in words:
    print(word)

Els bucles simples controlats per recompte utilitzen range:

for i in range(5):          # 0, 1, 2, 3, 4
    print(i)

for i in range(2, 5):       # 2, 3, 4
    print(i)

for i in range(0, 10, 2):   # 0, 2, 4, 6, 8
    print(i)

L’instrucció return surt d’una funció o mètode. Si no s’especifica cap expressió, es retorna None.

Funcions

A diferència dels mètodes, que són funcions associades a un objecte o una classe, Python disposa de funcions independents que no pertanyen a cap objecte i que es poden cridar directament:

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

result = add(3, 4)
print(result)   # 7

Python inclou moltes funcions built-in que es poden utilitzar sense importar cap mòdul:

FuncióDescripcióExemple
printEscriu dades a la sortida estàndardprint("Hello")
lenRetorna la longitud d’una col·lecció o cadenalen("abc") retorna 3
typeRetorna el tipus d’un objectetype(3) retorna <class 'int'>
intConverteix un valor a enterint("10") retorna 10
strConverteix un valor a cadenastr(5) retorna "5"
rangeGenera una seqüència d’enterslist(range(3)) retorna [0,1,2]
inputLlegeix entrada des del teclatinput("Type something: ")

Àmbit de les variables

Python resol els noms seguint l’ordre LEGB:

  • Local: dins de la funció actual
  • Enclosing: funció externa si hi ha funcions niades
  • Global: del mòdul actual
  • Built-in: funcions i noms predefinits de Python, com len, print

global fa que una variable dins d’una funció faci referència a la definida al nivell global del mòdul. Sense global, una assignació dins d’una funció crea una variable local.

nonlocal fa que una variable dins d’una funció interna faci referència a la definida a la funció externa.

Arguments per defecte i nominals

Els paràmetres d’una funció poden tenir valors per defecte:

def greet(name="World"):
    print(f"Hello, {name}!")

greet()           # Hello, World!
greet("Julian")   # Hello, Julian!

Els arguments també es poden passar per nom, independentment de l’ordre:

def show_info(name, age):
    print(f"Name: {name}, Age: {age}")

show_info(age=25, name="Anna")

Arguments variables

*args captura arguments posicionals i **kwargs captura arguments amb nom (keyword arguments):

def show(*args, **kwargs):
    print(args, kwargs)

show(1, 2, 3, name="Anna", age=25)
# (1, 2, 3) {'name': 'Anna', 'age': 25}

Funcions com a objectes

Les funcions a Python són objectes de primera classe: es poden assignar a variables, passar com a arguments i retornar des de funcions.

def square(x):
    return x * x

f = square
print(f(5))   # 25

def apply(f, value):
    return f(value)

print(apply(square, 7))   # 49

Funcions lambda

Les funcions lambda són funcions anònimes definides en una sola línia:

add = lambda a, b: a + b
print(add(3, 4))   # 7

numbers = [1, 4, 2, 5]
sorted_desc = sorted(numbers, key=lambda x: -x)
print(sorted_desc)   # [5, 4, 2, 1]

Closures

Es poden definir funcions dins d’altres funcions. Quan una funció interna recorda l’estat de les variables externes, es diu que és un closure:

def multiplier(n):
    def multiply(x):
        return x * n
    return multiply

times_3 = multiplier(3)
print(times_3(10))   # 30

Decoradors

Un decorador és una funció que envolta una altra funció (o classe) per afegir-hi comportament. La sintaxi @nom és equivalent a f = nom(f) aplicat just després de la definició:

def shout(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

@shout
def greet(name):
    return f"hello, {name}"

print(greet("world"))   # HELLO, WORLD

Python i les seves biblioteques estàndard proporcionen molts decoradors predefinits que s’utilitzen al llarg d’aquest document: @classmethod, @staticmethod, @property, @dataclass, @abstractmethod i @total_ordering.

Organització del codi

Els mòduls de Python s’utilitzen per defecte en la forma de singletons, com els mòduls de JavaScript. Comparativament a llenguatges com Java, les classes només s’utilitzen quan cal estat individual.

Un exemple de singleton:

value = 0

def increment():
    global value
    value += 1
    return value

Mòduls

Un mòdul és un fitxer .py amb definicions (funcions, classes, constants…). S’importa amb:

import <module name>

Els recursos del mòdul es referencien com <module name>.<resource name>:

import math
print(math.sqrt(2), math.pi)

Alternativament, es pot importar un recurs individual:

from math import sqrt, pi
print(sqrt(2), pi)

Es pot assignar un àlies per escurçar o evitar conflictes:

from datetime import datetime as dt

Paquets

Un paquet és un directori que conté un arxiu especial __init__.py i altres mòduls:

mypackage/
    __init__.py
    utils.py
    models.py
import mypackage.utils
from mypackage import models

Instal·lació de paquets

Python inclou un gestor de paquets, pip, per instal·lar biblioteques externes:

pip install numpy
pip install -r requirements.txt

És recomanable treballar dins d’un entorn virtual per aïllar les dependències de cada projecte:

python -m venv .venv
source .venv/bin/activate    # Linux/macOS
.venv\Scripts\activate       # Windows

Col·leccions

TipusMutabilitatOrdenatPermet duplicats
list
tupleNo
setNoNo
frozensetNoNoNo
dictSí (>=3.7)No (claus)

Llistes

  • Una llista és una seqüència mutable de 0 o més objectes de qualsevol tipus.
  • La funció len retorna el nombre d’elements d’una llista.
  • L’operador de subíndex [] accedeix a un element en una posició determinada.
numbers = [45, 56, 67]
print(len(numbers), numbers[2])   # 3 67
  • Les llistes d’objectes comparables es poden comparar amb els operadors estàndard ==, <, etc.
  • La classe list inclou molts mètodes útils per a insercions, eliminacions i cerques.

Sets

  • Un set és una col·lecció mutable de 0 o més objectes únics.
s1 = set()
for x in range(10):
    s1.add(x)

s2 = {1, 2, 3}
s3 = s1.intersection(s2)

Diccionaris

  • Un diccionari és una col·lecció mutable de parells clau/valor únics.
  • L’operador de subíndex accedeix, afegeix o substitueix valors.
scores = {}
for i in range(1, 6):
    scores["player" + str(i)] = i * 10

for key in scores:
    print(key, scores[key])

Tuples

  • Una tuple és una seqüència immutable de 0 o més objectes de qualsevol tipus.
  • Útil per agrupar valors que no han de canviar.
  • Es pot usar com a clau de diccionari, a diferència de les llistes.
empty = ()
point = (3, 4)
mixed = ("hello", 3.14, True)

print(point[0])        # 3
for item in mixed:
    print(item)

Iteradors

Un iterador és un objecte que admet el recorregut d’una col·lecció. La PVM utilitza automàticament un iterador sempre que veu un bucle for.

La funció iter retorna un objecte iterador per a una col·lecció:

it = iter([1, 2, 3])
print(next(it), next(it), next(it))   # 1 2 3

Quan next ha retornat l’últim element, qualsevol crida posterior genera una excepció StopIteration.

Per recórrer tots els elements manualment:

it = iter([1, 2, 3])
while True:
    try:
        element = next(it)
    except StopIteration:
        break

Comprehensions

Les comprehensions construeixen col·leccions a partir d’iterables de manera compacta.

Llistes: [expressió for element in iterable if condició]

evens = [x for x in range(10) if x % 2 == 0]
squares = [x**2 for x in range(5)]

Conjunts: mateixa forma però amb {}

vowels = {c for c in "informatics" if c in "aeiou"}

Diccionaris: {clau: valor for element in iterable}

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

names = ["Anna", "Joan", "Pau"]
lengths = {name: len(name) for name in names}

Generadors

Un generador és una manera fàcil de crear iteradors personalitzats. En lloc d’implementar una classe amb __iter__ i __next__, usem una funció amb la instrucció yield:

def count_to(n):
    i = 1
    while i <= n:
        yield i
        i += 1

for x in count_to(3):
    print(x)   # 1, 2, 3

Quan yield s’executa, la funció es pausa i el valor es retorna. L’execució es reprèn en el punt on es va deixar, mantenint l’estat local.

Classes i objectes

Mètodes especials

Els mètodes especials (o dunder methods, per “double underscore”) són mètodes amb noms de la forma __nom__ que Python crida automàticament en resposta a operacions del llenguatge. Per exemple, __init__ es crida en instanciar, __str__ quan es fa print, i __eq__ quan s’usa ==. Definint-los, la classe pot participar en protocols estàndard del llenguatge com comparació, iteració o gestió de context.

Definició bàsica

Les definicions de classes tenen la forma general:

class <name>(<superclass>):
    <class variables>
    <methods>

La superclasse en parèntesis s’omet per a classes bàsiques.

class Student:

    NUM_GRADES = 5

    def __init__(self, name):
        self.name = name
        self.grades = []
        for i in range(Student.NUM_GRADES):
            self.grades.append(0)

    def get_name(self): return self.name

    def get_grade(self, i):
        return self.grades[i - 1]

    def set_grade(self, i, new_grade):
        self.grades[i - 1] = new_grade

    def __str__(self):
        result = self.name + "\n"
        result += " ".join(map(str, self.grades))
        return result

Ús:

s = Student("Mary")
for i in range(1, Student.NUM_GRADES + 1):
    s.set_grade(i, 100)
print(s)

Instanciació

<class name>(<arguments>)

s = Student("Mary")

La PVM crida automàticament __init__ en instanciar. L’argument self s’assigna automàticament a l’objecte nou. Les variables i paràmetres no tenen tipus; el tipus el té l’objecte al qual fan referència.

Visibilitat

Tots els elements definits dins d’una classe (variables o mètodes) són potencialment visibles. Per desaconsellar l’accés directe a variables, els implementadors de la classe en prefixen el nom amb _.

Variables d’instància i de classe

Les variables d’instància sempre porten el prefix self. S’inicialitzen en __init__. Les variables de classe es declaren directament dins de la classe, es comparteixen per totes les instàncies i es referencien amb el nom de la classe. Per convenció, s’escriuen en majúscules.

class Student:

    NUM_GRADES = 5   # variable de classe

    def __init__(self, name=""):
        self.name = name       # variable d'instància
        self.grades = []
        for i in range(Student.NUM_GRADES):
            self.grades.append(0)
s1 = Student("Mary")
s2 = Student()
print(Student.NUM_GRADES)   # 5

Mètodes d’instància

def <name>(self, <other arguments>):
    <statements>

L’argument self és necessari per a un mètode d’instància. La PVM l’assigna a l’objecte receptor en la crida. Un mètode que no retorna explícitament un valor retorna None.

Com que no hi ha sobrecàrrega de mètodes, s’emula amb paràmetres per defecte o comprovant el tipus dels arguments:

def reset_grades(self, value=0):
    for i in range(Student.NUM_GRADES):
        if type(value) == list:
            self.grades[i] = value[i]
        else:
            self.grades[i] = value

Ús:

s = Student("Mary")
s.reset_grades(100)
s.reset_grades()
new_grades = [85, 66, 90, 100, 73]
s.reset_grades(new_grades)

Mètodes de classe

Els mètodes de classe no accedeixen a l’estat d’instàncies concretes. Reben cls com a primer argument i s’accedeix a ells via el nom de la classe:

class Student:

    @classmethod
    def get_letter_grade(cls, grade):
        if grade > 89: return "A"
        elif grade > 79: return "B"
        else: return "F"

Ús:

print(Student.get_letter_grade(95))   # A

Mètodes estàtics

Un mètode estàtic no rep ni self ni cls. S’usa per funcions utilitàries lògicament relacionades amb la classe però que no necessiten accés al seu estat:

class MathUtils:

    @staticmethod
    def is_even(n):
        return n % 2 == 0

print(MathUtils.is_even(4))   # True

Dataclasses

El decorador @dataclass genera automàticament __init__, __repr__ i __eq__ a partir de les anotacions de tipus declarades a la classe. És el patró estàndard per a classes que principalment contenen dades:

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

p = Point(1.0, 2.0)
print(p)          # Point(x=1.0, y=2.0)
print(p.x)        # 1.0
print(p == Point(1.0, 2.0))   # True

Amb frozen=True, la classe es fa immutable (equivalent a una tupla amb noms):

@dataclass(frozen=True)
class Color:
    r: int
    g: int
    b: int

Propietats

El decorador @property permet accedir a un mètode com si fos un atribut:

class Circle:

    def __init__(self, radius):
        self._radius = radius

    @property
    def area(self):
        return 3.14159 * self._radius ** 2

c = Circle(5)
print(c.area)   # 78.53...

Mètode __str__

La funció str converteix qualsevol objecte a la seva representació de cadena. Es pot personalitzar definint __str__. Operacions com print l’utilitzen automàticament:

class Student:

    def __str__(self):
        result = self.name + "\n"
        result += " ".join(map(str, self.grades))
        return result

Igualtat

L’operador == utilitza is per defecte, que comprova si dues variables apunten exactament al mateix objecte. Per a igualtat estructural, es defineix __eq__:

class Student:

    def __eq__(self, other):
        if self is other:
            return True
        elif type(self) != type(other):
            return False
        else:
            return self.name == other.name

    def __ne__(self, other):
        return not self == other

Ús:

s1 = Student("Mary")
s2 = Student("Bill")
s3 = Student("Bill")

print(s1 == s2)   # False
print(s2 == s3)   # True
print(s2 is s3)   # False

Comparable

Per fer que els objectes d’una classe suportin tots els operadors de comparació (<, >, <=, >=, ==, !=), es defineixen __eq__ i __lt__ i s’usa el decorador @total_ordering, que genera automàticament els operadors restants:

from functools import total_ordering

@total_ordering
class Student:

    def __eq__(self, other):
        if self is other: return True
        elif type(self) != type(other): return False
        else: return self.name == other.name

    def __lt__(self, other):
        return self.name < other.name

Ús:

s1 = Student("Mary")
s2 = Student("Bill")

print(s1 < s2)    # False
print(s1 > s2)    # True

Iteradors personalitzats

Per fer que una classe sigui iterable, es defineix __iter__ com a generador. L’exemple següent mostra una pila enllaçada que suporta iteració:

class Node:

    def __init__(self, data, next):
        self.data = data
        self.next = next


class LinkedStack:

    def __init__(self):
        self.top = None
        self.size = 0

    def push(self, element):
        self.top = Node(element, self.top)
        self.size += 1

    def pop(self):
        element = self.top.data
        self.top = self.top.next
        self.size -= 1
        return element

    def __len__(self):
        return self.size

    def __iter__(self):
        current = self.top
        while current is not None:
            yield current.data
            current = current.next

Ús:

stack = LinkedStack()
stack.push(1)
stack.push(2)
stack.push(3)

for item in stack:
    print(item)   # 3, 2, 1

Herència

En Python es pot definir una nova classe que reutilitza el codi d’una altra. La nova classe n’és una subclasse i hereta tots els atributs i mètodes de la classe pare.

class Animal:

    def __init__(self, name):
        self.name = name

    def speak(self):
        return ""

    def __str__(self):
        return f"{self.name} says: {self.speak()}"


class Dog(Animal):

    def speak(self):
        return "Woof!"


class Cat(Animal):

    def speak(self):
        return "Meow!"

Ús:

animals = [Dog("Rex"), Cat("Whiskers"), Dog("Buddy")]
for animal in animals:
    print(animal)

La subclasse pot cridar el constructor del pare amb super():

class Dog(Animal):

    def __init__(self, name, breed):
        super().__init__(name)
        self.breed = breed

    def speak(self):
        return "Woof!"

Classes abstractes

Quan diverses classes contenen codi comú, es pot factoritzar en una classe abstracta: una classe que no s’instancia directament, sinó que serveix de base comuna.

from abc import ABC, abstractmethod

class Shape(ABC):

    @abstractmethod
    def area(self):
        pass

    def __str__(self):
        return f"{type(self).__name__} with area {self.area():.2f}"


class Circle(Shape):

    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14159 * self.radius ** 2


class Rectangle(Shape):

    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

Ús:

shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
    print(shape)

El decorador @abstractmethod obliga les subclasses a implementar el mètode. Intentar instanciar directament Shape genera un error.

Gestió d’errors

Python permet capturar i gestionar errors amb blocs try i except:

try:
    x = int(input("Enter a number: "))
    result = 10 / x
except ValueError:
    print("That was not an integer.")
except ZeroDivisionError:
    print("Cannot divide by zero.")
finally:
    print("This always runs.")
  • try: conté el codi que pot generar una excepció.
  • except: captura un error específic i executa el codi corresponent.
  • finally: (opcional) s’executa sempre, hagi passat o no una excepció.

Es pot capturar l’excepció en una variable amb as:

try:
    1 / 0
except ZeroDivisionError as e:
    print(f"Error: {e}")

Es poden provocar errors manualment amb raise:

def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero.")
    return a / b

Tipus d’excepcions comunes:

ExcepcióQuan apareix
ValueErrorConversió incorrecta de tipus (int("abc"))
ZeroDivisionErrorDivisió entre zero
TypeErrorOperacions entre tipus incompatibles
IndexErrorÍndex fora de rang en una seqüència
KeyErrorClau inexistent en un diccionari
FileNotFoundErrorArxiu inexistent

Context managers

Un gestor de context defineix accions a fer abans i després d’un bloc de codi. S’utilitzen habitualment per gestionar recursos que s’han d’obrir i tancar.

with open("notes.txt", "r") as file:
    content = file.read()
  • open(...) obre el fitxer.
  • El fitxer es tanca automàticament en sortir del bloc, fins i tot si hi ha un error.
  • Això evita haver d’escriure file.close() manualment.

Es poden definir gestors de context propis amb dos mètodes especials:

  • __enter__: s’executa al començar el bloc.
  • __exit__: s’executa al final, fins i tot si hi ha excepcions.

Anotacions de tipus

Python permet indicar els tipus d’arguments i valors retornats. Les anotacions no es comproven en temps d’execució, però ajuden a documentar el codi i milloren la detecció d’errors per eines com mypy o editors com VSCode.

def greet(name: str, age: int) -> str:
    return f"Hello, {name}. You are {age} years old."

Des de Python 3.10, els tipus compostos s’escriuen directament amb tipus natius i l’operador |:

SintaxiDescripció
list[T]Llista d’elements del tipus T
dict[K, V]Diccionari amb claus de tipus K i valors V
T | NoneValor que pot ser de tipus T o None
tuple[T1, T2]Tupla amb elements de tipus T1, T2, etc.
T1 | T2Valor que pot ser de tipus T1 o T2
typing.AnyQualsevol tipus (requereix import)
def sum_list(nums: list[int]) -> int:
    return sum(nums)

def find(lst: list[int], value: int) -> int | None:
    if value in lst:
        return value
    return None

Les formes antigues del mòdul typing (List[T], Dict[K,V], Optional[T], Union[T1, T2]) continuen funcionant però es consideren llegat i no s’han d’usar en codi nou.

Python idiomàtic

Aquesta secció recull construccions i funcions habituals de Python.

  • enumerate(iterable) → índex i valor alhora

    for i, value in enumerate(["a", "b", "c"]):
        print(i, value)
    # 0 a / 1 b / 2 c
    
  • zip → agrupar iterables en paral·lel

    names = ["A", "B", "C"]
    scores = [7, 8, 9]
    for name, score in zip(names, scores):
        print(name, score)
    
  • all, any → comprovar condicions sobre col·leccions

    scores = [7, 8, 9]
    print(all(n > 5 for n in scores))    # True
    print(any(n == 10 for n in scores))  # False
    
  • sorted(iterable, key=...) → nova llista ordenada sense modificar l’original

    words = ["car", "water", "moon"]
    print(sorted(words, key=len))   # ['car', 'moon', 'water']
    
  • Desempaquetament múltiple

    a, b = (1, 2)
    a, *rest, b = [1, 2, 3, 4, 5]
    # a=1, rest=[2,3,4], b=5
    
  • Slicing avançat (essencial per arrays i tensors)

    lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    print(lst[2:7])      # [2, 3, 4, 5, 6]
    print(lst[::2])      # [0, 2, 4, 6, 8]
    print(lst[::-1])     # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    
  • map, filter, reduce → programació funcional

    from functools import reduce
    
    nums = [1, 2, 3, 4]
    squares = list(map(lambda x: x**2, nums))         # [1, 4, 9, 16]
    evens = list(filter(lambda x: x % 2 == 0, nums))  # [2, 4]
    total = reduce(lambda x, y: x + y, nums)           # 10
    
  • Gestió de paths amb pathlib

    from pathlib import Path
    
    file = Path("data") / "train.csv"
    if file.exists():
        content = file.read_text()
    
  • Lectura de fitxers amb with

    with open("data.txt") as f:
        for line in f:
            print(line.strip())
    
  • Mòdul csv

    import csv
    with open("data.csv") as f:
        reader = csv.reader(f)
        for row in reader:
            print(row)
    
  • Walrus operator (Python 3.8+)

    while (line := f.readline()):
        process(line)
    

Referències

Last change: , commit: fd407b1