otwórz czytaj i zamknij plik w 1 linii kodu

128

Teraz używam:

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

Ale żeby kod wyglądał lepiej, mogę:

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

W przypadku korzystania z powyższej składni, jak zamknąć plik, aby zwolnić zasoby systemowe?

1qazxsw2
źródło
19
Nie ma nic bardziej atrakcyjnego w jednoliniowcach. Kod jest czytany znacznie częściej niż jest napisany i powinien być napisany dla zrozumienia, a nie dla „chłodu”. Jedynym wyjątkiem jest sytuacja, gdy istnieje dobrze znany idiom w języku, ale w tym przypadku nie znam go.
drdwilcox
17
@drdwilcox: Cryptic one-lineers są złe, deklaratywne one-lineers są dobre. Nie ma powodu (a przynajmniej go nie widzę), dla którego w rdzeniu nie ma opakowania funkcji do odczytu pliku (taka powszechna potrzeba) w pojedynczym wywołaniu funkcji. Coś jak contents = os.readfile(path). Gdybym chciał zrobić coś bardziej wyszukanego, to ok, chętnie bym skorzystał with open(path) as fd: contents = fd.read(). Oczywiście można napisać własny wrapper, ale po to jest rdzeń, aby zapewnić programistom użyteczne elementy abstrakcji.
tokland
5
Prawdą jest, że kod jest czytany znacznie częściej niż jest napisany, ale implikacja, że ​​dłuższy kod jest tak samo dobry, jak krótki kod, nie może być bardziej błędna. Jeśli zainwestujesz czas w uczynienie kodu tak krótkim, jak to tylko możliwe (bez uciekania się do sprytnych sztuczek, które są trudne do zrozumienia), inwestycja ta będzie się opłacać w kółko, gdy kod zostanie odczytany. Każda napisana przez Ciebie linia jest krzywdą dla każdego, kto czyta Twój kod, więc powinieneś starać się pisać jak najmniej. Przypomnij sobie słynny cytat Pascala: „Przedłużyłem ten list tylko dlatego, że nie miałem czasu, by go skrócić”.
John Williams

Odpowiedzi:

195

Naprawdę nie musisz go zamykać - Python zrobi to automatycznie podczas czyszczenia pamięci lub przy wyjściu z programu. Ale jak zauważył @delnan, lepiej jest jawnie zamknąć go z różnych powodów.

Co więc możesz zrobić, aby było krótkie, proste i wyraźne:

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

Myślę, że to tylko dwie linijki i całkiem czytelne.

Tim Pietzcker
źródło
2
@ 1qazxsw2 Jeśli użyjesz withinstrukcji , zasób pliku zostanie poprawnie zamknięty.
David Alber
13
Do pierwszego zdania: Python ostatecznie je zamknie . Ale to nie znaczy, że powinieneś zapomnieć o zamknięciu. Nawet przy zliczaniu referencyjnym plik może pozostać otwarty znacznie dłużej, niż myślisz i chcesz (np. Jeśli zdarza się, że jest określany cyklami). Dzieje się to trzykrotnie w implementacjach Pythona, które mają przyzwoity GC, gdzie nie masz gwarancji, że cokolwiek zostanie GC w jakimkolwiek określonym czasie. Nawet dokumentacja CPythona mówi, że nie powinieneś polegać na GC przy takim czyszczeniu. Druga część odpowiedzi powinna być pogrubiona.
6
Jeśli naprawdę potrzebujesz jednowierszówki , możesz umieścić output = f.read()część w tej samej linii po :.
Karl Knechtel
1
„otwórz, przeczytaj i zamknij plik w 1 linii kodu” to dwie linie, bez odpowiedzi na pytanie.
user5359531
1
To zależy od implementacji - zobacz odpowiedź Svena.
Tim Pietzcker
71

Moduł Python Standard Library Pathlib robi to, czego szukasz:

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

Nie zapomnij zaimportować ścieżki:

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'

W Pythonie 27 zainstaluj backported pathliblubpathlib2

Janusz Skonieczny
źródło
8
Inne proponowane odpowiedzi withsą w porządku, ale withsą stwierdzeniem, a nie wyrażeniem. Ta pathlibodpowiedź jest jedyną odpowiedzią na pierwotne pytanie, które można osadzić w wyrażeniu Pythona. Coś jakSECRET_KEY = os.environ.get('SECRET_KEY') or pathlib.Path('SECRET_KEY').read_bytes()
LeoRochael
24

Używając CPythona, twój plik zostanie zamknięty natychmiast po wykonaniu linii, ponieważ obiekt pliku jest natychmiast usuwany. Są jednak dwie wady:

  1. W implementacjach Pythona innych niż CPython, plik często nie jest natychmiast zamykany, ale raczej w późniejszym czasie, poza twoją kontrolą.

  2. W Pythonie 3.2 lub nowszym spowoduje to wyświetlenie pliku ResourceWarning, jeśli jest włączony.

Lepiej zainwestować jedną dodatkową linię:

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

Zapewni to prawidłowe zamknięcie pliku w każdych okolicznościach.

Sven Marnach
źródło
17

Nie ma potrzeby importowania żadnych specjalnych bibliotek, aby to zrobić.

Użyj normalnej składni, a otworzy plik do odczytu, a następnie zamknie go.

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

lub

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

co daje tablicę x zawierającą wiersze i można ją wydrukować w następujący sposób:

for line in x: print line

Te jednowierszowe są bardzo pomocne w konserwacji - zasadniczo dokumentują się samodzielnie.

SDsolar
źródło
8

Możesz użyć withinstrukcji i zapisać dwa kroki w jednej linii:

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

withOświadczenie zadba, aby zadzwonić do __exit__funkcji danego obiektu, nawet jeśli coś złego się wydarzyło w kodzie; jest zbliżona do try... finallyskładni. Dla obiektu zwróconego przez open, __exit__odpowiada zamknięciu pliku.

Ta instrukcja została wprowadzona w Pythonie 2.6.

Joël
źródło
Małe wyjaśnienie: zgodnie z dokumentacją with został wprowadzony w Pythonie 2.5, ale musiał zostać wyraźnie zaimportowany z __future__. Stało się dostępne we wszystkich kontekstach w Pythonie 2.6.
David Alber
5

użyj ilio : (inline io):

tylko jedno wywołanie funkcji zamiast file open (), read (), close ().

from ilio import read

content = read('filename')
iman
źródło
2
with open('pagehead.section.htm')as f:contents=f.read()

źródło
4
Czym różni się to od 3 najpopularniejszych odpowiedzi?
Wszyscy pracownicy są niezbędni
4
Największą różnicą jest to, że jest to tylko jedna linia, jak określono w pytaniu. Osobiście nie mogę znaleźć nic poza tym, ale nie krępuj się, aby krytykować moją pracę, zamiast samemu odpowiadać na pytanie.
3
Najkrótszym, wbudowanym sposobem otwierania, odczytywania i zamykania pliku w Pythonie jest użycie 2 linii logicznych, niezależnie od tego, czy są one skondensowane do 1 linii, czy nie. Dlatego nie uważam, aby ta odpowiedź faktycznie różniła się od 3 oryginalnych odpowiedzi.
Wszyscy pracownicy są niezbędni
1
Nie ma znaczenia, czy „skutecznie” jest inaczej. Dotarłem do tej strony, szukając jednowierszowej składni, której można użyć python -cw wierszu poleceń, więc publikowanie 2-wierszowych odpowiedzi nie pomaga.
user5359531
1
@ user5359531 Nie widzę twojego punktu: czy wiesz, że możesz cytować wyrażenia Pythona ", użyć ;do dołączenia dwóch instrukcji i usunąć znak nowej linii po :? Następujące wyrażenie działa dla mnie dobrze:$> python -c "with open('some file', 'r') as f: print(next(f))"
Joël
2

Myślę, że najbardziej naturalnym sposobem osiągnięcia tego jest zdefiniowanie funkcji.

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

Następnie możesz wykonać następujące czynności:

output = read('pagehead.section.htm')
Adrien Pavao
źródło
0

Często robię coś takiego, gdy potrzebuję kilku wierszy otaczających coś, co grepowałem w pliku dziennika:

$ 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
Matthew Purdon
źródło
1
Całkowicie niezwiązane z pierwotnym tematem, ale należy spojrzeć na grep -A <n>, grep -B <n>i grep -C <n>, jeżeli jest to pomocne. Więcej informacji: stackoverflow.com/a/9083/1830159
Liam Stanley
0

Za pomocą more_itertools.with_itermożna otwierać, czytać, zamykać i przypisywać odpowiednik outputw jednej linii (z wyłączeniem instrukcji import):

import more_itertools as mit


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

Chociaż jest to możliwe, szukałbym innego podejścia niż przypisanie zawartości pliku do zmiennej, tj. Leniwej iteracji - można to zrobić za pomocą tradycyjnego withbloku lub w powyższym przykładzie, usuwając join()i iterując output.

pylang
źródło
Możesz również importować wewnątrz onelinera. "".join(line for line in __import__('more_itertools').with_iter(open("pagehead.section.htm", "r")))Działa to dobrze i eliminuje potrzebę tworzenia wiersza do importu.
melwil
1
Całkowicie się z Tobą zgadzam. Jednak podczas omawiania rozwiązywania zadań z onelinerami często znajdowałem się w kłótniach, w których uzgodnionym wynikiem powinna być pojedyncza linia kodu wklejona do nowej powłoki Pythona. Takie wyzwania rzadko są zgodne z pep8. W żaden sposób nie jest to dobra praktyka przy pisaniu kodu, miała służyć jedynie jako wskazówka, aby wyeliminować potrzebę importu.
melwil
0

Jeśli chcesz tego ciepłego i rozmytego uczucia, po prostu idź z tym .

W przypadku Pythona 3.6 uruchomiłem te dwa programy na nowym początku IDLE, dając środowiska wykonawcze:

0.002000093460083008  Test A
0.0020003318786621094 Test B: with guaranteed close

Więc nie ma to wielkiej różnicy.

#--------*---------*---------*---------*---------*---------*---------*---------*
# 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

WYNIK:

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

WYNIK:

OK, starting program...
--- 0.0020003318786621094 seconds ---
OK, program execution has ended.
CopyPasteIt
źródło