# objectes i referències

Referències en Python

Per què una llista SÍ es pot modificar des de dins d'una funció (i un int no), com retornar diverses dades alhora amb una tupla, i el mateix algorisme d'ordenació que a C — ara amb l'estil propi de Python.

Com funciona Python per sota: objectes i referències

Python no té apuntadors com C, però el mecanisme de sota és semblant: una variable no és una capsa amb un valor a dins, és un nom que apunta a un objecte. El que canvia és si aquell objecte es pot modificar un cop creat.

Dos comportaments diferents

Els tipus immutables (int, float, str, bool, les tuples) no es poden canviar un cop creats: quan "els reassignes" dins d'una funció, en realitat crees un objecte nou i hi apuntes el nom local — per això incrementa(n) (del capítol de funcions) mai tocava la variable original.

Els tipus mutables (les llistes) sí que es poden modificar sense crear-ne cap altre: llista.append(x) o llista[0] = 9 canvien l'objecte mateix. Com que la funció rep una referència al mateix objecte que té el cridant, aquests canvis sí que es veuen des de fora.

Llistes com a paràmetre: per fi, canvis que persisteixen

Exemple

def incrementa_tots(llista):
    for i in range(len(llista)):
        llista[i] = llista[i] + 1

valors = [1, 2, 3]
incrementa_tots(valors)
print(valors)

La sortida és [2, 3, 4]: a diferència de l'exemple amb un int del capítol de funcions, aquí sí que es veu el canvi des de fora. La funció no té una còpia de la llista; té el mateix objecte que valors, només que amb un altre nom (llista) a dins.

Retorn múltiple amb tuples

A C, una funció només pot fer return d'un sol valor (per retornar-ne més calen apuntadors, com fa scanf). Python ho resol de forma molt més directa: empaquetant diverses dades en una tupla.

Exemple

def dividir(a, b):
    return a // b, a % b

quocient, residu = dividir(17, 5)
print(f"{quocient} i {residu}")

return a // b, a % b crea i retorna la tupla (3, 2) en una sola instrucció. Qui crida la funció la pot desempaquetar directament en dues variables, quocient i residu, igual que ja vam fer amb tuples "soltes" al capítol de funcions.

Ordenació: el mètode de la bombolla

Amb llistes com a paràmetre, ja podem escriure una funció que ordeni una llista de debò, modificant-la des de dins — el mateix algorisme que a C.

Exemple

def bombolla(llista):
    n = len(llista)
    for i in range(n - 1):
        for j in range(n - 1 - i):
            if llista[j] > llista[j + 1]:
                llista[j], llista[j + 1] = llista[j + 1], llista[j]

La idea és idèntica a la de C: recórrer la llista comparant parelles consecutives i intercanviar-les si estan desordenades, repetint prou voltes perquè els valors més grans "pugin" cap al final. L'únic que canvia és l'intercanvi: en lloc d'una variable temp, Python el fa en una sola línia amb assignació múltiple — llista[j], llista[j + 1] = llista[j + 1], llista[j].

Bonus: sorted() i .sort()

A la pràctica, rarament escriuràs la teva pròpia ordenació: Python ja en porta. sorted(llista) retorna una llista nova, ordenada, sense tocar l'original; llista.sort() l'ordena in place, mutant-la directament — exactament el mateix mecanisme que acabem de veure amb bombolla.

Mira el programa executar-se pas a pas

Fixa't en com la llista del panell "Llista" va canviant quan la funció hi escriu — la prova que no treballa amb cap còpia.

Referències i retorn múltiple

Llistes i funcions

Codi

Variables

NomValor

Llista

Terminal

Pas 1 / 1

Problemes per resoldre

Sense solució ni comprovació automàtica: escriu el programa, executa'l (python3 programa.py) i prova'l amb els exemples. Les pistes són plegades perquè les obris només si les necessites.

1. Mínim i màxim alhora

Escriu una funció min_max(llista) que retorni, en una sola tupla, el valor mínim i el màxim de la llista. Prova-la amb una llista d'uns quants enters escrits directament al codi.

Mínim: 2, Màxim: 9
Pistes

Comença suposant que tots dos són llista[0], i recorre la resta comparant cada element amb els dos candidats actuals.

return minim, maxim — i, en cridar-la: mn, mx = min_max(valors).

2. Doblar una llista

Escriu una funció dobla(llista) que multipliqui per 2 cada element de la llista, modificant-la directament (sense fer return ni crear-ne cap altra).

Abans:   [1, 2, 3]
Després: [2, 4, 6]
Pistes

Recorre els índexs amb for i in range(len(llista)): i reassigna llista[i] = llista[i] * 2 — igual que incrementa_tots de la teoria.

3. Ordenació per selecció

Escriu una funció seleccio(llista) que ordeni la llista amb un algorisme diferent de la bombolla: a cada passada, busca l'element més petit de la part encara no ordenada i l'intercanvia amb el primer d'aquesta part.

Abans:   [5, 2, 4, 1]
Després: [1, 2, 4, 5]
Pistes

Bucle de fora i de 0 a len(llista)-2: a cada volta, busca l'índex del mínim entre llista[i] i l'últim element (un bucle de dins que guarda l'índex, no el valor).

Un cop trobat l'índex del mínim, intercanvia'l amb llista[i] amb assignació múltiple: llista[i], llista[idx_min] = llista[idx_min], llista[i].

4. Invertir una llista

Escriu una funció invertir(llista) que giri l'ordre dels elements (el primer passa a ser l'últim, i així), modificant-la directament — sense fer servir reverse() ni cap llesca com [::-1].

Abans:   [1, 2, 4, 5]
Després: [5, 4, 2, 1]
Pistes

Amb dos índexs esquerra (comença a 0) i dreta (comença a len(llista) - 1) que s'acosten: intercanvia llista[esquerra] amb llista[dreta] i avança'ls fins que esquerra >= dreta.

L'intercanvi, com sempre en Python: llista[esquerra], llista[dreta] = llista[dreta], llista[esquerra].

Exercicis d'autoavaluació

Aquests es generen amb valors nous a l'atzar i tenen solució comprovable.

—

Quina serà la sortida per terminal d'aquest programa?