Pourquoi dois-je créer une chaîne vide pour remplacer tous les caractères d'une chaîne?

J'écris une fonction ROT13, et je ne peux pas comprendre pourquoi ce qui suit ne fonctionne pas:

def ROT(string):
    # y = 0
    for char in string:
        x = ord(char)
        if 97 <= x < 110 or 65 <= x < 78:
            # string[y]=char.replace(char, chr(x+13))
            char=char.replace(char, chr(x+13))
            print(char)
            # y+=1
        elif x >= 110 or 78 <= x < 91:
            # string[y]=char.replace(char, chr(x-13))
            char=char.replace(char, chr(x-13))
            print(char)
            # y+=1
    return string

string = ROT('Hello, lorem ipsum dolor sit amet')
print(string)

L'appel à la fonction imprime simplement la chaîne d'origine. Comme vous pouvez le voir dans les lignes commentées ci-dessus (excuses si c'est un peu difficile à lire), j'ai essayé de définir une variable y pour incrémenter à travers la chaîne, puis y accéder mais j'ai une erreur d'exécution. La solution que j'ai trouvée était de créer une chaîne vide au début de la fonction (et de mon googling qui semble être la solution que la plupart des gens utilisent), mais personne n'explique pourquoi. Pourquoi ne fonctionne-t-il pas pour renvoyer la chaîne d'origine si vous remplacez tous les caractères qu'elle contient?

3
Quelle est l'erreur exacte que vous obtenez?
ajouté l'auteur thumbtackthief, source

4 Réponses

Le problème dans votre code est que vous ne manipulez pas la chaîne d'origine. Vous remplacez simplement la variable temporaire char et non la chaîne d'origine. Puisque les chaînes sont immuables dans python, vous pouvez essayer d'utiliser une nouvelle chaîne et au lieu de remplacer la chaîne d'origine, vous pouvez simplement ajouter les caractères à la nouvelle chaîne. Comme:

modified_string = ""
for char in string:
     #whatever condition
     modified_string += #value to be added
4
ajouté
Si la performance est un problème, c'est un idiome à éviter dans python car il a une complexité de temps quadratique.
ajouté l'auteur mgilson, source
modified_string + = ... dans une boucle.
ajouté l'auteur mgilson, source
@AswinMurugesh - J'ai posté une autre réponse ci-dessous. Vous pouvez utiliser un bytearray qui se comporte plus ou moins comme un hybride string-string mutable. Ou, comme je travaille dans ma réponse, vous pouvez utiliser string.translate .
ajouté l'auteur mgilson, source
@AswinMurugesh - Oui, string.translate n'est pas la fonction la plus populaire. Mais l'idée n'est pas vraiment difficile. Vous créez 2 chaînes - vous traduisez les caractères dans la chaîne de fromchr au caractère correspondant dans tochr ...
ajouté l'auteur mgilson, source
Vous ne pouvez pas faire de telles choses dans python .. peut être laissé temp = string + char .. Vérifiez la condition .. S'il satisfait, alors assignez string = temp
ajouté l'auteur Aswin Murugesh, source
@mgilson: lequel?
ajouté l'auteur Aswin Murugesh, source
@mgilson toute autre manière optimale?
ajouté l'auteur Aswin Murugesh, source
@mgilson: c'est bon .. mais trop dur pour un débutant à comprendre si ..
ajouté l'auteur Aswin Murugesh, source
Ah je comprends. Si je faisais quelque chose comme string = string + char dans mes instructions if cela fonctionnerait-il?
ajouté l'auteur ekrah, source

Vous retourniez la chaîne d'origine, essayez

def ROT(string):
#   y = 0
    result = ""
    for char in string:
        x = ord(char)
        if 97 <= x < 110 or 65 <= x < 78:
#           string[y]=char.replace(char, chr(x+13))
            char=char.replace(char, chr(x+13))
            result = result + char
            print(char)
            continue
#           y+=1
        elif x >= 110 or 78 <= x < 91:
#           string[y]=char.replace(char, chr(x-13))
            char=char.replace(char, chr(x-13))
            print(char)
            result = result + char
            continue
#           y+=1
        result = result + char
    return result

string = ROT('Hello, lorem ipsum dolor sit amet')
print(string)
2
ajouté

Les chaînes en Python sont immuables.

2
ajouté
c'est la réponse réelle à la question imho: P
ajouté l'auteur Joran Beasley, source

D'autres ont déjà abordé le problème principal - les chaînes sont immuables, donc vous ne pouvez pas simplement changer un seul caractère pendant que vous itérez. Vous pouvez utiliser un bytearray à la place, mais ...

FWIW, c'est un bon candidat pour string.translate :

>>> import string
>>> fromchr = ''.join(chr(x) for x in range(97, 110) + range(65, 78))
>>> tochr = ''.join(chr(x+13) for x in range(97, 110) + range(65, 78))
>>> fromchr += ''.join(chr(x) for x in range(110, 256) + range(78, 91))
>>> tochr += ''.join(chr(x-13) for x in range(110, 256) + range(78, 91))
>>> trans = string.maketrans(fromchr, tochr)
>>> 'Hello, lorem ipsum dolor sit amet'.translate(trans)
'Uryyb, yberz vcfhz qbybe fvg nzrg'

La grande chose ici est que la création de la table de traduction est un coût 1 fois. Une fois la table de traduction créée, vous pouvez l'utiliser autant de fois que vous le souhaitez. Votre traduction se fera en ~ O (n) temps en code C optimisé, donc je serais surpris si vous pouviez obtenir une implémentation beaucoup plus rapide (ou plus simple).

De cette façon, même bat le codec intégré 'rot13' :

def rot13a(s):
  return s.encode('rot13')

import string
fromchr = ''.join([chr(x) for x in range(97, 110) + range(65, 78)])
tochr = ''.join([chr(x+13) for x in range(97, 110) + range(65, 78)])
fromchr += ''.join(chr(x) for x in range(110, 256) + range(78, 91))
tochr += ''.join(chr(x-13) for x in range(110, 256) + range(78, 91))
trans = string.maketrans(fromchr, tochr)
def rot13b(s):
  return s.translate(trans)

import timeit
test_string = 'Hello, lorem ipsum dolor sit amet'
print rot13a(test_string) == rot13b(test_string)
print timeit.timeit("rot13a(test_string)", "from __main__ import test_string, rot13a")
print timeit.timeit("rot13b(test_string)", "from __main__ import test_string, rot13b")

(mes résultats):

True
1.52055001259  # rot13a
0.21444106102  # rot13b

Notez qu'il s'agit d'un code python2.x. Dans python3.x, vous ne pouvez pas simplement ajouter les plages car range ne renvoie plus un objet list . Mais, espérons que l'idée est assez claire ...

1
ajouté
Ya maintenant je l'obtiens clairement .. Belle fonction .. En regardant la première fois ..
ajouté l'auteur Aswin Murugesh, source