Question Python writelines () et write () énorme différence de temps


Je travaillais sur un script qui lit un dossier de fichiers (chacun d'une taille allant de 20 Mo à 100 Mo), modifie certaines données dans chaque ligne et les réécrit sur une copie du fichier.

with open(inputPath, 'r+') as myRead:
     my_list = myRead.readlines()
     new_my_list = clean_data(my_list)
with open(outPath, 'w+') as myWrite:
     tempT = time.time()
     myWrite.writelines('\n'.join(new_my_list) + '\n')
     print(time.time() - tempT)
print(inputPath, 'Cleaning Complete.')

En exécutant ce code avec un fichier de 90 Mo (~ 900 000 lignes), il imprimait 140 secondes comme le temps nécessaire pour écrire dans le fichier. Ici j'ai utilisé writelines(). Donc, j'ai cherché différentes façons d'améliorer la vitesse d'écriture des fichiers, et dans la plupart des articles que j'ai lus, il était dit: write() et writelines() ne devrait pas montrer de différence puisque j'écris une seule chaîne concaténée. J'ai également vérifié le temps pris pour la phrase suivante:

new_string = '\n'.join(new_my_list) + '\n'

Et cela n'a pris que 0,4 seconde, donc le grand temps pris n'était pas dû à la création de la liste. Juste pour essayer write() J'ai essayé ce code:

with open(inputPath, 'r+') as myRead:
     my_list = myRead.readlines()
     new_my_list = clean_data(my_list)
with open(outPath, 'w+') as myWrite:
     tempT = time.time()
     myWrite.write('\n'.join(new_my_list) + '\n')
     print(time.time() - tempT)
print(inputPath, 'Cleaning Complete.')

Et il a imprimé 2,5 secondes. Pourquoi y a-t-il une si grande différence dans le temps d'écriture des fichiers pour write() et writelines() même si ce sont les mêmes données? Est-ce un comportement normal ou y a-t-il quelque chose qui ne va pas dans mon code? Le fichier de sortie semble être le même pour les deux cas, donc je sais qu'il n'y a pas de perte de données.


22
2018-06-15 06:56


origine


Réponses:


file.writelines() attend un itérable de chaînes. Il passe ensuite en boucle et appelle file.write() pour chaque chaîne dans l'itérable. En Python, la méthode fait ceci:

def writelines(self, lines)
    for line in lines:
        self.write(line)

Vous passez une seule grande chaîne et une chaîne est une itération de chaînes. En itérant vous obtenez caractères individuels, des chaînes de longueur 1. Donc, en fait, vous faites len(data) appels séparés à file.write(). Et c'est lent, car vous construisez un tampon d'écriture d'un seul caractère à la fois.

Ne pas passer une seule chaîne à file.writelines(). Passez une liste ou un tuple ou autre itérable à la place.

Vous pouvez envoyer des lignes individuelles avec une nouvelle ligne ajoutée dans une expression de générateur, par exemple:

 myWrite.writelines(line + '\n' for line in new_my_list)

Maintenant, si vous pouviez faire clean_data() une GénérateurEn générant des lignes nettoyées, vous pouvez transférer des données du fichier d'entrée, via votre générateur de nettoyage de données, vers le fichier de sortie sans utiliser plus de mémoire que nécessaire pour les tampons de lecture et d'écriture. :

with open(inputPath, 'r+') as myRead, open(outPath, 'w+') as myWrite:
    myWrite.writelines(line + '\n' for line in clean_data(myRead))

En outre, je considérerais la mise à jour clean_data() pour émettre des lignes avec des nouvelles lignes incluses.


38
2018-06-15 07:04



en complément de la réponse de Martijn, le mieux serait d'éviter de construire la liste en utilisant join en premier lieu

Il suffit de passer une compréhension de générateur à writelines, en ajoutant la nouvelle ligne à la fin: pas d'allocation de mémoire inutile et pas de boucle (en plus de la compréhension)

myWrite.writelines("{}\n".format(x) for x in my_list)

5
2018-06-15 07:06



La méthode 'write (arg)' attend que string soit son argument. Donc, une fois qu'il appelle, il écrit directement. c'est la raison pour laquelle c'est beaucoup plus rapide. où comme si vous utilisez writelines() méthode, il attend la liste des chaînes comme itérateur. donc même si vous envoyez des données à writelines, il suppose qu'il a un itérateur et qu'il essaie de l'itérer. Comme il s'agit d'un itérateur, il faudra un certain temps pour l'itérer et l'écrire.

Est-ce clair ?


2
2018-06-15 07:03