2017-06-15 15 views
20

Tôi đang làm việc trên một tập lệnh đọc một thư mục tệp (mỗi kích thước từ 20 MB đến 100 MB), sửa đổi một số dữ liệu trong mỗi dòng và ghi lại một bản sao của tập tin.Python writelines() và write() sự khác biệt lớn về thời gian

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.') 

Khi chạy mã này với một tệp 90 MB (~ 900.000 dòng), nó in 140 giây khi mất thời gian để ghi vào tệp. Ở đây tôi đã sử dụng writelines(). Vì vậy, tôi đã tìm kiếm các cách khác nhau để cải thiện tốc độ ghi tệp và trong hầu hết các bài viết mà tôi đọc, nó cho biết write()writelines() không hiển thị bất kỳ sự khác biệt nào kể từ khi tôi viết một chuỗi nối. Tôi cũng đã kiểm tra thời gian thực hiện chỉ các tuyên bố sau:

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

Và nó chỉ mất 0,4 giây, vì vậy thời gian lớn chụp là không phải vì tạo danh sách. Chỉ cần thử write() Tôi đã thử mã này:

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.') 

Và nó in 2,5 giây. Tại sao có sự khác biệt lớn trong thời gian ghi tệp cho write()writelines() mặc dù dữ liệu đó giống nhau? Đây có phải là hành vi bình thường hoặc có điều gì đó sai trong mã của tôi không? Các tập tin đầu ra có vẻ giống nhau cho cả hai trường hợp, vì vậy tôi biết rằng không có mất dữ liệu.

+2

upvote cho việc tìm cách xoắn sử dụng writelines với kết quả mong đợi và tìm thấy cảnh báo bất ngờ. –

+0

Ngoài ra, hàm clean_data() của tôi cũng sẽ xóa từng hàng, vì vậy các dòng mới sẽ bị loại bỏ. –

Trả lời

37

file.writelines() mong đợi một số có thể lặp lại của chuỗi. Sau đó nó tiến hành lặp lại và gọi file.write() cho mỗi chuỗi trong vòng lặp. Trong Python, phương thức thực hiện điều này:

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

Bạn đang chuyển một chuỗi lớn và chuỗi cũng có thể lặp lại. Khi lặp lại, bạn nhận được các ký tự riêng lẻ, các chuỗi có độ dài 1. Vì vậy, có hiệu lực bạn đang thực hiện len(data) các cuộc gọi riêng lẻ đến file.write(). Và đó là chậm, bởi vì bạn đang xây dựng một bộ đệm ghi một nhân vật duy nhất tại một thời điểm.

Không chuyển một chuỗi đơn đến file.writelines(). Thay vào đó, hãy chuyển một danh sách hoặc tuple hoặc các biến khác.

Bạn có thể gửi theo đường cá nhân bằng dòng mới được thêm vào trong một biểu thức máy phát điện, ví dụ:

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

Bây giờ, nếu bạn có thể làm clean_data() một phát, năng suất dây chuyền làm sạch, bạn có thể truyền dữ liệu từ tệp đầu vào, thông qua trình tạo dữ liệu của bạn, và ra tệp đầu ra mà không cần sử dụng bộ nhớ nào nhiều hơn mức cần thiết cho bộ đệm đọc và ghi và nhiều trạng thái cần thiết để làm sạch các dòng của bạn:

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

Ngoài ra, tôi muốn xem xét cập nhật clean_data() để phát ra các dòng có dòng mới được bao gồm.

+0

'myWrite.writelines ('\ n'.join (my_list) +' \ n ')' chỉ có thể là 'myWrite.writelines (" {} \ n ".format (x) cho x trong my_list)' do đó sẽ thậm chí còn nhanh hơn; không có danh sách để xây dựng. –

+0

@ Jean-FrançoisFabre: đó là lý do tôi chuyển vào danh sách hoặc bộ dữ liệu * hoặc lặp lại * khác. :-) –

+0

@ Jean-FrançoisFabre: nó có thể chỉ là một biện pháp tiết kiệm bộ nhớ, vì bộ đệm vẫn nối các đường đó cho đến khi nó đầy. Nó sẽ giúp nếu 'clean_data()' là một trình tạo. –

2

phương thức 'write (arg)' mong đợi chuỗi là đối số của nó. Vì vậy, một khi nó gọi, nó sẽ trực tiếp viết. đây là lý do nó nhanh hơn nhiều. nơi như thể bạn đang sử dụng phương thức writelines(), nó sẽ chờ danh sách chuỗi như trình lặp. vì vậy ngay cả khi bạn đang gửi dữ liệu đến writelines, nó giả định rằng nó có trình lặp và nó cố gắng lặp lại nó. vì nó là một trình lặp, nó sẽ mất một thời gian để lặp lại và viết nó.

Điều đó có rõ ràng không?

+0

Nhưng nó vẫn là một chuỗi duy nhất phải không? Nó sẽ lặp lại trên 1 giá trị? Điều đó sẽ ảnh hưởng đến tốc độ ghi như thế nào? –

+1

Vâng, bạn có thể muốn đề xuất một cái gì đó như 'myWrite.writelines (['\ n'.join (my_list) +' \ n '])' – mgilson

+3

@ArjunBalgovind: một chuỗi duy nhất là một ký tự lặp lại riêng biệt. –

5

như một bổ sung cho Martijn câu trả lời, cách tốt nhất là nên tránh để xây dựng danh sách sử dụng join ở nơi đầu tiên

Chỉ cần vượt qua một sự hiểu biết máy phát điện để writelines, thêm xuống dòng cuối cùng: không có bộ nhớ không cần thiết phân bổ và không có vòng lặp (ngoài việc hiểu)

myWrite.writelines("{}\n".format(x) for x in my_list) 
Các vấn đề liên quan