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


94

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

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

if not isinstance(fp, file):
   raise something

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

답변:


44

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

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

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


why어떤 사업자가 좋아에 대해 __add__, __lshift__또는 __or__사용자 정의 클래스에서? (파일 객체와 API : docs.python.org/glossary.html#term-file-object )
n611x007

@naxa : 그렇다면 그 연산자는 정확히 어떻습니까?
martineau 2015 년

33
종종 시도하는 것만으로도 효과가 있지만 파이썬에서하기 어렵다면 그것은 틀렸다는 파이썬의 격언을 사지 않습니다. 객체가 전달되고 유형에 따라 해당 객체로 수행 할 수있는 10 가지 다른 작업이 있다고 가정합니다. 최종적으로 올바르게 될 때까지 각 가능성을 시도하고 오류를 처리하지 않을 것입니다. 그것은 완전히 비효율적입니다. 당신은 반드시 유형이 무엇인지 질문 할 필요가 없습니다,하지만 당신은이 객체는 인터페이스 X를 구현 않는 질문 할 수 있어야 할
jcoffland

32
파이썬 컬렉션 라이브러리가 "인터페이스 유형"(예 : 시퀀스)이라고하는 것을 제공한다는 사실은 이것이 파이썬에서도 종종 유용하다는 사실을 말해줍니다. 일반적으로 누군가가 "how to foo"를 물을 때 "do n't foo"는 매우 만족스러운 대답이 아닙니다.
AdamC

1
AttributeError는 객체가 필요한 인터페이스를 지원하는지 여부와 관련이없는 모든 종류의 이유로 발생할 수 있습니다. 으로 hasattr은 IOBASE에서 파생하지 않는 filelikes 필요하다
에릭 Aronesty을

76

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)(그 방향으로는 훨씬 적은 수의 옵션이 있지만).


1
마지막으로 누군가는 그것을 현실로 유지합니다.
Anthony Rutledge는

17
유일하게 유용한 답변입니다. StackOverflowers가 계속 찬성 투표를하는 이유 "내가 더 잘 알고 있기 때문에하려는 일을 중단하십시오. 게시물은 내 허약 한 정신 이상입니다. ( 아마도 Cthulhu가 알고 있습니까? )
Cecil Curry

1
우리는 미리 생각하지 않은 사람들이 작성한 코드를 너무 많이 작성했고, 명시 적으로 확인하기 때문에 파일이 아닌 거의 파일을 전달하면 손상됩니다. 전체 EAFP, 오리 타이핑은 헛소리 순도 테스트가 아닙니다. 그것은 실제 egineering 결정입니다
drxzcl

1
이것은 더 나은 엔지니어링으로 보일 수 있으며 개인적으로 선호하지만 작동하지 않을 수 있습니다. 일반적으로 하지 파일 - 류의 객체로부터 상속 필요합니다 IOBase. 예를 들어 pytest 픽스쳐 _pytest.capture.EncodedFile는 아무것도 상속하지 않는 것을 제공합니다.
Tomáš Gavenčiak

46

다른 사람들이 말했듯이 일반적으로 이러한 검사를 피해야합니다. 한 가지 예외는 객체가 합법적으로 다른 유형일 수 있고 유형에 따라 다른 동작을 원하는 경우입니다. 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). 대신 다음과 같은 것이 필요 합니다.


28

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


9
+1 : x파일과 유사하지 않은 경우 x.read()자체 예외가 발생합니다. 추가 if 문을 작성하는 이유는 무엇입니까? 개체를 사용하십시오. 작동하거나 중단됩니다.
S.Lott

3
예외도 처리하지 마십시오. 누군가가 당신이 기대하는 API와 일치하지 않는 것을 전달했다면 그것은 당신의 문제가 아닙니다.
habnabit 2009

1
@Aaron Gallagher : 잘 모르겠습니다. 내가 일관된 상태를 유지하기 어렵더라도 당신의 진술은 사실입니까?
dmeister

1
일관된 상태를 유지하려면 "try / finally"(예외는 아님) 또는 새로운 "with"문을 사용할 수 있습니다.
drxzcl 2009

이것은 또한 "빠르게 실패하고 크게 실패"패러다임과 일치합니다. 세심하지 않으면 명시적인 hasattr (...) 검사로 인해 의도 된 작업을 수행하지 않고 함수 / 메서드가 정상적으로 반환 될 수 있습니다.
Ben Burns

11

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

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

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

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


1
동의합니다.하지만 모든 곳에서이 문제에 열중하지 않는 선에 따라 테스트 중에 이러한 많은 우려가 흔들려 야하며 "이 문제를 포착 할 수있는 위치 / 사용자에게 표시하는 방법"질문 중 일부는 사용성 요구 사항에 의해 답변 될 것입니다.
Ben Burns

7

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

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

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

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

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


예제의 순서를 바꾸는 것이 좋습니다. try항상 첫 번째 선택입니다. hasattr검사는 단지입니다 - 정말 애매한 이유 - 당신은 단순히 사용할 수 없습니다 try.
S.Lott

1
나중에 데이터를 처리하려는 경우 블록 에 모든 코드를 넣는 것을 피하기 위해 fp.read(0)대신 사용 하는 것이 좋습니다 . fp.read()tryfp
Meow

3
참고 fp.read()큰 파일이 즉시 메모리 사용량이 증가합니다.
Kyrylo Perevozchikov

나는 이것이 파이썬 적이라고 생각하지만 다른 문제들 중에서 파일을 두 번 읽어야합니다. 예를 들어, Flask나는 이것을하고 기본 FileStorage객체가 읽은 후 포인터 재설정이 필요 하다는 것을 깨달았 습니다.
Adam Hughes

2

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

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

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

1
getattr(file, 'read')대신에 file.read? 이것은 똑같은 일을합니다.
abarnert

1
더 중요한 것은이 검사가 잘못되었다는 것입니다. file예를 들어 실제 인스턴스 가 주어지면 발생 합니다. (내장 / C 확장 유형의 인스턴스 메소드는 유형 builtin_function_or_method인 반면 이전 스타일 클래스의 메소드는입니다 instancemethod). 이것이 구식 클래스이고 또는 ==대신 유형에 사용한다는 사실 은 더 많은 문제이지만 기본 아이디어가 작동하지 않으면 거의 문제가되지 않습니다. ininstanceissubclass
abarnert

2

나는 내가 글을 쓸 때 당신의 질문에 부딪 혔습니다. open파일 이름, 파일 설명자 또는 미리 열린 파일과 같은 객체를 수락 할 수 유사 함수를 부딪 혔습니다.

read다른 답변에서 알 수 있듯이 메서드를 테스트하는 대신 개체를 열 수 있는지 확인했습니다. 가능한 경우 문자열 또는 설명자이며 결과에서 유효한 파일과 같은 개체가 있습니다. 경우 open을 제기 TypeError한 후 개체가 이미 파일입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.