Quando pensi ad oggetti all'interno del codice, che siano semplici variabili o liste etc, devi immaginarti che tutti questi oggetti vivono, e vengono manipolati, in memoria, non su disco.
Come è stato già detto nella risposta precedente, di solito una lista è organizzata come una sequenza di indirizzi di memoria (puntatori). Se volessimo trovare un paragone nella vita reale è come se gli elementi della lista fossero 100000 abitazioni e la lista in sé fosse semplicemente un'agenda contenente i 100000 indirizzi di queste abitazioni.
La funzione reverse non fa altro che invertire l'ordine degli indirizzi nella pagina dell'agenda, pertanto, dopo tale operazione, troverai in cima alla pagina l'indirizzo che prima era l'ultimo, come secondo quello che prima era il penultimo, e così via.
Quindi come puoi vedere non ha importanza che le abitazioni siano dei monolocali o delle ville, la tua lista (ammesso che sia stata allocata dinamicamente come nell'implementazione in CPython) è solo la lista di indirizzi.
Edit: mi scuso per aver dato una spiegazione superficiale (dovevo ancora svegliarmi), quello che ho scritto nelle due righe sopra non è accurato. Cerco di trovare il tempo per scrivere un commento corretto più tardi, nel frattempo sarò felice di leggere interventi altrui. :)
Quanto alla terminologia, c'è da stare attenti al tipo di contenitore utilizzato ma in generale credo sia più corretto dire che una sequenza ordinata di elementi può essere identificata con il puntatore (cioè l'indirizzo di memoria) del suo primo elemento. A questo punto diventa facile accedere via indice perché gli indirizzi sono comunque degli interi, quindi per trovare un determinato elemento ti basta fare 'indirizzo + indice'.
Es:
- 1° elemento = indirizzo + 0
- 2° elemento = indirizzo + 1
- etc
Tale aritmetica viene ovviamente mascherata dagli operatori della lista, non facciamo questa operazione a mano in Python.