태그 보관물: file-handling

file-handling

파일 이름을 열어서 열거 나 파일을 열어야합니까? 이름을 전달하고

텍스트 파일로 작업을 수행하는 함수가 있다고 가정합니다. 예를 들어 텍스트 파일을 읽고 ‘a’라는 단어를 제거합니다. 파일 이름을 전달하고 함수에서 열기 / 닫기를 처리하거나 열린 파일을 전달하여 파일을 호출 한 사람이 파일을 닫을 것으로 예상 할 수 있습니다.

첫 번째 방법은 파일이 열려 있지 않은 것을 보장하는 더 좋은 방법처럼 보이지만 StringIO 객체와 같은 것을 사용하지 못하게합니다.

두 번째 방법은 약간 위험 할 수 있습니다-파일이 닫힐 지 여부를 알 수있는 방법은 없지만 파일과 같은 객체를 사용할 수 있습니다

def ver_1(filename):
    with open(filename, 'r') as f:
        return do_stuff(f)

def ver_2(open_file):
    return do_stuff(open_file)

print ver_1('my_file.txt')

with open('my_file.txt', 'r') as f:
    print ver_2(f)

이들 중 하나가 일반적으로 선호됩니까? 일반적으로 함수가이 두 가지 방법 중 하나로 작동 할 것으로 예상됩니까? 또는 프로그래머가 기능을 적절하게 사용할 수 있도록 잘 문서화해야합니까?



답변

편리한 인터페이스가 좋으며 때로는 갈 수도 있습니다. 그러나 컴포저 블 추상화를 통해 그 위에 다른 기능 (편의 래퍼 포함)을 구현할 수 있기 때문에 대부분의 경우 컴포지션이 컴포지션보다 중요 합니다.

함수가 파일을 사용하는 가장 일반적인 방법은 파일 시스템의 일부가 아닌 파일 핸들 (예 : 파이프, 소켓 등)을 사용할 수 있으므로 열린 파일 핸들을 매개 변수로 사용하는 것입니다.

def your_function(open_file):
    return do_stuff(open_file)

철자 with open(filename, 'r') as f: result = your_function(f)가 너무 많아서 사용자에게 묻기 어려운 경우 다음 솔루션 중 하나를 선택할 수 있습니다.

  • your_function열린 파일 또는 파일 이름을 매개 변수로 사용합니다. 파일 이름 인 경우 파일이 열리고 닫히고 예외가 전파됩니다. 명명 된 인수를 사용하여 해결할 수있는 모호성에 약간의 문제가 있습니다.
  • 파일 열기를 담당하는 간단한 래퍼를 제공합니다. 예 :

    def your_function_filename(file):
        with open(file, 'r') as f:
            return your_function(f)

    필자는 일반적으로 API 팽창과 같은 기능을 인식하지만, 일반적으로 사용되는 기능을 제공하는 경우 편의성이 충분히 강력합니다.

  • with open기능을 다른 작성 가능한 기능으로 랩핑하십시오 .

    def with_file(filename, callback):
        with open(filename, 'r') as f:
            return callback(f)

    사용 with_file(name, your_function)또는 더 복잡한 경우에with_file(name, lambda f: some_function(1, 2, f, named=4))


답변

실제 질문은 완전성 중 하나입니다. 파일 처리 기능이 파일의 전체 처리입니까, 아니면 일련의 처리 단계 중 하나입니까? 그것이 완전하고 완전하다면, 함수 내에서 모든 파일 액세스를 자유롭게 캡슐화하십시오.

def ver(filepath):
    with open(filepath, "r") as f:
        # do processing steps on f
        return result

이것은 with명령문 의 끝에 자원을 마무리하는 (파일을 닫는) 아주 좋은 특성을 가지고 있습니다.

그러나 이미 열려있는 파일을 처리해야 할 필요가 있다면 구별 ver_1하고 ver_2더 의미가 있습니다. 예를 들면 다음과 같습니다.

def _ver_file(f):
    # do processing steps on f
    return result

def ver(fileobj):
    if isinstance(fileobj, str):
        with open(fileobj, 'r') as f:
            return _ver_file(f)
    else:
        return _ver_file(fileobj)

이러한 유형의 명시 적 유형 테스트는 종종 유형 또는 인터페이스 기반 디스패치가 직접 지원되는 Java, Julia 및 Go와 같은 언어에서 눈살을 찌푸리게합니다 . 그러나 Python에서는 유형 기반 디스패치에 대한 언어 지원이 없습니다. 때로는 파이썬에서 직접 유형 테스트에 대한 비판을 볼 수 있지만 실제로는 매우 일반적이며 효과적입니다. 그것은 “Duck 타이핑”이라고하는 데이터 타입을 처리 할 수있는 기능을 제공합니다. 밑줄에 주목하십시오 _ver_file. 이는 “비공개”기능 (또는 방법)을 지정하는 일반적인 방법입니다. 기술적으로 직접 호출 할 수는 있지만 함수가 직접적인 외부 소비를위한 것이 아니라고 제안합니다.


2019 업데이트 : Python 3의 최신 업데이트를 고려할 때 경로가 잠재적으로 또는 (3.4+)가 pathlib.Path아닌 객체로 저장 될 수 있으며 유형 힌트가 밀교에서 주류로 (현재 활발하게 진화하고는 있지만 3.6 이상) 변경되었습니다. 이러한 발전을 고려한 업데이트 된 코드 :strbytes

from pathlib import Path
from typing import IO, Any, AnyStr, Union

Pathish = Union[AnyStr, Path]  # in lieu of yet-unimplemented PEP 519
FileSpec = Union[IO, Pathish]

def _ver_file(f: IO) -> Any:
    "Process file f"
    ...
    return result

def ver(fileobj: FileSpec) -> Any:
    "Process file (or file path) f"
    if isinstance(fileobj, (str, bytes, Path)):
        with open(fileobj, 'r') as f:
            return _ver_file(f)
    else:
        return _ver_file(fileobj)


답변

파일 핸들 대신 파일 이름을 전달하면 두 번째 파일이 열릴 때 첫 번째 파일과 동일한 파일이라는 보장이 없습니다. 이로 인해 정확성 버그와 보안 허점이 생길 수 있습니다.


답변

이것은 소유권과 파일을 닫을 책임에 관한 것입니다. 당신은 그것을 소유하고 특정이 사람 분명하다만큼 당신이 있는지 확인으로, 다른 방법으로 어떤 점에 배치 닫아야합니다 스트림 또는 파일 핸들이든 꼬추 /에 전달할 수 있습니다 것입니다 당신이 완료되면 소유자에 의해 폐쇄 . 이것은 일반적으로 시공 적 구성 또는 일회용 패턴을 포함합니다.


답변

열린 파일을 전달하기로 선택하면 다음과 같은 작업을 수행 할 수 있지만 파일에 쓰는 함수에서 파일 이름에 액세스 할 수 없습니다.

파일 / 스트림 작업을 100 % 책임지는 클래스와 순진하고 해당 파일 / 스트림을 열거 나 닫을 것으로 예상되지 않는 다른 클래스 또는 함수를 원한다면이 작업을 수행합니다.

컨텍스트 관리자는 finally 절을 사용하는 것처럼 작동합니다. 따라서 라이터 기능에서 예외가 발생하면 파일은 무엇이든 닫힙니다.

import contextlib

class FileOpener:

    def __init__(self, path_to_file):
        self.path_to_file = path_to_file

    @contextlib.contextmanager
    def open_write(self):
        # ...
        # Here you can add code to create the directory that will accept the file.
        # ...
        # And you can add code that will check that the file does not exist 
        # already and maybe raise FileExistsError
        # ...
        try:
            with open(self.path_to_file, "w") as file:
                print(f"open_write: has opened the file with id:{id(file)}")
                yield file
        except IOError:
            raise
        finally:
            # The try/catch/finally is not mandatory (except if you want to manage Exceptions in an other way, as file objects have predefined cleanup actions 
            # and when used with a 'with' ie. a context manager (not the decorator in this example) 
            # are closed even if an error occurs. Finally here is just used to demonstrate that the 
            # file was really closed.
            print(f"open_write: has closed the file with id:{id(file)} - {file.closed}")


def writer(file_open, data, raise_exc):
    with file_open() as file:
        print("writer: started writing data.")
        file.write(data)
        if raise_exc:
            raise IOError("I am a broken data cable in your server!")
        print("writer: wrote data.")
    print("writer: finished.")

if __name__ == "__main__":
    fo = FileOpener('./my_test_file.txt')
    data = "Hello!"
    raise_exc = False  # change me to True and see that the file is closed even if an Exception is raised.
    writer(fo.open_write, data, raise_exc)


답변