หากคุณอ่านไฟล์ทั้งหมดด้วยcontent = open('Path/to/file', 'r').read()
จะมีการจัดการไฟล์เปิดค้างไว้จนกว่าสคริปต์จะออก? มีวิธีรัดกุมกว่าในการอ่านไฟล์ทั้งหมดหรือไม่?
หากคุณอ่านไฟล์ทั้งหมดด้วยcontent = open('Path/to/file', 'r').read()
จะมีการจัดการไฟล์เปิดค้างไว้จนกว่าสคริปต์จะออก? มีวิธีรัดกุมกว่าในการอ่านไฟล์ทั้งหมดหรือไม่?
คำตอบ:
คำตอบสำหรับคำถามนั้นขึ้นอยู่กับการใช้งานของ Python
เพื่อให้เข้าใจสิ่งที่เป็นข้อมูลเกี่ยวกับสิ่งนี้ให้ความสนใจเป็นพิเศษกับfile
วัตถุที่เกิดขึ้นจริง ในรหัสของคุณวัตถุนั้นถูกกล่าวถึงเพียงครั้งเดียวในการแสดงออกและจะไม่สามารถเข้าถึงได้ทันทีหลังจากที่read()
โทรกลับ
ซึ่งหมายความว่าวัตถุไฟล์เป็นขยะ คำถามเดียวที่เหลืออยู่คือ "เมื่อใดที่ตัวรวบรวมข้อมูลขยะจะรวบรวมวัตถุไฟล์?"
ใน CPython ซึ่งใช้ตัวนับอ้างอิงจะสังเกตเห็นขยะชนิดนี้ทันทีและจะถูกรวบรวมทันที นี่ไม่ใช่ความจริงของการใช้งานของงูหลามอื่น ๆ
ทางออกที่ดีกว่าเพื่อให้แน่ใจว่าไฟล์ถูกปิดเป็นรูปแบบนี้:
with open('Path/to/file', 'r') as content_file:
content = content_file.read()
ซึ่งจะปิดไฟล์ทันทีหลังจากบล็อกสิ้นสุด แม้ว่าจะมีข้อยกเว้นเกิดขึ้น
แก้ไข: หากต้องการวางจุดปลีกย่อยบน:
อื่น ๆ กว่าfile.__exit__()
ซึ่งก็คือ "อัตโนมัติ" เรียกว่าในwith
การตั้งค่าผู้จัดการบริบทวิธีเดียวที่อื่น ๆ ที่file.close()
มีการเรียกโดยอัตโนมัติ (นั่นคืออื่น ๆ กว่าอย่างชัดเจนเรียกมันด้วยตัวคุณเอง) file.__del__()
ผ่านทาง สิ่งนี้ทำให้เรามีคำถาม__del__()
ว่าจะได้รับการเรียกเมื่อใด
โปรแกรมที่เขียนอย่างถูกต้องไม่สามารถสรุปได้ว่า finalizers จะทำงานที่จุดใด ๆ ก่อนที่จะมีการยกเลิกโปรแกรม
- https://devblogs.microsoft.com/oldnewthing/20100809-00/?p=13203
โดยเฉพาะอย่างยิ่ง:
วัตถุจะไม่ถูกทำลายอย่างชัดเจน อย่างไรก็ตามเมื่อพวกเขาไม่สามารถเข้าถึงพวกเขาอาจถูกเก็บขยะ อนุญาตให้มีการนำไปใช้งานเพื่อเลื่อนการรวบรวมขยะหรือละเว้นไปพร้อมกัน - มันเป็นเรื่องของคุณภาพการใช้งานว่าการเก็บรวบรวมขยะถูกนำไปใช้อย่างไรตราบใดที่ไม่มีการรวบรวมวัตถุที่ยังสามารถเข้าถึงได้
[ ... ]
ปัจจุบัน CPython ใช้รูปแบบการนับการอ้างอิงด้วยการตรวจจับแบบล่าช้า (เป็นทางเลือก) ของขยะที่เชื่อมโยงแบบวนรอบซึ่งรวบรวมวัตถุส่วนใหญ่ทันทีที่เข้าไม่ถึง แต่ไม่รับประกันว่าจะรวบรวมขยะที่มีการอ้างอิงแบบวงกลม
- https://docs.python.org/3.5/reference/datamodel.html#objects-values-and-types
(เน้นเหมือง)
แต่ตามที่แนะนำการใช้งานอื่นอาจมีพฤติกรรมอื่น ตัวอย่างเช่น PyPy มีการใช้งานการรวบรวมขยะ6แบบ !
__exit__()
ในกรณีเช่นนี้ดูเหมือนข้อบกพร่องในการออกแบบ
try
/ finally
เป็นเรื่องตลกและเป็นประโยชน์อย่างมากของตัวจัดการล้างข้อมูลที่with
แก้ปัญหาได้ ความแตกต่างระหว่าง "การปิดอย่างชัดเจน" และ "การจัดการกับwith
" คือตัวจัดการทางออกเรียกว่าแม้ว่าจะเกิดข้อยกเว้น คุณสามารถใส่close()
ในfinally
ข้อ แต่ที่ไม่ได้แตกต่างกันมากจากการใช้with
แทนเป็น Messier บิต (3 สายพิเศษแทน 1) และเล็ก ๆ น้อย ๆ ยากที่จะได้รับเพียงขวา
with foo() as f: [...]
เป็นพื้นเดียวกับf = foo()
, f.__enter__()
[ ... ] และf.__exit__()
มีข้อยกเว้นการจัดการเพื่อให้__exit__
เรียกว่าเสมอ ดังนั้นไฟล์จะถูกปิดเสมอ
สำหรับ Python 3.5 และสูงกว่า:
from pathlib import Path
contents = Path(file_path).read_text()
สำหรับ Python เวอร์ชันเก่าให้ใช้pathlib2 :
$ pip install pathlib2
แล้ว:
from pathlib2 import Path
contents = Path(file_path).read_text()
นี่คือการread_text
ใช้งานจริง:
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()
ถ้าคุณต้องอ่านไฟล์ทีละบรรทัดเพื่อทำงานกับแต่ละบรรทัดคุณสามารถใช้
with open('Path/to/file', 'r') as f:
s = f.readline()
while s:
# do whatever you want to
s = f.readline()
หรือวิธีที่ดีกว่า:
with open('Path/to/file') as f:
for line in f:
# do whatever you want to
แทนที่จะเรียกเนื้อหาไฟล์เป็นสตริงเดียวมันจะสะดวกในการจัดเก็บเนื้อหาเป็นรายการของทุกบรรทัดที่ไฟล์ประกอบด้วย :
with open('Path/to/file', 'r') as content_file:
content_list = content_file.read().strip().split("\n")
ตามที่สามารถเห็นได้เราจำเป็นต้องเพิ่มวิธีการต่อข้อมูล.strip().split("\n")
เข้ากับคำตอบหลักในชุดข้อความนี้
นี่.strip()
เป็นเพียงแค่การลบช่องว่างและตัวอักษรขึ้นบรรทัดใหม่ในตอนจบของสตริงไฟล์ทั้งหมดและ.split("\n")
ผลิตรายการที่เกิดขึ้นจริงผ่านทางแยกสตริงไฟล์ทั้งหมดในทุกตัวอักษรขึ้นบรรทัดใหม่ \ n
นอกจากนี้วิธีนี้เนื้อหาไฟล์ทั้งหมดจะถูกเก็บไว้ในตัวแปรซึ่งอาจเป็นที่ต้องการในบางกรณีแทนการวนลูปผ่านสายแฟ้มโดยสายชี้ให้เห็นในคำตอบก่อนหน้านี้