35 votes

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

Je travaille sur un script qui la lecture d'un dossier de fichiers(chaque taille allant de 20 MO à 100 MO), modifie certaines données dans chaque ligne, et é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.')

Sur l'exécution de ce code avec un 90 MB fichier (~de 900 000 lignes), c'imprimé 140 secondes que le temps consacré à l'écriture dans le fichier. Ici, j'ai utilisé writelines(). J'ai donc cherché des moyens différents pour permettre d'améliorer la vitesse d'écriture, et dans la plupart des articles que j'ai lu, il dit write() et writelines() ne doit pas présenter de différence depuis que je suis en train d'écrire une seule chaîne concaténée. J'ai aussi vérifié le temps pris pour seulement la suite de la déclaration:

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

Et il a fallu que de 0,4 seconde, de sorte que le grand a pris le temps n'était pas à cause de 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 c'imprimé 2,5 secondes. Pourquoi il y a une grande différence dans l'écriture de fichier temps pour write() et writelines() , même si c'est les mêmes données? Est-ce un comportement normal ou est-il quelque chose qui cloche dans mon code? Le fichier de sortie semble être la même dans les deux cas, donc je sais qu'il n'y a pas de perte de données.

56voto

Martijn Pieters Points 271458

file.writelines() s'attend à un objet iterable de chaînes de caractères. Il procède ensuite à boucle et appelez - file.write() de chaque chaîne dans le itératif. En Python, la méthode fait ceci:

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

Vous êtes de passage dans une seule grande chaîne, et une chaîne de caractères est un objet iterable également des chaînes de caractères. Lors de l'itération, vous obtenez des caractères individuels, des chaînes de longueur 1. Donc, en effet vous rendant len(data) des appels distincts à l' file.write(). Et c'est lent, parce que vous êtes la construction d'une mémoire tampon d'écriture d'un caractère à la fois.

Ne passe pas dans une seule chaîne d' file.writelines(). Passer dans une liste ou un tuple ou d'un autre objet iterable à la place.

Vous pourriez les envoyer dans des lignes individuelles avec ajout de saut de ligne dans un générateur d'expression, par exemple:

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

Maintenant, si vous pouviez faire clean_data() un générateur, produisant nettoyé lignes, vous pourriez flux de données depuis le fichier d'entrée, par le biais de votre nettoyage des données générateur, et pour le fichier de sortie sans utiliser plus de mémoire que ce qui est nécessaire pour la lecture et l'écriture des mémoires tampons et cependant beaucoup de l'état est nécessaire pour nettoyer vos lignes:

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

En plus, j'avais envisager de mettre à jour clean_data() d'émettre des lignes avec des retours à la ligne inclus.

7voto

Jean-François Fabre Points 94672

en complément de Martijn réponse, le meilleur moyen serait d'éviter de créer la liste à l'aide de join à la première place

Il suffit de passer un générateur de compréhension writelines, l'ajout de la nouvelle ligne à la fin: pas de superflu de l'allocation de mémoire et pas de boucle (en plus de la compréhension)

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

2voto

nanithehaddock Points 48

"ecrire(arg)' méthode attend chaîne en argument. Donc, une fois qu'il appelle, il va directement à l'écrit. c'est la raison pour laquelle il est beaucoup plus rapide. si vous utilisez writelines() méthode, elle attend une liste de chaîne de caractères comme itérateur. donc, même si vous envoyez des données à l' writelines, il suppose qu'il a obtenu itérateur et il essaie de faire une itération sur elle. donc, puisque c'est un itérateur il faudra un certain temps pour parcourir et de les écrire.

Est-ce clair ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X