Czy jawne zamykanie plików jest ważne?

149

W Pythonie, jeśli otworzysz plik bez wywołania close()lub zamkniesz plik, ale nie używasz try- finallylub instrukcji " with", czy to jest problem? A może jako praktyka kodowania wystarczy polegać na wyrzucaniu elementów bezużytecznych w Pythonie, aby zamknąć wszystkie pliki? Na przykład, jeśli to zrobi:

for line in open("filename"):
    # ... do stuff ...

... czy jest to problem, ponieważ pliku nigdy nie można zamknąć i może wystąpić wyjątek uniemożliwiający jego zamknięcie? A może na pewno zostanie zamknięty po zakończeniu foroświadczenia, ponieważ plik wykracza poza zakres?

user553702
źródło
13
Plik nie wychodzi poza zakres na końcu forbloku. Jego liczba odwołań spadnie do zera, powodując automatyczne zamknięcie, ale tylko funkcje, klasy i moduły definiują zakresy w Pythonie, a nie inne instrukcje złożone.
agf
18
To nie jest problem, chyba że jest problemem. Na poziomie systemu operacyjnego wszystkie pliki otwierane przez skrypt zostaną zamknięte po zakończeniu działania skryptu, więc nie musisz się martwić o zamykanie plików w jednorazowych skryptach narzędzi. Jednak procesy mają ograniczenie liczby otwartych plików, które mogą utrzymywać, więc długotrwałe lub złożone skrypty mogą wymagać większej ostrożności. W każdym razie dobrym zwyczajem jest zamykanie plików.
Russell Borogove
3
@agf: Masz rację, że plik nie wykracza poza zakres, ale nie jest to związane z rozróżnieniem między forblokami a funkcjami / klasami / modułami. To znacznie prostsze: obiekty nie mają zasięgów, tylko nazwy mają. Nie ma nazwy odnoszącej się do tego obiektu, więc nie ma tu nic, co mogłoby pozostać w zakresie lub wyjść poza zakres.
maks.
@max Mój komentarz koryguje jego założenie, że forpętla jest związana z zakresem i wspomina, że ​​plik zostaje zamknięty z zupełnie innego powodu. Nie chodzi o to, jakie zakresy są w Pythonie, ponieważ nie ma to znaczenia.
agf
@max istnieje niejawne odniesienie do pętli for ... to jest argument semantyki
Peter R

Odpowiedzi:

126

W twoim przykładzie nie ma gwarancji, że plik zostanie zamknięty przed zamknięciem interpretera. W obecnych wersjach CPythona plik zostanie zamknięty na końcu pętli for, ponieważ CPython używa zliczania odwołań jako podstawowego mechanizmu usuwania pamięci, ale jest to szczegół implementacji, a nie funkcja języka. Nie ma gwarancji, że inne implementacje Pythona będą działać w ten sposób. Na przykład IronPython, PyPy i Jython nie używają liczenia odwołań i dlatego nie zamykają pliku na końcu pętli.

Poleganie na implementacji czyszczenia pamięci w CPythonie jest złą praktyką, ponieważ sprawia, że ​​kod jest mniej przenośny. Możesz nie mieć wycieków zasobów, jeśli używasz CPython, ale jeśli kiedykolwiek przełączysz się na implementację Pythona, która nie korzysta z liczenia odwołań, musisz przejść przez cały kod i upewnić się, że wszystkie pliki są poprawnie zamknięte.

Na przykład użyj:

with open("filename") as f:
     for line in f:
        # ... do stuff ...
Peter Graham
źródło
8
Czy użycie with open() as fautomatycznie zamyka plik po zakończeniu?
Rohan,
24
@Rohan tak, to jest ta mała magia, którą withzapewnia instrukcja, ale oczywiście, aby ta magia działała, obiekt musi mieć specjalne metody, __enter__a __exit__w tym drugim przypadku obiekt musi wykonać closewszystkie inne czynności porządkowe, które należy wykonać w koniec withwypowiedzi ...
Copperfield
1
Do Twojej wiadomości: Ta odpowiedź wyjaśnia tylko „kiedy będzie zamknięta”, ale nie wyjaśnia, „co, jeśli pozostanie otwarta”. W tym drugim przypadku przeczytaj „Co by się stało, gdyby plik pozostał otwarty?” część w tej odpowiedzi ( askubuntu.com/questions/701491/… )
RayLuo
Ponadto niezamknięcie plików może spowodować ich obcięcie, ponieważ zawartość pliku nie została opróżniona.
Erwan Legrand
Jeśli więc nie zamknę pliku, czy na pewno odzyskam pamięć, gdy program przestanie działać? Czy faktycznie muszę zrezygnować z całego tłumacza?
Pro Q,
22

Niektóre Pythony zamkną pliki automatycznie, gdy nie są już przywoływane, podczas gdy inne nie będą, a system operacyjny musi zamknąć pliki po zakończeniu pracy interpretera Pythona.

Nawet dla Pythonów, które zamkną pliki za Ciebie, czas nie jest gwarantowany: może nastąpić natychmiast lub może to być sekundy / minuty / godziny / dni później.

Tak więc, chociaż możesz nie mieć problemów z używanym językiem Python, zdecydowanie nie jest dobrą praktyką pozostawianie otwartych plików. W rzeczywistości w cpythonie 3 otrzymasz teraz ostrzeżenia, że ​​system musiał zamknąć pliki, jeśli tego nie zrobiłeś.

Morał: posprzątaj po sobie. :)

Ethan Furman
źródło
9
Pliki są zamykane, gdy nie ma do nich odwołań w CPythonie, ale to nie jest funkcja języka. Gdyby tak było, można by na nim polegać.
Peter Graham
9

Chociaż użycie takiej konstrukcji w tym konkretnym przypadku jest całkiem bezpieczne, istnieją pewne zastrzeżenia dotyczące uogólnienia takiej praktyki:

  • run może potencjalnie zabraknąć deskryptorów plików, chociaż jest to mało prawdopodobne, wyobraź sobie polowanie na taki błąd
  • możesz nie być w stanie usunąć tego pliku w niektórych systemach, np. win32
  • jeśli uruchamiasz cokolwiek innego niż CPython, nie wiesz, kiedy plik zostanie zamknięty
  • jeśli otworzysz plik w trybie do zapisu lub odczytu i zapisu, nie wiesz, kiedy dane są opróżniane
Dima Tisnek
źródło
3

Plik pobiera śmieci, a tym samym jest zamykany. GC określa, kiedy zostanie zamknięty, a nie Ty. Oczywiście nie jest to zalecana praktyka, ponieważ możesz przekroczyć limit otwierania plików, jeśli nie zamykasz plików zaraz po zakończeniu ich używania. Co jeśli w tej swojej forpętli otworzysz więcej plików i zostawisz je na swoim miejscu?

Nam Nguyen
źródło
Ale jeśli otworzysz inne pliki w tej pętli for, nadal będzie tak, że będzie otwartych więcej niż jeden plik jednocześnie, niezależnie od tego, czy jawnie zamkniesz którykolwiek z nich, czy nie. Czy mówisz, że plik niekoniecznie jest zbierany jako śmieci, gdy tylko wyjdzie poza zakres, więc zostanie zamknięty wcześniej, jeśli zostanie wykonany jawnie? A co z sytuacją, w której zdarzy się wyjątek (kiedy używasz z / try-final vs. nie robienie tego)?
user553702
1
W CPythonie zliczanie odwołań spowoduje, że zostaną one zebrane po forinstrukcji - nie będziesz musiał czekać na następne uruchomienie czyszczenia pamięci.
agf
3

Cześć Bardzo ważne jest, aby zamknąć deskryptor pliku w sytuacji, gdy zamierzasz użyć jego zawartości w tym samym skrypcie Pythona. Dziś sam zdaję sobie sprawę, po tak długim hektarnym debugowaniu. Powodem jest to, że zawartość będzie edytowana / usuwana / zapisywana dopiero po zamknięciu deskryptora pliku i wpłynie to na zmiany w pliku!

Więc przypuśćmy, że masz sytuację, w której zapisujesz zawartość do nowego pliku, a następnie bez zamykania fd używasz tego pliku (nie fd) w innym poleceniu powłoki, które czyta jego zawartość. W takiej sytuacji nie otrzymasz zawartości polecenia powłoki zgodnie z oczekiwaniami, a jeśli spróbujesz debugować, nie możesz łatwo znaleźć błędu. możesz również przeczytać więcej na moim blogu http://magnificentzps.blogspot.in/2014/04/importance-of-closing-file-descriptor.html

Zeel Shah
źródło
1

Podczas procesu I / O dane są buforowane: oznacza to, że są przechowywane w tymczasowej lokalizacji przed zapisaniem do pliku.

Python nie opróżnia bufora - to znaczy nie zapisuje danych do pliku - dopóki nie jest pewne, że skończysz pisać. Jednym ze sposobów jest zamknięcie pliku.

Jeśli piszesz do pliku bez zamykania, dane nie trafią do pliku docelowego.

Sanket Nagrale
źródło