web-dev-qa-db-ja.com

Pythonで型をチェックする標準的な方法は何ですか?

特定のオブジェクトが特定のタイプであるかどうかを確認するための最良の方法は何ですか?オブジェクトが与えられた型から継承しているかどうかを調べるのはどうですか?

オブジェクトoがあるとしましょう。それがstrであるかどうかをどうやって確認できますか?

1067
Herge

ostrのインスタンスまたはstrの任意のサブクラスであるかどうかを確認するには、 isinstance を使用します(これは「標準的な」方法です)。

if isinstance(o, str):

oの型が正確にstrであるかどうかを確認するには(サブクラスを除く):

if type(o) is str:

次のものも機能し、場合によっては便利です。

if issubclass(type(o), str):

関連情報については、Pythonライブラリリファレンスの 組み込み関数 を参照してください。

もう1つ注意してください。この場合、python 2を使用しているのであれば、実際には次のものを使用することができます。

if isinstance(o, basestring):

これはUnicode文字列もキャッチするためです( unicodestrのサブクラスではありません。strunicodeの両方は basestring のサブクラスです)。 basestringはpython 3ではもう存在しないことに注意してください。そこでは 厳密な分離 の文字列( str )とバイナリデータ( bytes )があります。

あるいは、isinstanceはTuple of classを受け入れます。 xが(str、unicode)のいずれかのサブクラスのインスタンスである場合、これはTrueを返します。

if isinstance(o, (str, unicode)):
1247

オブジェクトの型をチェックする most Pythonicの方法は...チェックしないことです。

Pythonは Duck Typing を推奨しているので、オブジェクトのメソッドを使いたい方法で使うにはtry...exceptを使うべきです。あなたの関数が書き込み可能なファイルオブジェクトを探しているのであれば、don'tfileのサブクラスであることを確認し、その.write()メソッドを使ってみてください!

もちろん、時々これらのNice抽象化が壊れてisinstance(obj, cls)があなたが必要とするものです。しかし、控えめに使ってください。

163
Dan Lenski

trueoであるか、strから継承するタイプの場合、isinstance(o, str)strを返します。

trueがstrの場合に限り、type(o) is stroを返します。 falseoから継承した型の場合、strを返します。

44
Herge

質問に答えてから Pythonにタイプヒントが追加されました 。 Pythonの型ヒントを使用すると、型をチェックできますが、静的に型付けされた言語とは非常に異なる方法です。 Pythonのタイプヒントは、関数に関連付けられたランタイムアクセス可能データとして、期待される引数のタイプを関数に関連付け、チェック対象のタイプについてはこのallowsを使用します。型ヒントの構文の例:

def foo(i: int):
    return i

foo(5)
foo('oops')

この場合、注釈の引数の型はintであるため、foo('oops')に対してエラーをトリガーする必要があります。追加されたタイプヒントは、スクリプトが正常に実行されたときにエラーが発生するcauseではありません。ただし、他のプログラムがクエリして型エラーのチェックに使用できる予想される型を記述する属性を関数に追加します。

型エラーを見つけるために使用できるこれらの他のプログラムの1つはmypyです。

mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"

(パッケージマネージャーからmypyをインストールする必要があるかもしれません。CPythonには付属していないと思いますが、ある程度の「公式性」があるようです。)

この方法での型チェックは、静的に型指定されたコンパイル言語での型チェックとは異なります。 Pythonでは型は動的であるため、実行時に型チェックを行う必要があります。これは、正しいプログラムであっても、毎回発生すると主張する場合にはコストがかかります。明示的な型チェックは、必要以上に制限的であり、不必要なエラーを引き起こす可能性があります(たとえば、引数は本当にlist型である必要がありますか?.

明示的な型チェックの利点は、カモタイピングよりも早くエラーをキャッチし、より明確なエラーメッセージを提供できることです。アヒルの種類の正確な要件は、外部ドキュメントでのみ表現できます(うまくいけば、それは徹底的かつ正確です)。

Pythonの型ヒントは、型を指定およびチェックできる妥協案を提供することを意図していますが、通常のコード実行中に追加費用はかかりません。

typingパッケージは、特定の型を必要とせずに必要な動作を表現するために型ヒントで使用できる型変数を提供します。たとえば、IterableCallableなどの変数が含まれており、これらの動作を持つ任意の型の必要性を指定するヒントを提供します。

型のヒントは型をチェックするための最もPython的な方法ですが、型をまったくチェックせず、アヒルの型付けに依存することは、多くの場合、よりPythonicです。型のヒントは比較的新しく、それらが最もPythonicなソリューションであるとき、審査員はまだ出ています。比較的議論の余地のないが非常に一般的な比較:型のヒントは、強制できるドキュメントの形式を提供し、コードがより早くより理解しやすいエラーを生成できるようにし、ダックタイピングではできないエラーをキャッチでき、静的にチェックできます(異常な場合)センスですが、それはまだランタイム外です)。一方、アヒルの型付けは長い間Pythonの方法であり、静的型付けの認知オーバーヘッドを課すことはなく、冗長性が低く、すべての実行可能な型を受け入れます。

23
Praxeolitic

危険な状況を知らずにアヒルの型付けが悪くなる理由の例を次に示します。あなたが本当にアヒルを必要とするとき、あなたが爆弾を得ることがないのを確実にするためのisinstanceとissubclassof関数の.

class Bomb:
    def __init__(self):
        ""

    def talk(self):
        self.explode()

    def explode(self):
        print "BOOM!, The bomb explodes."

class Duck:
    def __init__(self):
        ""
    def talk(self):
        print "I am a duck, I will not blow up if you ask me to talk."    

class Kid:
    kids_duck = None

    def __init__(self):
        print "Kid comes around a corner and asks you for money so he could buy a duck."

    def takeDuck(self, duck):
        self.kids_duck = duck
        print "The kid accepts the duck, and happily skips along"

    def doYourThing(self):
        print "The kid tries to get the duck to talk"
        self.kids_duck.talk()

myKid = Kid()
myBomb = Bomb()
myKid.takeDuck(myBomb)
myKid.doYourThing()
17
Dmitry
12

私はPythonのような動的言語を使うことについての素晴らしいことはあなたが本当にそのようなことをチェックする必要がないということです。

私はあなたのオブジェクトに必要なメソッドを呼び出してAttributeErrorをキャッチします。後でこれを使用して、テスト用のオブジェクトのモックなど、さまざまなタスクを実行するために他の(一見無関係の)オブジェクトを使用してメソッドを呼び出すことができます。

file likeオブジェクトを返すurllib2.urlopen()を使用してWebからデータを取得するときに、これを頻繁に使用しました。これは、実際のファイルと同じread()メソッドを実装しているため、ファイルから読み取るほとんどすべてのメソッドに渡すことができます。

しかしisinstance()を使う時間と場所があると確信しています、そうでなければおそらくそこにはないでしょう:)

6
Will Harding

ヒューゴへ:

おそらくlistではなくarrayを意味しますが、それは型チェックに関する全体的な問題を示しています - 問題のオブジェクトがリストなのか、それが何らかのシーケンスなのか、それとも単一のシーケンスなのか知りたくないのですオブジェクトそれで、シーケンスのようにそれを使うようにしてください。

オブジェクトを既存のシーケンスに追加したいとします。それがオブジェクトのシーケンスの場合は、それらすべてを追加します。

try:
   my_sequence.extend(o)
except TypeError:
  my_sequence.append(o)

これに関する一つのトリックは、あなたが文字列や文字列のシーケンス、あるいはその両方で作業している場合です - 文字列はしばしば単一のオブジェクトとして考えられるので、それはトリッキーですが、それは文字のシーケンスでもあります。それは実際には単一長の文字列のシーケンスなので、それより悪いことです。

私は通常、単一の値またはシーケンスのいずれかしか受け入れないように自分のAPIを設計することを選択します - それによって物事が容易になります。必要に応じて値を渡すときに、単一の値を[ ]で囲むのは難しくありません。

(これは(are)シーケンスのように見えるので、文字列でエラーを引き起こす可能性があります。)

4
Chris Barker

型の__name__を使用して、変数の型を確認できます。

例:

>>> a = [1,2,3,4]  
>>> b = 1  
>>> type(a).__name__
'list'
>>> type(a).__== 'list'
True
>>> type(b).__== 'list'
False
>>> type(b).__name__
'int'
0

より複雑な型検証のためには、 typeguard のPython型ヒント注釈に基づく検証のアプローチが好きです。

from typeguard import check_type
from typing import List

try:
    check_type('mylist', [1, 2], List[int])
except TypeError as e:
    print(e)

非常に複雑な検証を非常にきれいで読みやすい方法で実行できます。

check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo) 
0
Granitosaurus