C'est surtout un choix sémantique.
En C, une variable de type int désigne une case mémoire 32 bits dont on se sert pour stocker un entier compris entre -2^31 et 2^31-1. Quand tu écris i=i+1 tu es en train de changer la valeur stockée dans cette case.
En Python, une variable de type int est un entier. Ce sont les entiers des maths, ceux qu'on connait bien, qui n'ont pas de problème d'overflow. On n'a pas de raison de vouloir modifier l'entier 3, l'entier 3 vaut 3 et on aurait de sérieux soucis si on pouvait changer sa valeur : c'est pour ça que les entiers sont immutables. L'opération '+' prend en entrée deux entiers et renvoie un *nouvel* entier. Quand tu écris i=i+1 en python tu n'es pas en train de modifier l'entier i, tu es en train de calculer un nouvel entier et de l'appeler i. Le fait que tu lui donnes le même nom que ton 'i' précédent est purement accidentel, il n'y a pas de raison que ça se comporte différemment que de faire j=i+1.
Dans la machine, au final tout ça sera bien sûr représenté par des cases mémoires avec un nombre de bits fini, des problèmes d'overflow, etc. Mais ça c'est le travail de l'interpréteur, pas du programmeur. Le programmeur code en ayant la sémantique de python en tête, il s'en fiche de ce qui se passe derrière. En rajoutant cette couche d'abstraction, on perd en rapidité d'exécution comme tu l'as remarqué, mais on gagne en sûreté : on a moins de bugs, et on code plus vite.