[python] 객체가 Python에서 파일과 유사한 지 확인

파일 류 객체 는 실제 파일처럼 동작하는 파이썬의 객체입니다. 예를 들어 read () 및 write method ()가 있지만 구현이 다릅니다. 이것은 Duck Typing 개념의 실현입니다 .

예를 들어 StringIO 또는 Socket 객체가 실제 파일 대신 사용될 수 있도록 파일이 예상되는 모든 곳에서 파일과 유사한 객체를 허용하는 것이 좋습니다 . 따라서 다음과 같은 검사를 수행하는 것은 좋지 않습니다.

if not isinstance(fp, file):
   raise something

객체 (예 : 메소드의 매개 변수)가 “파일과 유사한”것인지 확인하는 가장 좋은 방법은 무엇입니까?



답변

특별한 요구 사항이없는 한 일반적으로 코드에서 이와 같은 검사를 수행하는 것은 좋지 않습니다.

Python에서 타이핑은 동적입니다. 객체가 파일 인 것처럼 사용하고 결과 오류를 처리하는 대신 객체가 파일과 같은지 확인해야하는 이유는 무엇입니까?

당신이 할 수있는 모든 검사는 어쨌든 런타임에 일어날 것입니다. 그래서 같은 일을하고 if not hasattr(fp, 'read')일부 예외를 fp.read()발생 시키면 메소드가 존재하지 않는 경우 결과 속성 오류를 호출 하고 처리하는 것보다 약간 더 유용 합니다.


답변

3.1+의 경우 다음 중 하나 :

isinstance(something, io.TextIOBase)
isinstance(something, io.BufferedIOBase)
isinstance(something, io.RawIOBase)
isinstance(something, io.IOBase)

2.x의 경우, “파일 류 객체”는 확인하기에는 너무 모호하지만, 어떤 기능을 다루고 있는가에 대한 문서는 실제로 필요한 것이 무엇인지 알려줄 것입니다. 그렇지 않은 경우 코드를 읽으십시오.


다른 답변이 지적했듯이 가장 먼저 물어볼 것은 정확히 무엇을 확인하고 있는지입니다. 일반적으로 EAFP는 충분하고 관용적입니다.

용어집은 “개체와 같은 파일이”궁극적으로 세 가지 중 하나의 인스턴스를 의미합니다 “파일 객체”에 대한 동의어 말한다 추상 기본 클래스 에 정의 된 모듈 스스로 모든 서브 클래스, . 따라서 확인하는 방법은 위와 같습니다.ioIOBase

(그러나 검사 IOBase는 그다지 유용하지 않습니다. 텍스트 파일과 원시 파일을 구분할 필요없이 실제 파일과 유사한 read(size)이름의 일부 단일 인수 함수 를 구분해야하는 경우를 상상할 수 있습니까? read바이너리 파일입니까? 그래서 실제로는 거의 항상 “is a text file object”가 아닌 “is a file-like object”를 확인하고 싶습니다.)


2.x의 경우 io모듈이 2.6 이상부터 존재했지만 내장 파일 객체는 io클래스의 인스턴스가 아니며 stdlib의 파일 류 객체도 아니며 대부분의 타사 파일 류 객체도 아닙니다. 만날 가능성이 있습니다. “파일 류 객체”가 의미하는 바에 대한 공식적인 정의는 없습니다. 그것은 단지 “내장 파일 객체 와 같은 것 “이고, 다른 기능은 “like”에 의해 다른 것을 의미합니다. 이러한 기능은 그 의미를 문서화해야합니다. 그렇지 않은 경우 코드를 확인해야합니다.

그러나 가장 일반적인 의미는 “has read(size)“, “has read()“또는 “is an iterable of strings”이지만, 일부 오래된 라이브러리는 readline이들 중 하나 대신 예상 할 수 있습니다 . 일부 라이브러리는 close()사용자가 제공하는 파일을 좋아 합니다. fileno존재하면 다른 기능을 사용할 수 있습니다. 그리고 유사하게 write(buf)(그 방향으로는 훨씬 적은 수의 옵션이 있지만).


답변

다른 사람들이 말했듯이 일반적으로 이러한 검사를 피해야합니다. 한 가지 예외는 객체가 합법적으로 다른 유형일 수 있고 유형에 따라 다른 동작을 원하는 경우입니다. EAFP 방법은 객체가 둘 이상의 유형의 오리처럼 보일 수 있으므로 여기서 항상 작동하는 것은 아닙니다!

예를 들어 이니셜 라이저는 파일, 문자열 또는 자체 클래스의 인스턴스를 취할 수 있습니다. 그러면 다음과 같은 코드가있을 수 있습니다.

class A(object):
    def __init__(self, f):
        if isinstance(f, A):
            # Just make a copy.
        elif isinstance(f, file):
            # initialise from the file
        else:
            # treat f as a string

여기서 EAFP를 사용하면 각 초기화 경로가 예외를 발생시키기 전에 부분적으로 실행되므로 모든 종류의 미묘한 문제가 발생할 수 있습니다. 본질적으로이 구조는 함수 오버로딩을 모방하므로 Pythonic은 아니지만주의해서 사용하면 유용 할 수 있습니다.

참고로 Python 3에서 동일한 방식으로 파일 검사를 수행 할 수 없습니다 isinstance(f, io.IOBase). 대신 다음과 같은 것이 필요 합니다.


답변

여기서 지배적 인 패러다임은 EAFP입니다. 허가보다 용서를 구하기가 더 쉽습니다. 계속해서 파일 인터페이스를 사용한 다음 결과 예외를 처리하거나 호출자에게 전파되도록하십시오.


답변

일반적으로 오류가 훨씬 나중에 발생하지 않을 때 조건을 확인하여 오류를 발생시키는 것이 유용합니다. 특히 ‘user-land’와 ‘api’코드 사이의 경계에 해당됩니다.

출구 문 경찰서에 금속 탐지기를 두지 않고 입구에 두었습니다! 조건을 확인하지 않으면 100 줄 이전에 잡힐 수있는 오류가 발생하거나 하위 클래스에서 발생하는 대신 수퍼 클래스에서 오류가 발생할 수 있다는 의미라면 확인하는 데 아무런 문제가 없다고 말합니다.

둘 이상의 유형을 허용 할 때 적절한 유형을 확인하는 것도 의미가 있습니다. 일부 변수에는 ‘검색’메서드가 없기 때문에 예외를 발생시키는 것보다 “기본 문자열, OR 파일의 하위 클래스가 필요합니다”라는 예외를 발생시키는 것이 좋습니다.

그렇다고 미쳐서 어디서나이 작업을 수행한다는 의미는 아닙니다. 대부분의 경우 예외가 스스로 발생한다는 개념에 동의하지만 API를 대폭 명확하게 만들 수 있거나 간단한 조건이 충족되지 않아 불필요한 코드 실행을 피할 수 있다면 그렇게하세요!


답변

메서드를 시도하고 호출 한 다음 예외를 포착 할 수 있습니다.

try:
    fp.read()
except AttributeError:
    raise something

읽기 및 쓰기 방법 만 원하는 경우 다음을 수행 할 수 있습니다.

if not (hasattr(fp, 'read') and hasattr(fp, 'write')):
   raise something

내가 당신이라면 나는 try / except 방법을 사용할 것입니다.


답변

대부분의 상황에서이를 처리하는 가장 좋은 방법은 그렇지 않은 것입니다. 메서드가 파일과 유사한 객체를 취하고 전달 된 객체가 아닌 것으로 판명되면 메서드가 객체를 사용하려고 할 때 발생하는 예외는 명시 적으로 발생했을 수있는 예외보다 정보가 적지 않습니다.

그래도 이런 종류의 검사를하고 싶은 경우가 적어도 한 가지 있습니다. 객체가 전달 된 대상 (예 : 클래스의 생성자에 설정되는 경우)에 의해 즉시 사용되지 않는 경우입니다. 이 경우 EAFP의 원칙이 “Fail Fast”원칙보다 우선한다고 생각합니다. 내 클래스에 필요한 메서드를 구현했는지 확인하기 위해 개체를 확인합니다 (그리고 메서드가 메서드인지). 예 :

class C():
    def __init__(self, file):
        if type(getattr(file, 'read')) != type(self.__init__):
            raise AttributeError
        self.file = file