เปิดอ่านและปิดไฟล์ในโค้ด 1 บรรทัด


129

ตอนนี้ฉันใช้:

pageHeadSectionFile = open('pagehead.section.htm','r')
output = pageHeadSectionFile.read()
pageHeadSectionFile.close()

แต่เพื่อให้โค้ดดูดีขึ้นฉันสามารถทำได้:

output = open('pagehead.section.htm','r').read()

เมื่อใช้ไวยากรณ์ข้างต้นฉันจะปิดไฟล์เพื่อเพิ่มทรัพยากรระบบได้อย่างไร


19
ไม่มีอะไรที่น่าสนใจมากขึ้นเกี่ยวกับ one-liners โค้ดถูกอ่านบ่อยกว่าที่เขียนและควรเขียนเพื่อความเข้าใจไม่ใช่เพื่อ "ความเท่" มีข้อยกเว้นอย่างเดียวคือเมื่อมีสำนวนที่เป็นที่รู้จักกันดีในภาษาหนึ่ง ๆ แต่ในกรณีนี้ฉันไม่รู้
drdwilcox

17
@drdwilcox: หนึ่งสมุทรที่เป็นความลับไม่ดีหนึ่งสมุทรที่ประกาศเป็นสิ่งที่ดี ไม่มีเหตุผลใด ๆ (อย่างน้อยฉันก็มองไม่เห็น) เหตุใดจึงไม่มีฟังก์ชัน wrapper ในแกนกลางเพื่ออ่านไฟล์ (ความต้องการทั่วไปดังกล่าว) ในการเรียกใช้ฟังก์ชันเดียว บางอย่างเช่นcontents = os.readfile(path). ถ้าฉันอยากทำอะไรที่with open(path) as fd: contents = fd.read()แปลกใหม่กว่านี้ก็โอเคฉันจะใช้อย่างมีความสุข แน่นอนว่าเราสามารถเขียนกระดาษห่อหุ้มของตัวเองได้ แต่นั่นคือสิ่งที่เป็นแกนหลักเพื่อให้ประโยชน์กับนามธรรมแก่โปรแกรมเมอร์
tokland

5
เป็นความจริงที่ว่าโค้ดอ่านได้ไกลกว่าที่เขียนไว้ แต่ความหมายที่ว่าโค้ดที่ยาวขึ้นนั้นดีพอ ๆ กับโค้ดสั้น ๆ ก็ไม่ผิดไปกว่านี้ หากคุณใช้เวลาในการสร้างโค้ดให้สั้นที่สุด (โดยไม่ต้องใช้กลอุบายอันชาญฉลาดที่เข้าใจยาก) การลงทุนนั้นจะจ่ายผลตอบแทนซ้ำแล้วซ้ำเล่าเมื่ออ่านโค้ด ทุกบรรทัดที่คุณเขียนเป็นการสร้างความเสียหายให้กับทุกคนที่อ่านโค้ดของคุณดังนั้นคุณควรพยายามเขียนให้น้อยที่สุด จำคำพูดที่มีชื่อเสียงจาก Pascal: "ฉันเขียนจดหมายฉบับนี้ให้ยาวขึ้นเพียงเพราะฉันไม่มีเวลาว่างที่จะทำให้สั้นลง"
John Williams

คำตอบ:


195

คุณไม่จำเป็นต้องปิดมันจริงๆ - Python จะทำโดยอัตโนมัติระหว่างการรวบรวมขยะหรือเมื่อออกจากโปรแกรม แต่อย่างที่ @delnan ตั้งข้อสังเกตว่าควรปิดอย่างชัดเจนด้วยเหตุผลหลายประการ

ดังนั้นสิ่งที่คุณสามารถทำได้เพื่อให้สั้นง่ายและชัดเจน:

with open('pagehead.section.htm','r') as f:
    output = f.read()

ตอนนี้มันเป็นแค่สองบรรทัดและฉันคิดว่าอ่านได้สวย


2
@ 1qazxsw2 หากคุณใช้withคำสั่งทรัพยากรไฟล์จะถูกปิดอย่างเหมาะสมสำหรับคุณ
David Alber

13
Re ประโยคแรก: งูหลามจะปิดมันในที่สุด แต่ไม่ได้หมายความว่าคุณควรลืมปิด แม้จะมีการอ้างอิงซ้ำไฟล์ก็อาจเปิดได้นานกว่าที่คุณคิดและต้องการ (เช่นถ้ามันถูกอ้างถึงโดยรอบ) สิ่งนี้เกิดขึ้นสามครั้งในการใช้งาน Python ที่มี GC ที่เหมาะสมโดยที่คุณไม่สามารถรับประกันได้ว่า GC จะมีอะไรในช่วงเวลาใดเวลาหนึ่ง แม้แต่เอกสาร CPython ก็บอกว่าคุณไม่ควรพึ่งพา GC ในการล้างข้อมูลเช่นนี้ คำตอบส่วนหลังควรเป็นตัวหนา

6
หากคุณต้องการซับหนึ่งเส้นจริงๆคุณสามารถวางชิ้นoutput = f.read()ส่วนไว้ในบรรทัดเดียวกันหลังไฟล์:.
Karl Knechtel

1
"เปิดอ่านและปิดไฟล์ในโค้ด 1 บรรทัด" นี่คือสองบรรทัดและไม่ตอบคำถาม
user5359531

1
นั่นขึ้นอยู่กับการใช้งาน - ดูคำตอบของ Sven
Tim Pietzcker

72

โมดูลPython Standard Library Pathlibทำสิ่งที่คุณกำลังมองหา:

Path('pagehead.section.htm').read_text()

อย่าลืมนำเข้า Path:

jsk@dev1:~$ python3
Python 3.5.2 (default, Sep 10 2016, 08:21:44)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pathlib import Path
>>> (Path("/etc") / "hostname").read_text()
'dev1.example\n'

บน Python 27 ติดตั้ง backported pathlibหรือpathlib2


8
คำตอบอื่น ๆ ที่เสนอwithนั้นใช้ได้ แต่withเป็นคำสั่งไม่ใช่นิพจน์ pathlibคำตอบนี้เป็นคำตอบเดียวสำหรับคำถามดั้งเดิมที่สามารถฝังอยู่ในนิพจน์ Python สิ่งที่ชอบSECRET_KEY = os.environ.get('SECRET_KEY') or pathlib.Path('SECRET_KEY').read_bytes()
LeoRochael

24

การใช้ CPython ไฟล์ของคุณจะถูกปิดทันทีหลังจากที่บรรทัดถูกเรียกใช้งานเนื่องจากอ็อบเจ็กต์ไฟล์จะถูกรวบรวมในทันที มีข้อเสียอยู่สองประการ:

  1. ในการใช้งาน Python แตกต่างจาก CPython ไฟล์มักจะไม่ถูกปิดทันที แต่ในภายหลังซึ่งอยู่นอกเหนือการควบคุมของคุณ

  2. ใน Python 3.2 ขึ้นไปสิ่งนี้จะทำให้เกิดResourceWarningถ้าเปิดใช้งาน

ดีกว่าที่จะลงทุนเพิ่มเติมหนึ่งบรรทัด:

with open('pagehead.section.htm','r') as f:
    output = f.read()

เพื่อให้แน่ใจว่าไฟล์ถูกปิดอย่างถูกต้องในทุกสถานการณ์


18

ไม่จำเป็นต้องนำเข้าไลบรารีพิเศษใด ๆ เพื่อดำเนินการนี้

ใช้ไวยากรณ์ปกติและจะเปิดไฟล์เพื่ออ่านจากนั้นปิด

with open("/etc/hostname","r") as f: print f.read() 

หรือ

with open("/etc/hosts","r") as f: x = f.read().splitlines()

ซึ่งให้อาร์เรย์ x ที่มีเส้นและสามารถพิมพ์ได้ดังนี้:

for line in x: print line

หนึ่งไลน์เนอร์เหล่านี้มีประโยชน์มากสำหรับการบำรุงรักษา - โดยพื้นฐานแล้วการจัดทำเอกสารด้วยตัวเอง


8

สิ่งที่คุณทำได้คือใช้withคำสั่งและเขียนสองขั้นตอนในหนึ่งบรรทัด:

>>> with open('pagehead.section.htm', 'r') as fin: output = fin.read();
>>> print(output)
some content

withคำสั่งจะดูแลการเรียก__exit__ฟังก์ชั่นของวัตถุที่กำหนดแม้ว่าสิ่งที่ไม่ดีที่เกิดขึ้นในรหัสของคุณ; ใกล้เคียงกับtry... finallyไวยากรณ์ สำหรับวัตถุกลับโดยopen, __exit__สอดคล้องกับการปิดไฟล์

คำสั่งนี้ถูกนำมาใช้กับ Python 2.6


คำชี้แจงเล็กน้อย: ตามเอกสาร withแนะนำใน Python 2.5 แต่ต้องนำเข้าอย่างชัดเจนจาก__future__. พร้อมใช้งานจากบริบททั้งหมดใน Python 2.6
David Alber

5

ใช้ilio : (inline io):

เรียกใช้ฟังก์ชันเดียวแทนการเปิดไฟล์ (), อ่าน (), ปิด ()

from ilio import read

content = read('filename')

3

ฉันคิดว่าวิธีที่เป็นธรรมชาติที่สุดในการบรรลุสิ่งนี้คือการกำหนดฟังก์ชัน

def read(filename):
    f = open(filename, 'r')
    output = f.read()
    f.close()
    return output

จากนั้นคุณสามารถทำสิ่งต่อไปนี้:

output = read('pagehead.section.htm')

2
with open('pagehead.section.htm')as f:contents=f.read()

4
สิ่งนี้แตกต่างจากคำตอบ 3 อันดับแรกอย่างไร?
พนักงานทุกคนมีความจำเป็น

4
ข้อแตกต่างที่ใหญ่ที่สุดคือบรรทัดเดียวตามที่คำถามระบุไว้ โดยส่วนตัวแล้วฉันไม่สามารถหาอะไรได้มากกว่านั้น แต่อย่าลังเลที่จะวิจารณ์งานของฉันแทนที่จะมีส่วนร่วมในคำถามด้วยตัวคุณเองจริงๆ

3
วิธีที่สั้นที่สุดในตัวในการเปิดอ่านและปิดไฟล์ใน Python คือการใช้บรรทัดตรรกะ 2 บรรทัดไม่ว่าจะย่อเป็น 1 บรรทัดหรือไม่ก็ตาม ดังนั้นฉันจึงไม่เห็นว่าคำตอบนี้จะแตกต่างจาก 3 คำตอบเดิมอย่างมีประสิทธิภาพ
แรงงานทุกคนมีความจำเป็น

1
ไม่สำคัญว่า 'ประสิทธิภาพ' จะแตกต่างกันหรือไม่ ฉันไปที่หน้านี้เพื่อค้นหาไวยากรณ์บรรทัดเดียวที่อาจใช้กับpython -cบรรทัดคำสั่งดังนั้นการโพสต์คำตอบแบบ 2 บรรทัดจึงไม่ช่วยอะไร
user5359531

1
@ user5359531 ฉันไม่เห็นประเด็นของคุณ: คุณรู้ไหมว่าคุณสามารถอ้างถึงนิพจน์ python โดย"ใช้;เพื่อต่อท้ายสองคำสั่งและลบขึ้นบรรทัดใหม่หลัง:? การแสดงออกต่อไปนี้ใช้ได้ผลดีสำหรับฉัน:$> python -c "with open('some file', 'r') as f: print(next(f))"
Joël

0

ฉันมักจะทำบางสิ่งเช่นนี้เมื่อฉันต้องได้รับสองสามบรรทัดรอบ ๆ สิ่งที่ฉันโลภในไฟล์บันทึก:

$ grep -n "xlrd" requirements.txt | awk -F ":" '{print $1}'
54

$ python -c "with open('requirements.txt') as file: print ''.join(file.readlines()[52:55])"
wsgiref==0.1.2
xlrd==0.9.2
xlwt==0.7.5

1
สมบูรณ์ไม่เกี่ยวข้องกับหัวข้อเดิม แต่คุณควรมีลักษณะเป็นgrep -A <n>, grep -B <n>และgrep -C <n>ถ้ามันเป็นประโยชน์ ข้อมูลเพิ่มเติม: stackoverflow.com/a/9083/1830159
Liam Stanley

0

การใช้more_itertools.with_iterมันเป็นไปได้ที่จะเปิดอ่านปิดและกำหนดค่าเทียบเท่าoutputในบรรทัดเดียว (ไม่รวมคำสั่งนำเข้า):

import more_itertools as mit


output = "".join(line for line in mit.with_iter(open("pagehead.section.htm", "r")))

แม้ว่าจะเป็นไปได้ผมจะมองหาวิธีการอื่นอื่น ๆ นอกเหนือจากการกำหนดเนื้อหาของไฟล์ที่ตัวแปรคือการย้ำขี้เกียจ - นี้สามารถทำได้โดยใช้แบบดั้งเดิมwithบล็อกหรือในตัวอย่างข้างต้นโดยการลบjoin()และ outputiterating


คุณสามารถนำเข้าภายใน oneliner ได้เช่นกัน "".join(line for line in __import__('more_itertools').with_iter(open("pagehead.section.htm", "r")))วิธีนี้ใช้งานได้ดีและไม่ต้องมีบรรทัดสำหรับการนำเข้า
melwil

1
ฉันเห็นด้วยกับคุณ. อย่างไรก็ตามในขณะที่พูดคุยเกี่ยวกับการแก้งานกับ oneliners ฉันมักจะพบว่าตัวเองอยู่ในข้อโต้แย้งที่ผลลัพธ์ที่ตกลงกันควรเป็นโค้ดบรรทัดเดียวที่วางลงในไพ ธ อนเชลล์ใหม่ ความท้าทายดังกล่าวแทบไม่เป็นไปตาม pep8 ไม่มีวิธีปฏิบัติที่ดีในการเขียนโค้ด แต่เป็นเพียงเคล็ดลับในการขจัดความจำเป็นในการนำเข้าเท่านั้น
melwil

0

หากคุณต้องการที่อบอุ่นและความรู้สึกคลุมเครือก็ไปกับกับ

สำหรับ python 3.6 ฉันรันโปรแกรมทั้งสองนี้ภายใต้การเริ่มต้นใหม่ของ IDLE โดยให้ช่วงเวลาของ:

0.002000093460083008  Test A
0.0020003318786621094 Test B: with guaranteed close

จึงไม่แตกต่างกันมากนัก

#--------*---------*---------*---------*---------*---------*---------*---------*
# Desc: Test A for reading a text file line-by-line into a list
#--------*---------*---------*---------*---------*---------*---------*---------*

import sys
import time

#                                  # MAINLINE
if __name__ == '__main__':
    print("OK, starting program...")

    inTextFile = '/Users/Mike/Desktop/garbage.txt'

#                                  # Test: A: no 'with;
    c=[]
    start_time = time.time()
    c = open(inTextFile).read().splitlines()
    print("--- %s seconds ---" % (time.time() - start_time))

    print("OK, program execution has ended.")
    sys.exit()                     # END MAINLINE

เอาท์พุท:

OK, starting program...
--- 0.002000093460083008 seconds ---
OK, program execution has ended.

#--------*---------*---------*---------*---------*---------*---------*---------*
# Desc: Test B for reading a text file line-by-line into a list
#--------*---------*---------*---------*---------*---------*---------*---------*

import sys
import time

#                                  # MAINLINE
if __name__ == '__main__':
    print("OK, starting program...")

    inTextFile = '/Users/Mike/Desktop/garbage.txt'

#                                  # Test: B: using 'with'
    c=[]
    start_time = time.time()
    with open(inTextFile) as D: c = D.read().splitlines()
    print("--- %s seconds ---" % (time.time() - start_time))

    print("OK, program execution has ended.")
    sys.exit()                     # END MAINLINE

เอาท์พุท:

OK, starting program...
--- 0.0020003318786621094 seconds ---
OK, program execution has ended.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.