オブジェクトのタイプを判別しますか?


1791

変数がリスト、辞書、または何か他のものであるかどうかを判断する簡単な方法はありますか?どちらかのタイプのオブジェクトを取得していますが、その違いを区別できるようにする必要があります。


44
一般的に私はあなたに同意しますが、知っておくと役立つ状況もあります。この特定のケースでは、私は最終的にロールバックするいくつかの迅速なハッキングを行っていたので、今回は正解です。ただし、場合によっては(たとえば、リフレクションを使用する場合)、処理するオブジェクトのタイプを知ることが重要です。
ジャスティンエティエ2010

67
@ S.Lott私はそれに同意しません。タイプを知ることができることで、かなり多様な入力を処理しながら、正しいことを行うことができます。これにより、純粋なダックタイピングに依存することに固有のインターフェースの問題を回避できます(たとえば、ツリーの.bark()メソッドは、ドッグの場合とはまったく異なるものを意味します)。たとえば、いくつかの機能を果たす関数を作成できます。文字列(パスなど)、パスオブジェクト、またはリストを受け入れるファイル。インターフェースはすべて異なりますが、最終的な結果は同じです。そのファイルに対して何らかの操作を行います。
ロバートP

22
@ S.Lott不自然な例であることは明らかだと思いました。それにもかかわらず、それはアヒルのタイピングの大きな失敗点であり、try役に立たないものです。たとえば、ユーザーが文字列または配列を渡すことができることがわかっている場合、どちらもインデックス可能ですが、そのインデックスはまったく異なるものを意味します。それらのケースで単にtry-catchに依存すると、予期しない奇妙な方法で失敗します。1つの解決策は、別個のメソッドを作成することであり、もう1つの解決策は、小さな型チェックを追加することです。私は個人的に、ほとんど同じことをする複数のメソッドよりもポリモーフィックな振る舞いを好みます...しかし、それは私だけです:)
Robert P

22
@ S.Lott、単体テストはどうですか?テストで、関数が正しい型の何かを返していることを確認したい場合があります。非常に現実的な例は、クラスファクトリがある場合です。
Elliot Cameron

17
さほど不自然ではない例として、シリアライザ/デシリアライザを検討してください。定義により、ユーザー指定のオブジェクトとシリアル化された表現との間で変換します。シリアライザは、渡されたオブジェクトのタイプを判別する必要があります。また、ランタイムに問い合わせずにデシリアライズされたタイプを判別するための適切な情報がない場合があります(または、少なくとも、サニティチェックで不正なデータが入力される前にそれをキャッチする必要がある場合があります。あなたのシステム!)
Karl

回答:


1976

オブジェクトのタイプの識別に役立つ2つの組み込み関数があります。あなたは使用することができtype() ますが、オブジェクトの正確な型が必要な場合は、とisinstance()してチェックし、何かに対して、オブジェクトの型を。isistance()それは非常に堅牢で、型の継承もサポートしているため、通常はほとんどの場合を使用します。


オブジェクトの実際のタイプを取得するには、組み込みtype()関数を使用します。オブジェクトを唯一のパラメーターとして渡すと、そのオブジェクトのタイプオブジェクトが返されます。

>>> type([]) is list
True
>>> type({}) is dict
True
>>> type('') is str
True
>>> type(0) is int
True

もちろん、これはカスタムタイプでも機能します。

>>> class Test1 (object):
        pass
>>> class Test2 (Test1):
        pass
>>> a = Test1()
>>> b = Test2()
>>> type(a) is Test1
True
>>> type(b) is Test2
True

type()オブジェクトのみの即時型を返しますが、タイプの継承をご紹介することはできません。

>>> type(b) is Test1
False

それをカバーするには、isinstance関数を使用する必要があります。もちろん、これは組み込み型でも機能します。

>>> isinstance(b, Test1)
True
>>> isinstance(b, Test2)
True
>>> isinstance(a, Test1)
True
>>> isinstance(a, Test2)
False
>>> isinstance([], list)
True
>>> isinstance({}, dict)
True

isinstance()派生型も受け入れるため、通常はオブジェクトの型を確認するための推奨方法です。ですから、実際に(何らかの理由で)型オブジェクトを必要としない限り、使用してisinstance()好まれますtype()

の2番目のパラメーターisinstance()も型のタプルを受け入れるため、一度に複数の型をチェックすることができます。isinstance次に、オブジェクトがこれらのタイプのいずれかである場合、trueを返します。

>>> isinstance([], (tuple, list, set))
True

68
タイプはシングルトンなので、is代わりに使用する方が明確だと思います==
John La Rooy

18
@gnibbler、とにかくタイプチェック(最初から行うべきではない)の場合isinstanceは、いずれに==せよ推奨形式なので、どちらもis使用する必要はありません。
マイクグラハム、

23
@マイク・グラハム、それがtype最良の答えである場合があります。がisinstance最良の答えである場合と、アヒルのタイピングが最良の答えである場合があります。状況に適したものを選択できるように、すべてのオプションを知っておくことが重要です。
John La Rooy、2010

6
@gnibbler、それはそうかもしれませんが、私はまだtype(foo) is SomeTypeより良い状況に遭遇していませんisinstance(foo, SomeType)
マイクグラハム、

5
@poke:私はPEP8について完全に同意しますが、ここでストローマンを攻撃しています:Svenの議論の重要な部分はPEP8ではなくisinstance、ユースケース(型の範囲のチェック)に使用でき、構文もきれいになり、サブクラスをキャプチャできるという大きな利点があります。OrderedDict純粋な口述を受け入れるだけなので、誰かが使用するとコードが失敗するのを嫌います。
空飛ぶ羊

166

あなたはそれを使うことができますtype()

>>> a = []
>>> type(a)
<type 'list'>
>>> f = ()
>>> type(f)
<type 'tuple'>

40

try... exceptブロックを使用する方がよりPythonicかもしれません。このようにして、リストのようにいじくるクラスや、dictのようにいじくるクラスがある場合、そのタイプが実際に何であるかに関係なく、正しく動作します。

明確にするために、変数タイプ間の「違いを伝える」ための推奨される方法は、ダックタイピングと呼ばれるものを使用することです。変数が応答するメソッド(および戻り値のタイプ)が、サブルーチンが期待するものである限り、それを期待どおりに扱います。することが。たとえば、ブラケット演算子をgetattrsetattrでオーバーロードするクラスがあり、面白い内部スキーマを使用している場合、それがエミュレートしようとしているものである場合は、辞書として動作することが適切です。

type(A) is type(B)チェックのもう1つの問題Aは、がのサブクラスである場合、プログラムBで評価するとfalseいつになるかを評価することtrueです。オブジェクトがリストのサブクラスである場合、それはリストのように機能するはずです。他の回答で提示されているようにタイプをチェックすると、これを防ぐことができます。(isinstanceただし、機能します)。


16
ただし、アヒルのタイピングとは、違いを伝えることではありません。共通のインターフェースを使用することです。
ジャスティンエティエ11/11/18

5
注意してください-ほとんどのコーディングスタイルガイドでは、通常のコードの制御フローの一部として例外処理を使用しないことを推奨しています。これは通常、コードを読みにくくするためです。try... exceptは、エラーに対処したいが、タイプに基づいて動作を決定する場合には適していないソリューションです。
Rens van der Heijden 2016年

34

オブジェクトのインスタンスには、次のものもあります。

__class__

属性。これはPython 3.3コンソールから取得したサンプルです

>>> str = "str"
>>> str.__class__
<class 'str'>
>>> i = 2
>>> i.__class__
<class 'int'>
>>> class Test():
...     pass
...
>>> a = Test()
>>> a.__class__
<class '__main__.Test'>

Python 3.xとNew-Styleクラス(オプションでPython 2.6からアクセス可能)では、クラスとタイプがマージされているため、予期しない結果になる可能性があることに注意してください。主にこの理由から、タイプ/クラスをテストする私のお気に入りの方法は、組み込み関数isinstanceです。


2
最後のあなたのポイントは非常に重要です。type(obj)is Classは正しく機能していませんでしたが、isinstanceでうまくいきました。とにかくisinstanceが優先されることを理解していますが、受け入れられた回答で提案されているように、派生型をチェックするだけの場合よりも有益です。
mstbaum 2016

__class__Python 2.xではほとんど問題ありません。__class__属性を持たないPythonの唯一のオブジェクトは、古いスタイルのクラスAFAIKです。ところで、私はあなたのPython 3の懸念を理解していません-そのようなバージョンでは、すべてのオブジェクト__class__に適切なクラスを指す属性があります。
Alan Franzoni 2016年

21

Pythonオブジェクトのタイプを判別する

オブジェクトのタイプを決定する type

>>> obj = object()
>>> type(obj)
<class 'object'>

機能しますが、次のような二重のアンダースコア属性は避け__class__てください-それらは意味的にはパブリックではありません。

>>> obj.__class__ # avoid this!
<class 'object'>

型チェック

変数がリスト、辞書、または何か他のものであるかどうかを判断する簡単な方法はありますか?どちらかのタイプのオブジェクトを取得していますが、その違いを区別できるようにする必要があります。

まあそれは別の質問です、タイプを使用しないでください-使用isinstance

def foo(obj):
    """given a string with items separated by spaces, 
    or a list or tuple, 
    do something sensible
    """
    if isinstance(obj, str):
        obj = str.split()
    return _foo_handles_only_lists_or_tuples(obj)

これは、ユーザーがサブクラス化によって賢いまたは賢明な何かをしている場合をカバーしますstr-Liskov Substitutionの原則に従って、コードを壊すことなくサブクラスインスタンスを使用できるようにしたい- isinstanceこれをサポートします。

抽象化を使用する

さらに良いのは、collectionsまたはから特定の抽象基本クラスを探すことですnumbers

from collections import Iterable
from numbers import Number

def bar(obj):
    """does something sensible with an iterable of numbers, 
    or just one number
    """
    if isinstance(obj, Number): # make it a 1-tuple
        obj = (obj,)
    if not isinstance(obj, Iterable):
        raise TypeError('obj must be either a number or iterable of numbers')
    return _bar_sensible_with_iterable(obj)

または、明示的に型チェックを行わないでください

または、おそらく何よりも、ダックタイピングを使用し、コードを明示的に型チェックしないでください。ダックタイピングは、よりエレガントで冗長性の低いLiskov Substitutionをサポートします。

def baz(obj):
    """given an obj, a dict (or anything with an .items method) 
    do something sensible with each key-value pair
    """
    for key, value in obj.items():
        _baz_something_sensible(key, value)

結論

  • type実際にインスタンスのクラスを取得するために使用します。
  • isinstance実際のサブクラスまたは登録された抽象化を明示的にチェックするために使用します。
  • そして、意味のある場所で型チェックをしないでください。

明示的にチェックする代わりに、常にtry/ がありexceptます。
toonarmycaptain 2017

おそらく、渡される型についてユーザーがわからない場合は、ユーザーがそれを行います。例外を処理するための非常に優れた方法がない限り、例外処理で正しい実装を混乱させたくありません。発生した例外は、使用方法を修正する必要があることをユーザーに通知するのに十分なはずです。
アーロンホール

13

type()またはを使用できますisinstance()

>>> type([]) is list
True

list同じ名前の現在のスコープで変数を割り当てることにより、クローバーまたはその他のタイプを作成できることに注意してください。

>>> the_d = {}
>>> t = lambda x: "aight" if type(x) is dict else "NOPE"
>>> t(the_d) 'aight'
>>> dict = "dude."
>>> t(the_d) 'NOPE'

上記ではdict、文字列に再割り当てされるため、テストは次のようになります。

type({}) is dict

...失敗します。

これを回避してtype()より注意深く使用するには:

>>> import __builtin__
>>> the_d = {}
>>> type({}) is dict
True
>>> dict =""
>>> type({}) is dict
False
>>> type({}) is __builtin__.dict
True

2
この場合、組み込みデータ型の名前をシャドーイングすることは不適切であることを指摘する必要があるかどうかはわかりません。あなたのdict文字列も同様に、他のコードの多くのために失敗しますdict([("key1", "value1"), ("key2", "value2")])。これらの種類の問題に対する答えは、「それをしないでください」です。組み込みの型名を隠したり、正常に動作することを期待したりしないでください。
Blckknght 2014年

3
私は「それをしないでください」の部分であなたに同意します。しかし、確かに誰かに何かをしないように言うには、少なくともなぜそうしないのかを説明する必要があります。これはまさにそれを行うための適切な機会だと思いました。私は慎重な方法が醜く見え、なぜ彼らがそれをしたくないのかを説明し、彼らに決定を任せることを意味しました。
deed02392 14年

type()は、クラシックインスタンスのPython 2.xでは期待どおりに機能しません。
Alan Franzoni 2016年

5

質問はかなり古いですが、私は自分自身で適切な方法を見つけている間、これに偶然出会いました。少なくともPython 2.xについては、まだ明確にする必要があると思います(Python 3では確認しませんでしたが、クラシッククラスで問題が発生するためそのようなバージョンではなくなっていますが、それはおそらく問題ではありません)。

ここで私はタイトルの質問に答えようとしています:任意のオブジェクトのタイプをどのように判断できますか?isinstanceの使用または使用しない他の提案は、多くのコメントと回答で問題ありませんが、私はそれらの懸念に対処していません。

このtype()アプローチの主な問題は、古いスタイルのインスタンスでは正しく機能しないことです。

class One:
    pass

class Two:
    pass


o = One()
t = Two()

o_type = type(o)
t_type = type(t)

print "Are o and t instances of the same class?", o_type is t_type

このスニペットを実行すると、次のようになります。

Are o and t instances of the same class? True

これは、ほとんどの人が期待することではないと私は主張します。

__class__アプローチは正確に最も近いですが、それは1重要なケースでは動作しません:渡されたときにオブジェクト古いスタイルのあるクラスこれらのオブジェクトは、そのような属性が欠けているので、(!ないインスタンス)。

これは、そのような正当な質問を一貫した方法で満たすと私が考えることができる最小のコードスニペットです。

#!/usr/bin/env python
from types import ClassType
#we adopt the null object pattern in the (unlikely) case
#that __class__ is None for some strange reason
_NO_CLASS=object()
def get_object_type(obj):
    obj_type = getattr(obj, "__class__", _NO_CLASS)
    if obj_type is not _NO_CLASS:
        return obj_type
    # AFAIK the only situation where this happens is an old-style class
    obj_type = type(obj)
    if obj_type is not ClassType:
        raise ValueError("Could not determine object '{}' type.".format(obj_type))
    return obj_type

5

isinstanceの使用には注意してください

isinstance(True, bool)
True
>>> isinstance(True, int)
True

しかしタイプ

type(True) == bool
True
>>> type(True) == int
False

3

前の回答とは別に、collections.abcアヒルのタイピングを補完するいくつかの抽象基本クラス(ABC)が含まれていることには言及する価値があります。

たとえば、何かがリストであるかどうかを明示的にチェックする代わりに:

isinstance(my_obj, list)

あなたが持っているオブジェクトがアイテムの取得を許可しているかどうかを確認することだけに興味がある場合は、以下を使用できますcollections.abc.Sequence

from collections.abc import Sequence
isinstance(my_obj, Sequence) 

アイテムの取得、設定削除を許可するオブジェクト(つまり、可変シーケンス)に厳密に関心がある場合は、を選択しcollections.abc.MutableSequenceます。

他の多くのいろはは、そこに定義されたMappingマップとして使用することができるオブジェクトに対してIterableCallable、エトセトラ。これらすべての完全なリストは、のドキュメントで確認できますcollections.abc


1

一般に、クラス名を持つオブジェクトから文字列を抽出できます。

str_class = object.__class__.__name__

比較のために使用します

if str_class == 'dict':
    # blablabla..
elif str_class == 'customclass':
    # blebleble..

1

多くの実際的なケースでは、を使用する代わりに、typeまたはisinstanceを使用することもできます@functools.singledispatch。これは、ジェネリック関数異なるタイプに対して同じ操作を実装する複数の関数で構成される関数)を定義するために使用されます。

つまり、次のようなコードがある場合に使用します。

def do_something(arg):
    if isinstance(arg, int):
        ... # some code specific to processing integers
    if isinstance(arg, str):
        ... # some code specific to processing strings
    if isinstance(arg, list):
        ... # some code specific to processing lists
    ...  # etc

これがどのように機能するかの小さな例です:

from functools import singledispatch


@singledispatch
def say_type(arg):
    raise NotImplementedError(f"I don't work with {type(arg)}")


@say_type.register
def _(arg: int):
    print(f"{arg} is an integer")


@say_type.register
def _(arg: bool):
    print(f"{arg} is a boolean")
>>> say_type(0)
0 is an integer
>>> say_type(False)
False is a boolean
>>> say_type(dict())
# long error traceback ending with:
NotImplementedError: I don't work with <class 'dict'>

さらに、抽象クラスを使用して、一度に複数のタイプをカバーできます。

from collections.abc import Sequence


@say_type.register
def _(arg: Sequence):
    print(f"{arg} is a sequence!")
>>> say_type([0, 1, 2])
[0, 1, 2] is a sequence!
>>> say_type((1, 2, 3))
(1, 2, 3) is a sequence!

0

type()isinstance()特にの場合よりも優れたソリューションですbooleans

TrueそしてFalseちょうどそれが平均キーワードである10pythonで。したがって、

isinstance(True, int)

そして

isinstance(False, int)

どちらも戻りTrueます。両方のブール値は整数のインスタンスです。type()ただし、より賢いです。

type(True) == int

を返しますFalse

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