これは、入力がlist
/ tuple
ではなく、/ではないことを確認するために通常行うことですstr
。関数がstr
誤ってオブジェクトを渡すバグに何度も遭遇したため、ターゲット関数はfor x in lst
それlst
が実際にlist
またはであると想定しているためtuple
です。
assert isinstance(lst, (list, tuple))
私の質問は、これを達成するためのより良い方法はありますか?
これは、入力がlist
/ tuple
ではなく、/ではないことを確認するために通常行うことですstr
。関数がstr
誤ってオブジェクトを渡すバグに何度も遭遇したため、ターゲット関数はfor x in lst
それlst
が実際にlist
またはであると想定しているためtuple
です。
assert isinstance(lst, (list, tuple))
私の質問は、これを達成するためのより良い方法はありますか?
回答:
Python 2のみ(Python 3ではない):
assert not isinstance(lst, basestring)
実際に必要なものです。そうしないと、リストのように機能しますが、list
またはのサブクラスではない多くのものが見落とされますtuple
。
basestring
はなくなったので、を確認するだけですisinstance(lst, str)
。
set
ます。例えば、ジェネレーター式、イテレーターなどです。のようなエキゾチックなものがありmmap
、array
リストのように振る舞うようなそれほどエキゾチックではないものがあります。
lst
オリジナルがそうであった間、これが反復可能であることを保証しないことは注目に値します(たとえば、intはこのチェックに合格します)
assert isinstance(lst, (list, tuple)) and assert not isinstance(lst, basestring)
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__()
私が上記を変更したよりも基本的なようです。
srepr
非常に興味深いアイデアです。しかし、特別なケースが必要かどうかについては、私はあなたとは異なる意見を持っていますstr
。はい。str
これは、で無限再帰を引き起こす最も明白で一般的な反復可能オブジェクトですsrepr
。しかし、同じ理由で(正当な理由の有無にかかわらず)動作するユーザー定義のイテラブルを簡単に想像できます。特殊なケースstr
ではなく、このアプローチが無限の再帰に遭遇する可能性があることを認め、何らかの対処方法に同意する必要があります。私の提案を回答に投稿します。
srepr()
は問題ないと思います。別のリスト内にネストされたリストなどを処理するには、再帰関数が必要です。ただし、文字列の場合は、ではなくとして印刷する"foo"
方が適切<'f', 'o', 'o'>
です。したがって、文字列の明示的なチェックはここでは理にかなっています。また、反復が常に反復可能を返し、再帰が常にスタックオーバーフローを引き起こすデータ型の例は他にありません。そのため、これをテストするための特別なプロパティは必要ありません(「実用性が純粋さを上回る」)。
__iter__()
Pythonの3の方法を、しかし、Pythonの2にあなたがで括弧が欠落しているではないis_sequence()
、それは読んでください:return (not hasattr(arg, "strip") and (hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")))
H = "Hello"
if type(H) is list or type(H) is tuple:
## Do Something.
else
## Do Something.
if isinstance( H, (list, tuple) ): ...
より短くて明確です。
if type(H) in [list, tuple]:
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"
collections.Sequence
ことを知りませんでしたが、テストしたところ、継承されていることがわかりました。そうxrange
です。さらに良いことに、このテストはdict
との両方__getitem__
を含むを除外し__iter__
ます。
inspect.getmro(list)
含まれSequence
ていない理由は何ですか?がすべてを表示しないisinstance
場合に、私たちが何ができるかをどのように理解するべきgetmro
ですか?
Sequence
抽象クラスです。
PHP風味のPython:
def is_array(var):
return isinstance(var, (list, tuple))
__getitem__
。また、アレイモジュールもあるので、名前は誤解を招きやすいです。また、varはnumpy.ndarrayまたはその他の任意のタイプにすることもできます__getitem__
。正しい答えについては、stackoverflow.com / a / 1835259/470560を参照してください。
str
には__getitem__
、したがって、チェックは除外されませんstr
__getitem__
ここでのチェックは悪いアドバイスです。
一般的に言えば、オブジェクトを反復処理する関数が文字列だけでなく、タプルやリストでも機能するという事実は、バグよりも機能的です。あなたは確かに引数をチェックするためにタイピングを使うかダックすることができますisinstance
が、なぜあなたはそうすべきですか?
それは修辞的な質問のように聞こえますが、そうではありません。「なぜ引数の型をチェックする必要があるのか」に対する答え。おそらく、認識された問題ではなく、実際の問題の解決策を提案します。文字列が関数に渡されるとバグになるのはなぜですか?また、文字列がこの関数に渡されたときにバグであった場合、他の非リスト/タプルの反復可能オブジェクトが渡された場合もバグですか?なぜですか?
この質問に対する最も一般的な答えは、作成した開発者f("abc")
が、関数が作成したかのように動作することを期待していることであると思われf(["abc"])
ます。文字列内の文字を反復処理するユースケースをサポートするよりも、開発者自身を保護する方が理にかなっている状況がおそらくあります。しかし、私は最初にそれについて長くて難しいと思います。
読みやすさとベストプラクティスのためにこれを試してください。
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
それが役に立てば幸い。
AttributeError: module 'types' has no attribute 'ListType'
from typing import List
isinstance([1, 2, 3], List
True
isinstance("asd", List)
False
str
オブジェクトが持っていない__iter__
属性を
>>> hasattr('', '__iter__')
False
だからあなたはチェックをすることができます
assert hasattr(x, '__iter__')
そして、これはまたAssertionError
、他のすべての反復不可能なオブジェクトのための素晴らしいも発生させます。
編集: Timがコメントで言及しているように、これはpython 2.xでのみ機能し、3.xでは機能しません
hasattr('','__iter__')
が返されますTrue
。そしてもちろん、文字列を反復処理できるので、それは理にかなっています。
これはOPに直接回答することを目的としたものではありませんが、いくつかの関連するアイデアを共有したいと思いました。
上記の@stevehaの回答に非常に興味がありました。これは、アヒルのタイピングがうまくいかない例を示しているようです。しかし、考え直してみると、彼の例は、アヒルのタイピングが順応するのは難しいことを示唆していますが、特別な処理に値するものであることを示唆していませんstr
。
結局のところ、非str
型(たとえば、いくつかの複雑な再帰的構造を維持するユーザー定義型)は、@ steveha srepr
関数が無限再帰を引き起こす可能性があります。これは確かにかなり可能性は低いですが、この可能性を無視することはできません。したがって、の特別なケースstr
ではなく、無限再帰が発生したときにsrepr
何をしたいかを明確にする必要srepr
があります。
合理的なアプローチの1つはsrepr
、現時点で再帰を単純に中断することlist(arg) == [arg]
です。これは、実際にはstr
、なしでで問題を完全に解決しますisinstance
。
ただし、非常に複雑な再帰構造では、無限ループが発生する可能性list(arg) == [arg]
があります。したがって、上記のチェックは役立ちますが、それだけでは不十分です。再帰の深さに対するハードリミットのようなものが必要です。
私の要点は、任意の引数型を処理することを計画している場合、str
ダック型による処理は、(理論的には)遭遇する可能性のあるより一般的な型を処理するよりもはるかに簡単です。したがって、str
インスタンスを除外する必要性を感じた場合は、代わりに、引数が明示的に指定する数少ないタイプの1つのインスタンスであることを要求する必要があります。
str
。特殊なケースのコードが処理する一般的なケースは1つだけです。しかし、コードが検査できる新しい標準プロパティが存在する必要があるかもしれません。.__atomic__
たとえば、これ以上何かを分解することができないことを通知します。atomic()
Pythonに別の組み込み関数を追加するのはおそらく遅すぎますが、追加することもできfrom collections import atomic
ます。
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))
そして、私はそれがあなたのニーズを満たしていることを確認しました。
私はテストケースでこれを行います。
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))
ジェネレーターでテストされていないので、ジェネレーターに渡された場合、次の「収量」が残り、下流で問題が発生する可能性があります。しかし、再び、これは「単体テスト」です
「ダックタイピング」で、いかがですか
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
あるリスト(ないタプル)。そのため、この方法で割り当てが行われます。たぶん誰かがその理由を明らかにすることができます。
最も簡単な方法...とを使用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
文字列のようなオブジェクトを他のシーケンスのようなオブジェクトと区別するのに役立つ、ダックタイピングの別のバージョン。
文字列のようなオブジェクトの文字列表現は文字列そのものなので、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
およびあらゆる種類の反復可能なオブジェクトで機能します。
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)
assert (type(lst) == list) | (type(lst) == tuple), "Not a valid lst type, cannot be string"
これをしてください
if type(lst) in (list, tuple):
# Do stuff
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
str
では、文字列ではなくタイプを使用します。試してみるisinstance('my_string', collections.abc.Container)
と、戻ってくることがわかりますTrue
。これはメソッドをabc.Container
提供し、__contains__
文字列にはもちろんそれがあるためです。
私はこれを行う傾向があります(本当にそうしなければならなかった場合):
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
some_var
サブクラスであるクラスのインスタンスである場合はlist()
どうなりますか?「リストを使って何かをする」コードの下では完全に機能しますが、コードはそれをどうするべきかまったくわかりません。また、リストとタプルの違いを気にする必要はほとんどありません。すみません、-1。
type(tuple())
- tuple
行いません。リストも同じです。また、両方str
とunicode
拡張basestring
現実の文字列型であるので、あなたの代わりにそれをチェックしたいです、。
type(i) is list
。また、type(list())
ちょうどさlist
自体...最後に、これはサブクラスで正常に動作しません。場合はi
、実際とOrderedDict、またはnamedtupleのいくつかの種類である、扱います。このコードは、文字列としてです。