Việc đọc toàn bộ tập tin có khiến tập tin xử lý mở không?


372

Nếu bạn đọc toàn bộ tệp với content = open('Path/to/file', 'r').read()phần xử lý tệp còn mở cho đến khi tập lệnh thoát? Có một phương pháp ngắn gọn hơn để đọc toàn bộ tập tin?

Câu trả lời:


585

Câu trả lời cho câu hỏi đó phụ thuộc phần nào vào việc triển khai Python cụ thể.

Để hiểu tất cả những điều này là gì, đặc biệt chú ý đến fileđối tượng thực tế . Trong mã của bạn, đối tượng đó chỉ được đề cập một lần, trong một biểu thức và không thể truy cập ngay sau khi read()cuộc gọi trở lại.

Điều này có nghĩa là đối tượng tập tin là rác. Câu hỏi duy nhất còn lại là "Khi nào thì trình thu gom rác sẽ thu thập đối tượng tệp?".

trong CPython, sử dụng bộ đếm tham chiếu, loại rác này được chú ý ngay lập tức và vì vậy nó sẽ được thu gom ngay lập tức. Điều này thường không đúng với các triển khai trăn khác.

Một giải pháp tốt hơn, để đảm bảo rằng tệp được đóng, là mẫu này:

with open('Path/to/file', 'r') as content_file:
    content = content_file.read()

sẽ luôn đóng tệp ngay sau khi khối kết thúc; ngay cả khi một ngoại lệ xảy ra.

Chỉnh sửa: Để đặt một điểm tốt hơn trên nó:

Khác với file.__exit__(), được gọi là "tự động" trong withcài đặt trình quản lý ngữ cảnh, cách duy nhất khác file.close()được gọi tự động (nghĩa là, ngoài việc tự gọi nó một cách rõ ràng,) là thông qua file.__del__(). Điều này dẫn chúng ta đến câu hỏi khi nào __del__()được gọi?

Một chương trình được viết chính xác không thể cho rằng các trình hoàn thiện sẽ chạy tại bất kỳ thời điểm nào trước khi kết thúc chương trình.

- https://devbloss.microsoft.com/oldnewthing/20100809-00/?p=13203

Đặc biệt:

Đối tượng không bao giờ bị phá hủy rõ ràng; tuy nhiên, khi chúng không thể truy cập được, chúng có thể được thu gom rác. Việc triển khai được phép hoãn thu gom rác hoặc bỏ qua hoàn toàn - đó là vấn đề về chất lượng thực hiện cách thức thu gom rác được thực hiện, miễn là không có đối tượng nào được thu thập mà vẫn có thể truy cập được.

[...]

CPython hiện đang sử dụng sơ đồ đếm tham chiếu với phát hiện chậm (tùy chọn) rác được liên kết theo chu kỳ, thu thập hầu hết các đối tượng ngay khi chúng không thể truy cập được, nhưng không được bảo đảm để thu gom rác chứa các tham chiếu vòng tròn.

- https://docs.python.org/3.5/reference/datamodel.html#objects-values-and-types

(Nhấn mạnh của tôi)

nhưng như nó cho thấy, các triển khai khác có thể có hành vi khác. Ví dụ, PyPy 6 triển khai thu gom rác khác nhau !


24
Trong một thời gian, thực sự không có các triển khai Python khác; nhưng dựa vào chi tiết thực hiện không thực sự là Pythonic.
Karl Knechtel

Nó vẫn còn cụ thể thực hiện, hay đã được chuẩn hóa? Không gọi __exit__()trong trường hợp như vậy nghe có vẻ như một lỗ hổng thiết kế.
rr-

2
@jgmjgm Chính xác là do 3 vấn đề đó, GC không thể đoán trước được, try/ finallykhó hiểu và vô dụng rất phổ biến đối với các trình xử lý dọn dẹp đã withgiải quyết. Sự khác biệt giữa "đóng rõ ràng" và "quản lý với with" là trình xử lý thoát được gọi ngay cả khi ném ngoại lệ. Bạn có thể đặt close()một finallymệnh đề, nhưng điều đó không khác nhiều so với việc sử dụng withthay vào đó, một chút lộn xộn hơn (3 dòng thêm thay vì 1) và khó hơn một chút để làm cho đúng.
Độc thân Tăng tốc

1
Điều tôi không hiểu về điều đó là tại sao 'với' sẽ còn đáng tin cậy hơn vì nó cũng không rõ ràng. Có phải bởi vì thông số kỹ thuật nói rằng nó phải làm điều đó luôn luôn được thực hiện như vậy?
jgmjgm

3
@jgmjgm nó vì đáng tin cậy hơn with foo() as f: [...]về cơ bản là giống như f = foo(), f.__enter__()[...] và f.__exit__() với ngoại lệ xử lý , do đó __exit__luôn gọi. Vì vậy, các tập tin luôn luôn được đóng lại.
neingeist

104

Bạn có thể sử dụng pathlib .

Đối với Python 3.5 trở lên:

from pathlib import Path
contents = Path(file_path).read_text()

Đối với các phiên bản cũ hơn của Python, hãy sử dụng pathlib2 :

$ pip install pathlib2

Sau đó:

from pathlib2 import Path
contents = Path(file_path).read_text()

Đây là thực read_text hiện thực tế :

def read_text(self, encoding=None, errors=None):
    """
    Open the file in text mode, read it, and close the file.
    """
    with self.open(mode='r', encoding=encoding, errors=errors) as f:
        return f.read()

2

Chà, nếu bạn phải đọc từng dòng tệp để làm việc với từng dòng, bạn có thể sử dụng

with open('Path/to/file', 'r') as f:
    s = f.readline()
    while s:
        # do whatever you want to
        s = f.readline()

Hoặc thậm chí cách tốt hơn:

with open('Path/to/file') as f:
    for line in f:
        # do whatever you want to

0

Thay vì truy xuất nội dung tệp dưới dạng một chuỗi, có thể thuận tiện để lưu trữ nội dung dưới dạng danh sách tất cả các dòng mà tệp bao gồm :

with open('Path/to/file', 'r') as content_file:
    content_list = content_file.read().strip().split("\n")

Có thể thấy, người ta cần thêm các phương thức nối .strip().split("\n")vào câu trả lời chính trong chuỗi này .

Ở đây, .strip()chỉ cần xóa các khoảng trắng và ký tự dòng mới ở phần cuối của toàn bộ chuỗi tệp và .split("\n")tạo danh sách thực tế thông qua việc tách toàn bộ chuỗi tệp ở mỗi ký tự dòng mới \ n .

Hơn nữa, theo cách này, toàn bộ nội dung tệp có thể được lưu trữ trong một biến, có thể được mong muốn trong một số trường hợp, thay vì lặp qua từng dòng tệp như được chỉ ra trong câu trả lời trước này .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.