オブジェクトがリストまたはタプル(文字列ではない)かどうかを確認する方法は?


443

これは、入力がlist/ tupleではなく、/ではないことを確認するために通常行うことですstr。関数がstr誤ってオブジェクトを渡すバグに何度も遭遇したため、ターゲット関数はfor x in lstそれlstが実際にlistまたはであると想定しているためtupleです。

assert isinstance(lst, (list, tuple))

私の質問は、これを達成するためのより良い方法はありますか?


9
type(lst)はリストですか?
jackalope 2014年

1
isinstance(key、six.string_types)ではありません
wyx 2018

回答:


332

Python 2のみ(Python 3ではない):

assert not isinstance(lst, basestring)

実際に必要なものです。そうしないと、リストのように機能しますが、listまたはのサブクラスではない多くのものが見落とされますtuple


91
はい、これが正解です。Python 3でbasestringはなくなったので、を確認するだけですisinstance(lst, str)
steveha 2009

5
リストのように反復できるものがたくさんありsetます。例えば、ジェネレーター式、イテレーターなどです。のようなエキゾチックなものがありmmaparrayリストのように振る舞うようなそれほどエキゾチックではないものがあります。
Nick Craig-Wood

50
lstオリジナルがそうであった間、これが反復可能であることを保証しないことは注目に値します(たとえば、intはこのチェックに合格します)
Peter Gibson

11
@PeterGibson-2つの組み合わせは、有効でより制限的なチェックを提供し、1)lstが反復可能である、2)lstが文字列ではないことを確認します。 assert isinstance(lst, (list, tuple)) and assert not isinstance(lst, basestring)
strongMA 2013年

4
さて、このソリューションは文字列派生型のみをチェックしますが、整数、倍精度浮動小数点型、またはその他の反復不可能な型についてはどうですか?
エネコアロンソ

171

Pythonでは「ダックタイピング」を使用することを忘れないでください。したがって、リストのように機能するものはすべてリストとして扱うことができます。したがって、リストのタイプをチェックせず、リストのように機能するかどうかを確認してください。

しかし、文字列もリストのように振る舞い、多くの場合それは私たちが望むものではありません。それさえ問題になる時があります!したがって、文字列を明示的にチェックしてから、ダックタイピングを使用してください。

これは私がおもしろく書いた関数です。repr()山かっこ( '<'、 '>')でシーケンスを出力する特別なバージョンです。

def srepr(arg):
    if isinstance(arg, basestring): # Python 3: isinstance(arg, str)
        return repr(arg)
    try:
        return '<' + ", ".join(srepr(x) for x in arg) + '>'
    except TypeError: # catch when for loop fails
        return repr(arg) # not a sequence so just return repr

これは全体的にクリーンでエレガントです。しかし、そのisinstance()チェックはそこで何をしているのでしょうか?それは一種のハックです。しかし、それは不可欠です。

この関数は、リストのように機能するものに対して再帰的に呼び出します。文字列を特別に処理しなかった場合、リストのように扱われ、一度に1文字に分割されます。しかし、その後、再帰呼び出しは各文字をリストとして処理しようとします-そしてそれはうまくいきます!1文字の文字列でもリストとして機能します。関数は、スタックオーバーフローが発生するまで再帰的に呼び出し続けます。

このような関数は、実行する作業を分解する各再帰呼び出しに依存しますが、特殊な文字列にする必要があります。1文字の文字列のレベルより下の文字列は分解できません。 -文字列はリストのように機能します。

注:try/ exceptは、意図を表す最もクリーンな方法です。しかし、このコードが何らかの形で時間を重視する場合argは、それがシーケンスであるかどうかを確認するために、ある種のテストに置き換えたい場合があります。タイプをテストするのではなく、おそらく動作をテストする必要があります。.strip()メソッドがある場合は文字列なので、シーケンスとは見なさないでください。それ以外の場合は、インデックス可能または反復可能であれば、シーケンスです。

def is_sequence(arg):
    return (not hasattr(arg, "strip") and
            hasattr(arg, "__getitem__") or
            hasattr(arg, "__iter__"))

def srepr(arg):
    if is_sequence(arg):
        return '<' + ", ".join(srepr(x) for x in arg) + '>'
    return repr(arg)

編集:私は最初にチェックのために上記を書きました__getslice__()が、collectionsモジュールのドキュメントでは、興味深い方法はであることに気付きました__getitem__()。これは理にかなっています、それがあなたがオブジェクトにインデックスを付ける方法です。それは__getslice__()私が上記を変更したよりも基本的なようです。


2
@stantonk、そう言ってくれてありがとうございますが、私がこれを書いたときにはすでに受け入れられた回答があったと思いますし、受け入れられた回答が変更されることを本当に期待していません。
steveha

@steveha:srepr非常に興味深いアイデアです。しかし、特別なケースが必要かどうかについては、私はあなたとは異なる意見を持っていますstr。はい。strこれは、で無限再帰を引き起こす最も明白で一般的な反復可能オブジェクトですsrepr。しかし、同じ理由で(正当な理由の有無にかかわらず)動作するユーザー定義のイテラブルを簡単に想像できます。特殊なケースstrではなく、このアプローチが無限の再帰に遭遇する可能性があることを認め、何らかの対処方法に同意する必要があります。私の提案を回答に投稿します。
最大

1
これは間違いなく正しい道だと思います。ただし、(このシナリオでは文字列の)特殊なケースを処理するために、「人間はどのように違いを見分けることができるのか」という質問をする方がよいと思います。たとえば、電子メールアドレスのリストまたは単一の電子メールアドレスである関数の引数を考えます(文字列は単なる文字のリストであることに注意してください)。この変数を人間に与えます。どのようにしてそれがわかるのでしょうか?私が考えることができる最も簡単な方法は、リストの各項目にいくつの文字があるかを確認することです。1より大きい場合、引数は文字のリストであってはなりません。
Josh

1
私はこれについて少し考えて、他の数人と話し合ったのですが、現状でsrepr()は問題ないと思います。別のリスト内にネストされたリストなどを処理するには、再帰関数が必要です。ただし、文字列の場合は、ではなくとして印刷する"foo"方が適切<'f', 'o', 'o'>です。したがって、文字列の明示的なチェックはここでは理にかなっています。また、反復が常に反復可能を返し、再帰が常にスタックオーバーフローを引き起こすデータ型の例は他にありません。そのため、これをテストするための特別なプロパティは必要ありません(「実用性が純粋さを上回る」)。
steveha 2013年

1
文字列が持っているので、これは、Pythonの3では動作しません__iter__()Pythonの3の方法を、しかし、Pythonの2にあなたがで括弧が欠落しているではないis_sequence()、それは読んでください:return (not hasattr(arg, "strip") and (hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")))
MiniQuark

124
H = "Hello"

if type(H) is list or type(H) is tuple:
    ## Do Something.
else
    ## Do Something.

11
他のコメンターが指摘したように、アヒルのタイピングのPythonイディオムを使用しないことを除いて(ただし、質問に直接かつ明確に回答します)。
Soren Bjornstad

7
この答えは、ダックタイピングを許可せず、サブクラス化の単純なケース(典型的な例はnamedtupleクラスです)でも失敗するため、他の答えよりも受け入れられません。
Philippe Gauthier

11
「ダックタイピングを許可しない」ことは、特にこの回答が実際に質問に回答することを考えると、回答の受け入れを悪くしません。
ペトリ

4
私はこの回答に賛成していますが、if isinstance( H, (list, tuple) ): ...より短くて明確です。
shahar_m 2017

2
代替構文:if type(H) in [list, tuple]:
ステファン・シンドラー

77

Python 3の場合:

import collections.abc

if isinstance(obj, collections.abc.Sequence) and not isinstance(obj, str):
    print("obj is a sequence (list, tuple, etc) but not a string")

バージョン3.3で変更:コレクションの抽象基本クラスをcollections.abcモジュールに移動しました。下位互換性のために、バージョン3.8が機能しなくなるまで、これらはこのモジュールでも引き続き表示されます。

Python 2の場合:

import collections

if isinstance(obj, collections.Sequence) and not isinstance(obj, basestring):
    print "obj is a sequence (list, tuple, etc) but not a string or unicode"

5
うわー!これは本当にうまく機能し、他のどの正解よりもはるかに簡潔です。組み込みの型が継承するcollections.Sequenceことを知りませんでしたが、テストしたところ、継承されていることがわかりました。そうxrangeです。さらに良いことに、このテストはdictとの両方__getitem__を含むを除外し__iter__ます。
Neil Mayhew 2016年

の結果がinspect.getmro(list)含まれSequenceていない理由は何ですか?がすべてを表示しないisinstance場合に、私たちが何ができるかをどのように理解するべきgetmroですか?
Steve Jorgensen 2017年

@SteveJorgensenメソッド解決順序は、Pythonがクラスで使用する適切なメソッドを検索するために使用するクラス検索パスを定義します。Sequence抽象クラスです。
スザンシャキャ

3
Python3では、isinstance(obj、basestring)をisinstance(obj、str)に置き換えることができます。これでうまくいくはずです。
エイドリアンキースター

2
Python 3ではisinstance(obj、bytes)は必要ありませんが、バイトを列挙するだけでなく、物事のリストが必要な場合
Erik Aronesty

35

PHP風味のPython:

def is_array(var):
    return isinstance(var, (list, tuple))

6
Pythonはアヒル型の言語なので、varにattributeがあるかどうかを確認する必要があります__getitem__。また、アレイモジュールもあるので、名前は誤解を招きやすいです。また、varはnumpy.ndarrayまたはその他の任意のタイプにすることもできます__getitem__。正しい答えについては、stackoverflow.com / a / 1835259/470560を参照してください。
peterhil 2012

9
@peterhil strには__getitem__、したがって、チェックは除外されませんstr
erikbwork

9
辞書もそうです。__getitem__ここでのチェックは悪いアドバイスです。
ペトリ

10

一般的に言えば、オブジェクトを反復処理する関数が文字列だけでなく、タプルやリストでも機能するという事実は、バグよりも機能的です。あなたは確かに引数をチェックするためにタイピングを使うかダックすることができますisinstanceが、なぜあなたはそうすべきですか?

それは修辞的な質問のように聞こえますが、そうではありません。「なぜ引数の型をチェックする必要があるのか​​」に対する答え。おそらく、認識された問題ではなく、実際の問題の解決策を提案します。文字列が関数に渡されるとバグになるのはなぜですか?また、文字列がこの関数に渡されたときにバグであった場合、他の非リスト/タプルの反復可能オブジェクトが渡された場合もバグですか?なぜですか?

この質問に対する最も一般的な答えは、作成した開発者f("abc")が、関数が作成したかのように動作することを期待していることであると思われf(["abc"])ます。文字列内の文字を反復処理するユースケースをサポートするよりも、開発者自身を保護する方が理にかなっている状況がおそらくあります。しかし、私は最初にそれについて長くて難しいと思います。


16
「しかし、私は最初にそれについて長くて難しいと思います。」私はしません。関数がリスト-y関数であると想定されている場合、はい、それらは同じように扱われます(つまり、リストが与えられた場合、それを逆に吐き出します)。ただし、引数の1つが文字列または文字列のリストのいずれかである関数である場合(これはかなり一般的なニーズです)、開発者がその関数を使用して配列内に常にパラメーターを入力するように強制することは少し多く思われます。また、JSON入力などの処理方法についても検討してください。あなたは間違いなく文字列とは異なるオブジェクトのリストを扱いたいでしょう。
ジョーダンライター、

8

読みやすさとベストプラクティスのためにこれを試してください。

Python2

import types
if isinstance(lst, types.ListType) or isinstance(lst, types.TupleType):
    # Do something

Python3

import typing
if isinstance(lst, typing.List) or isinstance(lst, typing.Tuple):
    # Do something

それが役に立てば幸い。


Python 3.6.5:AttributeError: module 'types' has no attribute 'ListType'
Juha Untinen

1
Python 3では、次のとおりですfrom typing import Listisinstance([1, 2, 3], ListTrueisinstance("asd", List)False
。-

5

strオブジェクトが持っていない__iter__属性を

>>> hasattr('', '__iter__')
False 

だからあなたはチェックをすることができます

assert hasattr(x, '__iter__')

そして、これはまたAssertionError、他のすべての反復不可能なオブジェクトのための素晴らしいも発生させます。

編集: Timがコメントで言及しているように、これはpython 2.xでのみ機能し、3.xでは機能しません


8
注意:Python 3ではhasattr('','__iter__')が返されますTrue。そしてもちろん、文字列を反復処理できるので、それは理にかなっています。
Tim Pietzcker、2009

1
本当に?知らなかった。これは問題のエレガントな解決策だといつも思っていました。
萌え

1
このテストはpyodbc.Rowでは機能しませんでした。iter __()はありませんが、多かれ少なかれリストのように動作します(「__setitemも定義しています)。あなたはその要素をうまく反復することができます。len()関数が機能し、その要素にインデックスを付けることができます。すべてのタイプのリストをキャッチし、文字列を除外する適切な組み合わせを見つけるのに苦労しています。私は「のチェックのために解決すると思うのgetItem」と「lenを明示的basestringを除外して」。
haridsv 2010年

5

これはOPに直接回答することを目的としたものではありませんが、いくつかの関連するアイデアを共有したいと思いました。

上記の@stevehaの回答に非常に興味がありました。これは、アヒルのタイピングがうまくいかない例を示しているようです。しかし、考え直してみると、彼の例は、アヒルのタイピングが順応するのは難しいことを示唆していますが、特別な処理に値するものであることを示唆していませstr

結局のところ、非str型(たとえば、いくつかの複雑な再帰的構造を維持するユーザー定義型)は、@ steveha srepr関数が無限再帰を引き起こす可能性があります。これは確かにかなり可能性は低いですが、この可能性を無視することはできません。したがって、の特別なケースstrではなく、無限再帰が発生したときにsrepr何をしたいかを明確にする必要sreprがあります。

合理的なアプローチの1つはsrepr、現時点で再帰を単純に中断することlist(arg) == [arg]です。これは、実際にはstr、なしでで問題を完全に解決しますisinstance

ただし、非常に複雑な再帰構造では、無限ループが発生する可能性list(arg) == [arg]があります。したがって、上記のチェックは役立ちますが、それだけでは不十分です。再帰の深さに対するハードリミットのようなものが必要です。

私の要点は、任意の引数型を処理することを計画している場合、strダック型による処理は、(理論的には)遭遇する可能性のあるより一般的な型を処理するよりもはるかに簡単です。したがって、strインスタンスを除外する必要性を感じた場合は、代わりに、引数が明示的に指定する数少ないタイプの1つのインスタンスであることを要求する必要があります。


1
うーん、私はあなたの考え方が好きです。私のコードが実用的であると主張することはできないと思いますstr。特殊なケースのコードが処理する一般的なケースは1つだけです。しかし、コードが検査できる新しい標準プロパティが存在する必要があるかもしれません。.__atomic__たとえば、これ以上何かを分解することができないことを通知します。atomic()Pythonに別の組み込み関数を追加するのはおそらく遅すぎますが、追加することもできfrom collections import atomicます。
steveha 2012年

5

is_sequenceという名前の関数がtensorflowにあります。

def is_sequence(seq):
  """Returns a true if its input is a collections.Sequence (except strings).
  Args:
    seq: an input sequence.
  Returns:
    True if the sequence is a not a string and is a collections.Sequence.
  """
  return (isinstance(seq, collections.Sequence)
and not isinstance(seq, six.string_types))

そして、私はそれがあなたのニーズを満たしていることを確認しました。


2

私はテストケースでこれを行います。

def assertIsIterable(self, item):
    #add types here you don't want to mistake as iterables
    if isinstance(item, basestring): 
        raise AssertionError("type %s is not iterable" % type(item))

    #Fake an iteration.
    try:
        for x in item:
            break;
    except TypeError:
        raise AssertionError("type %s is not iterable" % type(item))

ジェネレーターでテストされていないので、ジェネレーターに渡された場合、次の「収量」が残り、下流で問題が発生する可能性があります。しかし、再び、これは「単体テスト」です


2

「ダックタイピング」で、いかがですか

try:
    lst = lst + []
except TypeError:
    #it's not a list

または

try:
    lst = lst + ()
except TypeError:
    #it's not a tuple

それぞれ。これはisinstance/ hasattrイントロスペクションを回避します。

また、その逆も確認できます。

try:
    lst = lst + ''
except TypeError:
    #it's not (base)string

すべてのバリアントが実際に変数の内容を変更するわけではありませんが、再割り当てを意味します。状況によってはこれが望ましくないのかどうかわかりません。

興味深いことに、「代わりに」の割り当てを+=一切TypeError場合はどのような場合には上げないであろうlstあるリスト(ないタプル)。そのため、この方法で割り当てが行われます。たぶん誰かがその理由を明らかにすることができます。


1

最も簡単な方法...とを使用anyしてisinstance

>>> console_routers = 'x'
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
False
>>>
>>> console_routers = ('x',)
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
True
>>> console_routers = list('x',)
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
True

1

文字列のようなオブジェクトを他のシーケンスのようなオブジェクトと区別するのに役立つ、ダックタイピングの別のバージョン。

文字列のようなオブジェクトの文字列表現は文字列そのものなので、strコンストラクタから等しいオブジェクトが返されるかどうかを確認できます。

# If a string was passed, convert it to a single-element sequence
if var == str(var):
    my_list = [var]

# All other iterables
else: 
    my_list = list(var)

これは、互換性のあるすべてのオブジェクト、strおよびあらゆる種類の反復可能なオブジェクトで機能します。


0

Python 3にはこれがあります:

from typing import List

def isit(value):
    return isinstance(value, List)

isit([1, 2, 3])  # True
isit("test")  # False
isit({"Hello": "Mars"})  # False
isit((1, 2))  # False

したがって、リストとタプルの両方をチェックするには、次のようになります。

from typing import List, Tuple

def isit(value):
    return isinstance(value, List) or isinstance(value, Tuple)

0
assert (type(lst) == list) | (type(lst) == tuple), "Not a valid lst type, cannot be string"

2
これはこれを行うための大丈夫な方法ですか?
ersh

1
SOへようこそ。このコードが質問に答える理由の説明は役に立ちます。
ニック

もちろん、パイプはとして扱われるため、これに似たメソッドを使用します。タイプはリストであるか、エラー処理のためにカスタムメッセージエラーを出力するタプルである必要があると主張しています。私はそれが質問に答えると信じていますが、私はそれが最も最適化されたコードを書くことにこだわり続けようとしているので、これを行う効果的な方法であるかのように興味を持っていました。しかし、このコードがリスト/タプルのように機能する可能性があるが、どちらのサブクラスでもないものをこのコードが見逃しているかどうかは、受け入れられた回答がその可能性にどのように対処しているか不明です。ありがとう!
ersh

-1

これをしてください

if type(lst) in (list, tuple):
    # Do stuff

5
isinstance(lst、(list、tuple))
Davi Lima

@DaviLima OK、それは別の方法です。しかし、組み込み型にはtype()が推奨され、クラスにはインスタンスが推奨されます。
ATOzTOA 2017

-1

Python> 3.6

import collections
isinstance(set(),collections.abc.Container)
True
isinstance([],collections.abc.Container)
True
isinstance({},collections.abc.Container)
True
isinstance((),collections.abc.Container)
True
isinstance(str,collections.abc.Container)
False

2
最後のチェックstrでは、文字列ではなくタイプを使用します。試してみるisinstance('my_string', collections.abc.Container)と、戻ってくることがわかりますTrue。これはメソッドをabc.Container提供し、__contains__文字列にはもちろんそれがあるためです。
ジョージー

-6

私はこれを行う傾向があります(本当にそうしなければならなかった場合):

for i in some_var:
   if type(i) == type(list()):
       #do something with a list
   elif type(i) == type(tuple()):
       #do something with a tuple
   elif type(i) == type(str()):
       #here's your string

5
あなたはほとんどこれを行うべきではありません。のsome_varサブクラスであるクラスのインスタンスである場合はlist()どうなりますか?「リストを使って何かをする」コードの下では完全に機能しますが、コードはそれをどうするべきかまったくわかりません。また、リストとタプルの違いを気にする必要はほとんどありません。すみません、-1。
steveha 2009

1
書き込みを行う必要はありませんtype(tuple())- tuple行いません。リストも同じです。また、両方strunicode拡張basestring現実の文字列型であるので、あなたの代わりにそれをチェックしたいです、。
MOD

@DrBloodmoney:偶発的な反対投票。(簡単に)回答を編集して、反対票を削除してください。
SabreWolfy 2012

平等は、型にとって意味のある比較のようには思えません。代わりにIDをテストしますtype(i) is list。また、type(list())ちょうどさlist自体...最後に、これはサブクラスで正常に動作しません。場合はi、実際とOrderedDict、またはnamedtupleのいくつかの種類である、扱います。このコードは、文字列としてです。
ブクソール2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.