Python - write () versus writelines () et chaînes concaténées

124

Alors j'apprends Python. Je suis en train de parcourir les leçons et j'ai rencontré un problème où j'ai dû en condenser un grand nombre target.write()en un seul write(), tout en ayant une "\n"variable d'entrée entre chaque utilisateur (l'objet de write()).

Je suis venu avec:

nl = "\n"
lines = line1, nl, line2, nl, line3, nl
textdoc.writelines(lines)

Si j'essaye de faire:

textdoc.write(lines)

J'obtiens une erreur. Mais si je tape:

textdoc.write(line1 + "\n" + line2 + ....)

Ensuite, cela fonctionne très bien. Pourquoi suis-je incapable d'utiliser une chaîne pour une nouvelle ligne dans write()mais je peux l'utiliser dans writelines()?

Python 2.7 Quand j'ai cherché sur Google, la plupart des ressources que j'ai trouvées étaient bien au-dessus de ma tête, je suis toujours un profane.

AbeLinkon
la source
linesn'est pas une chaîne dans votre exemple. C'est un tuple composé de six chaînes.
Bachsau

Réponses:

147
  • writelines attend un itérable de chaînes
  • write attend une seule chaîne.

line1 + "\n" + line2fusionne ces chaînes en une seule chaîne avant de la transmettre write.

Notez que si vous avez plusieurs lignes, vous pouvez utiliser "\n".join(list_of_lines).

DGH
la source
50
Plus précisément, writelinesattend un itérable. Vous pouvez utiliser une liste, un tuple ou un générateur.
Mark Ransom
Merci pour la réponse monsieur. Je suppose par le nom (list_of_lines) que je devrais faire une liste de chaînes et passer ensuite dans .join (list)?
AbeLinkon le
9
Pourquoi devriez-vous utiliser writeau lieu de writelinessi vous avez plusieurs lignes? Les écritures pourraient être plus performantes car il n'est pas nécessaire de créer une chaîne concaténée temporaire, juste une itération sur les lignes.
Bouke
@ hBy2Py: exactement le contraire: stackoverflow.com/a/6165711/281545
Mr_and_Mrs_D
1
Une seule chaîne est également un itérable en Python
natbusa
123

Pourquoi suis-je incapable d'utiliser une chaîne pour une nouvelle ligne dans write () mais je peux l'utiliser dans writelines ()?

L'idée est la suivante: si vous voulez écrire une seule chaîne, vous pouvez le faire avec write(). Si vous avez une séquence de chaînes, vous pouvez toutes les écrire en utilisant writelines().

write(arg)attend une chaîne comme argument et l'écrit dans le fichier. Si vous fournissez une liste de chaînes, cela lèvera une exception (au fait, montrez-nous les erreurs!).

writelines(arg)attend un itérable comme argument (un objet itérable peut être un tuple, une liste, une chaîne ou un itérateur au sens le plus général). Chaque élément contenu dans l'itérateur doit être une chaîne. Un tuple de chaînes est ce que vous avez fourni, donc les choses ont fonctionné.

La nature de la ou des chaînes n'a pas d'importance pour les deux fonctions, c'est-à-dire qu'elles écrivent simplement dans le fichier tout ce que vous leur fournissez. La partie intéressante est qu'elle writelines()n'ajoute pas de caractères de nouvelle ligne en soi, de sorte que le nom de la méthode peut en fait être assez déroutant. Il se comporte en fait comme une méthode imaginaire appelée write_all_of_these_strings(sequence).

Ce qui suit est une manière idiomatique en Python d'écrire une liste de chaînes dans un fichier tout en gardant chaque chaîne dans sa propre ligne:

lines = ['line1', 'line2']
with open('filename.txt', 'w') as f:
    f.write('\n'.join(lines))

Cela prend soin de fermer le fichier pour vous. La construction '\n'.join(lines)concatène (connecte) les chaînes de la liste lineset utilise le caractère '\ n' comme colle. C'est plus efficace que d'utiliser l' +opérateur.

À partir de la même linesséquence, se terminant avec la même sortie, mais en utilisant writelines():

lines = ['line1', 'line2']
with open('filename.txt', 'w') as f:
    f.writelines("%s\n" % l for l in lines)

Cela utilise une expression de générateur et crée dynamiquement des chaînes terminées par une nouvelle ligne. writelines()itère sur cette séquence de chaînes et écrit chaque élément.

Edit: Un autre point dont vous devez être conscient:

write()et readlines()existait avant a writelines()été introduit. writelines()a été introduit plus tard en tant que contrepartie de readlines(), afin que l'on puisse facilement écrire le contenu du fichier qui vient d'être lu via readlines():

outfile.writelines(infile.readlines())

Vraiment, c'est la principale raison pour laquelle writelinesa un nom si déroutant. Aussi, aujourd'hui, nous ne voulons plus vraiment utiliser cette méthode. readlines()lit le fichier entier dans la mémoire de votre machine avant de writelines()commencer à écrire les données. Tout d'abord, cela peut perdre du temps. Pourquoi ne pas commencer à écrire des parties de données tout en lisant d'autres parties? Mais, plus important encore, cette approche peut être très gourmande en mémoire. Dans un scénario extrême, où le fichier d'entrée est plus volumineux que la mémoire de votre machine, cette approche ne fonctionnera même pas. La solution à ce problème consiste à n'utiliser que des itérateurs. Un exemple de travail:

with open('inputfile') as infile:
    with open('outputfile') as outfile:
        for line in infile:
            outfile.write(line)

Cela lit le fichier d'entrée ligne par ligne. Dès qu'une ligne est lue, cette ligne est écrite dans le fichier de sortie. En termes schématiques, il n'y a toujours qu'une seule ligne en mémoire (par rapport au contenu entier du fichier étant en mémoire dans le cas de l'approche readlines / writelines).

Dr Jan-Philip Gehrcke
la source
5
@AbeLinkon: Je ne soutiendrais pas cette conclusion. write()et writelines()sont fondamentalement équivalents et leur utilisation est également une question de goût personnel. Cependant, il est important de noter que pour une liste extrêmement longue de chaînes (appelées lines), il est moins efficace d'écrire f.write('\n'.join(lines))que for l in line: f.write('%s\n' % l). Dans le premier cas, une chaîne entièrement nouvelle et très longue est créée en mémoire avant de l'écrire. Dans le second cas, les données sont écrites par morceaux.
Dr.Jan-Philip Gehrcke
3
f.write ('\ n'.join (lines)) n'a pas ajouté le dernier nl lorsque je l'ai exécuté.
Jiminion
5
Bien sûr, vous ne feriez pas outf.writelines(inf.readlines())mais plutôt outf.writelines(inf). La fonction que nous ne voulons plus utiliser readlines()ne l' est plus writelines().
moooeeeep
2
@moooeeeep: bien que rien ne cloche avec la fonctionnalité / l'implémentation de writelines(), sa sémantique est, comme expliqué, loin d'être idéale. C'est pourquoi je ne l'ai jamais utilisé. Et je ne l'ai jamais manqué.
Dr.Jan-Philip Gehrcke
2
@AbeLinkon - peut-être devriez-vous envisager d'accepter cette réponse, elle est clairement meilleure que celle que vous avez initialement acceptée
Peter M. - représente Monica le
-4

si vous voulez juste enregistrer et charger une liste, essayez Pickle

Économie de cornichon:

with open("yourFile","wb")as file:
 pickle.dump(YourList,file)

et chargement:

with open("yourFile","rb")as file:
 YourList=pickle.load(file)
Venya
la source
-5

En fait, je pense que le problème est que vos "lignes" variables sont mauvaises. Vous avez défini les lignes comme un tuple, mais je pense que write () nécessite une chaîne. Tout ce que vous avez à changer, ce sont vos virgules en plus (+).

nl = "\n"
lines = line1+nl+line2+nl+line3+nl
textdoc.writelines(lines)

devrait marcher.

Kevin
la source
-5

Exercice 16 du livre de Zed Shaw? Vous pouvez utiliser les caractères d'échappement comme suit:

paragraph1 = "%s \n %s \n %s \n" % (line1, line2, line3)
target.write(paragraph1)
target.close()
Gérald
la source
Solution très faible. Si vous voulez concaténer plusieurs lignes de cette manière, vous devriez le faire comme ceci: " \n ".join((line1, line2, line3)).
Bachsau