Pythonでオブジェクトがファイルに似ているかどうかを確認する


93

ファイルのようなオブジェクトは、実際のファイルのように動作するPythonのオブジェクトです。たとえば、read()とwrite method()がありますが、実装が異なります。それはダックタイピングのコンセプトの実現です。

たとえば、実際のファイルの代わりにStringIOまたはSocketオブジェクトを使用できるように、ファイルが期待されるすべての場所でファイルのようなオブジェクトを許可することは、良い習慣と考えられています。したがって、次のようなチェックを実行することは悪いことです。

if not isinstance(fp, file):
   raise something

オブジェクト(たとえば、メソッドのパラメーター)が「ファイルのような」かどうかを確認する最良の方法は何ですか?

回答:


45

特別な要件がない限り、コードでこのようなチェックを行うことは、一般的にはお勧めできません。

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年

32
多くの場合、それを試してみればうまくいきますが、Pythonで行うのが難しい場合は間違っているというPythonicの格言を購入しません。オブジェクトが渡され、そのタイプに応じて、そのオブジェクトを使用して10の異なる処理を実行するとします。最終的に正しくなるまで、それぞれの可能性を試し、エラーを処理することはしません。それは完全に非効率です。あなたは、必ずしもこれですどのようなタイプ、依頼する必要はありませんが、このオブジェクトがインタフェースを実装しX.ん尋ねることができるようにする必要性を行う
jcoffland

31
pythonコレクションライブラリが「インターフェイスタイプ」(たとえば、シーケンス)と呼ばれるものを提供するという事実は、これがPythonでも有用であることが多いという事実を物語っています。一般に、誰かが「fooのやり方」を尋ねた場合、「fooはいけない」は満足のいく答えではありません。
AdamC、2015年

1
AttributeErrorは、オブジェクトが必要なインターフェースをサポートしているかどうかとは関係のないあらゆる種類の理由で発生する可能性があります。hasattrは、IOBaseから派生していないfilelikeに必要です
Erik Aronesty

74

3.1+の場合、次のいずれかです。

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

2.xの場合、「ファイルのようなオブジェクト」はチェックするのが漠然としすぎていますが、処理している関数のドキュメントで、実際に必要なものを説明できれば幸いです。そうでない場合は、コードを読んでください。


他の回答が指摘するように、最初に尋ねるべきことは、正確に何をチェックしているかです。通常、EAFPで十分であり、より慣用的です。

用語集では、「ファイルのようなオブジェクト」は「ファイルオブジェクト」の同義語であり、最終的にはモジュールで定義され 3つの抽象基本クラスのいずれかのインスタンスであり、それ自体がすべてのサブクラスであると述べています。したがって、確認方法は上記とまったく同じです。ioIOBase

(ただし、チェックIOBaseはあまり役に立ちません。テキストファイルとrawを区別する必要なしに、実際のファイルのようにread(size)、ファイルのように名前が付けられreadていない1つの引数を持つ関数と区別する必要がある場合を想像できますか。バイナリファイルですか?つまり、実際には、ほとんどの場合、たとえば、「ファイルのようなオブジェクトであるか」ではなく、「テキストファイルオブジェクトであるか」を確認する必要があります。


2.xの場合、ioモジュールは2.6以降で存在しますが、組み込みファイルオブジェクトはioクラスのインスタンスではなく、stdlibのファイル類似オブジェクトでも、サードパーティのファイル類似オブジェクトでもありません。出会う可能性が高いです。「ファイルのようなオブジェクト」が何を意味するかについての公式の定義はありませんでした。それは単に「組み込みファイルオブジェクトのようなもの」であり、関数が異なると「好き」によって意味が異なります。このような関数は、それらの意味を文書化する必要があります。そうでない場合は、コードを確認する必要があります。

ただし、最も一般的な意味は「has read(size)」、「has read()」、または「is itableable of strings」ですが、一部の古いライブラリreadlineはそれらのいずれかではなく予期する場合があり、一部のライブラリは指定したclose()ファイルに似ています。filenoが存在する場合は、他の機能を利用できます。同様にwrite(buf)(その方向にははるかに少ないオプションがあります)。


1
最後に、誰かがそれを現実に保ちます。
Anthony Rutledge

16
唯一の有用な答え。StackOverflowersが「あなたがやろうとしていることをやめなさい。私はもっとよく知っているので...そしてPEP 8、EAFPなどだ」と賛成し続ける理由。投稿は私の壊れやすい正気を超えています。(たぶんクトゥルフは知っていますか?
セシルカレー

1
なぜなら、前向きに考えていなかった人々によって書かれたコードが多すぎて、ほとんどチェックされていないものの、明示的にチェックするため、ファイルではない場合、それは壊れるからです。EAFP全体のアヒルのタイピングは、ありきたりな純度テストではありません。これは実際の独断的な決定です
drxzcl

1
これは優れたエンジニアリングと見なされる可能性があり、個人的には望ましいと思いますが、機能しない可能性があります。通常、ファイルのようなオブジェクトがから継承する必要はありませんIOBase。たとえば、pytestフィクスチャ_pytest.capture.EncodedFileは何も継承しないことを提供します。
トマーシュGavenčiak

46

他の人が言ったように、一般的にそのようなチェックを避けるべきです。1つの例外は、オブジェクトが正当に異なるタイプであり、タイプに応じて異なる動作が必要な場合です。オブジェクトが複数の種類のアヒルのように見える可能性があるため、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、2009年

3
例外も処理しないでください。誰かが予期したAPIと一致しないものを渡した場合、それはあなたの問題ではありません。
habnabit 2009年

1
@Aaron Gallagher:よくわかりません。一貫した状態を維持するのが難しい場合でも、あなたの発言は本当ですか?
dmeister 2009年

1
一貫した状態を維持するには、 "try / finally"(ただし例外なし!)または新しい "with"ステートメントを使用できます。
drxzcl 2009年

これは、「速く失敗して大声で失敗する」パラダイムとも一致します。細心の注意を払わない限り、明示的なhasattr(...)チェックにより、関数/メソッドが意図したアクションを実行せずに正常に戻ることがあります。
ベンバーンズ

11

条件をチェックしてエラーを発生させると、多くの場合、そのエラーは通常、後で発生するまで発生しません。これは特に、「ユーザーランド」と「API」コードの境界に当てはまります。

出口のドアの警察署に金属探知機を設置するのではなく、入口に設置します。条件をチェックしないと、100行前に、またはサブクラスで発生するのではなくスーパークラスでキャッチされた可能性のあるエラーが発生する可能性があることを意味します。チェックに問題はありません。

適切な型をチェックすることは、複数の型を受け入れる場合にも意味があります。一部の変数には「シーク」メソッドがないため、単に例外を発生させるよりも、「ベースストリング、またはファイルのサブクラスが必要です」という例外を発生させる方が適切です...

これはあなたが夢中になってこれをどこでも行うことを意味するわけではありませんが、ほとんどの場合、例外を自分で発生させるという概念に同意しますが、APIを大幅に明確にできる場合、または単純な条件が満たされていないために不要なコード実行を回避できる場合そうする!


1
私は同意しますが、どこでもこれに夢中にならないという線に沿って-これらの懸念の多くはテスト中に振り払われるべきであり、「これをどこでキャッチするか/どのようにユーザーに表示するか」という質問のいくつかは、ユーザビリティ要件によって答えられます。
ベンバーンズ

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、2009年

1
以降からデータを処理する場合は、すべてのコードをブロックに配置しないようにするために、fp.read(0)ではなくを使用することをお勧めします。fp.read()tryfp
Meow

3
fp.read()大きなファイルを使用すると、すぐにメモリ使用量が増えることに注意してください。
Kyrylo Perevozchikov

これはpythonicですが、他の問題の中でファイルを2回読み取る必要があります。たとえば、Flask私はこれを行い、基になるFileStorageオブジェクトには、読み込まれた後にポインターをリセットする必要があることに気付きました。
アダムヒューズ

2

ほとんどの状況では、これを処理する最良の方法はそうではありません。メソッドがファイルのようなオブジェクトを取り、渡されたオブジェクトがそうでないことが判明した場合、メソッドがオブジェクトを使用しようとしたときに発生する例外は、明示的に発生させた可能性のある例外よりも有益です。

ただし、この種のチェックを行う必要があるケースは少なくとも1つあります。たとえば、クラスのコンストラクターで設定されている場合など、渡されたオブジェクトによってオブジェクトがすぐに使用されない場合です。その場合、EAFPの原則は「フェイルファスト」の原則に負けていると思います。オブジェクトをチェックして、クラスに必要なメソッドが実装されていることを確認します(それらはメソッドです)。

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 2014

1
さらに重要なことに、このチェックは間違っています。fileたとえば、実際のインスタンスを指定すると発生します。(組み込み/ C拡張タイプのインスタンスのメソッドはタイプですがbuiltin_function_or_method、古いスタイルのクラスのインスタンスはですinstancemethod)。これが古いスタイルのクラスであり==ininstanceorの代わりに型で使用されているという事実issubclassはさらに問題ですが、基本的な考え方が機能しない場合、それはほとんど問題になりません。
abarnert 2014

2

私が書いていたときに私はあなたの質問に出くわした openファイル名、ファイル記述子、または事前に開かれたファイルのようなオブジェクトを受け入れることができる-like関数を出くわしました。

readメソッドをテストするのではなく、他の回答が示唆するように、オブジェクトを開くことができるかどうかを確認することになりました。可能であれば、それは文字列または記述子であり、結果から有効なファイルのようなオブジェクトが手元にあります。がopen発生する場合TypeError、オブジェクトはすでにファイルです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.