web-dev-qa-db-ja.com

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

これは私が入力がlist/Tupleであることを確かめるために私が通常することです - しかしstrではありません。関数が誤ってstrオブジェクトを渡すバグに遭遇したことが何度もあり、ターゲット関数はlstが実際にはlistまたはTupleであると仮定してfor x in lstを行います。

assert isinstance(lst, (list, Tuple))

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

410

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

assert not isinstance(lst, basestring)

そうでなければ、リストのように振る舞うがlistTupleのサブクラスではない多くのことを見逃してしまいます。

322
Nick Craig-Wood

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文字の文字列でもリストとして機能します。この関数は、スタックがオーバーフローするまでそれ自身を再帰的に呼び出し続けます。

このような関数は、実行する作業を細分化する各再帰呼び出しに依存しますが、特別な場合には文字列を使用する必要があります。文字ストリングはリストのように機能します。

注意: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__()より基本的なようですので、私は上記を変更しました。

168
steveha
H = "Hello"

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

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"

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 or unicode")

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

61
suzanshakya

PHPフレーバーを持つPython:

def is_array(var):
    return isinstance(var, (list, Tuple))
31
Cesar

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

それは修辞的な質問のように聞こえますが、そうではありません。 「なぜ引数の型をチェックすべきなのか」に対する答え。認識されている問題ではなく、実際の問題に対する解決策を提案することになるでしょう。文字列が関数に渡されるとなぜそれがバグなのですか?また、もし文字列がこの関数に渡されたときにそれがバグであるならば、それが他のリスト/タプルでないイテラブルがそれに渡されたならばそれもバグですか?どうしてですか、どうしてですか?

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

10
Robert Rossney

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

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

それが役に立てば幸い。

7
Om Prakash

strオブジェクトは__iter__属性を持っていません

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

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

assert hasattr(x, '__iter__')

これはまた、他の反復不可能なオブジェクトに対してもNice AssertionErrorを発生させます。

編集: Timがコメントで述べているように、これはpython 2.xでのみ動作し、3.xでは動作しません。

6
Moe

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

私は上記の@stevehaの回答に非常に興味を持っていました。これは、アヒルの打ち手が壊れているように見える例を示しているようでした。しかし考え直すと、彼の例はアヒルの型付けに従うのが難しいことを示唆していますが、notstrが特別な扱いに値することを示唆しています。

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

list(arg) == [arg]の瞬間にsrepr内の再帰を単純に破ることが合理的な方法の1つであるように思われるかもしれません。実際、これはstrがなくてもisinstanceに関する問題を完全に解決します。

しかし、本当に複雑な再帰的構造はlist(arg) == [arg]が決して起こらない無限ループを引き起こすかもしれません。したがって、上記のチェックは便利ですが、それだけでは不十分です。再帰の深さを厳しく制限するようなものが必要です。

私がポイントしているのは、もしあなたが任意の引数型を扱うつもりなら、アヒルタイピングによるstrの扱いはあなたが(理論的に)遭遇するより一般的な型を扱うよりはるかに簡単であるということです。 strインスタンスを除外する必要があると感じる場合は、代わりに、引数が明示的に指定された数種類のうちの1つのインスタンスであることを要求してください。

5
max

このような 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))

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

3
Lerner Zhang

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

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
FlipMcF

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

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 list Tuple ではない)の場合、+= no lstが「インプレース」割り当てで発生します。割り当てがこのように行われるのはそのためです。たぶん誰かがその理由を明らかにすることができます。

1
utobi

最も簡単な方法... anyisinstanceを使う

>>> 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
abarik

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
Juha Untinen
assert (type(lst) == list) | (type(lst) == Tuple), "Not a valid lst type, cannot be string"
0
ersh

これだけ

if type(lst) in (list, Tuple):
    # Do stuff
0
ATOzTOA