18

シーケンスまたは単一の値のいずれかのパラメーターを受け入れる関数を作成したいと考えています。値の型は str や int などですが、ハードコードされたリストに制限したくありません。言い換えれば、パラメーター X がシーケンスなのか、後で特別なケースを避けるためにシーケンスに変換する必要があるものなのかを知りたいのです。私はそれをできた

type(X) in (list, tuple)

しかし、私が気付いていない他のシーケンスタイプがあり、共通の基本クラスがない可能性があります。

-N.

編集:これらの回答のほとんどが役に立たない理由については、以下の「回答」を参照してください。多分あなたは提案するより良い何かを持っています。

4

12 に答える 12

19

2.6 以降では、抽象基本クラスを使用します。

>>> import collections
>>> isinstance([], collections.Sequence)
True
>>> isinstance(0, collections.Sequence)
False

さらに、ABC は、文字列をシーケンスと見なさないなどの例外を考慮してカスタマイズできます。ここに例があります:

import abc
import collections

class Atomic(object):
    __metaclass__ = abc.ABCMeta
    @classmethod
    def __subclasshook__(cls, other):
        return not issubclass(other, collections.Sequence) or NotImplemented

Atomic.register(basestring)

登録後、Atomicクラスをisinstanceおよびissubclassで使用できます。

assert isinstance("hello", Atomic) == True

これは、例外をルールに登録するだけでよく、コードの外部ユーザーは独自のリストを登録できるため、ハードコードされたリストよりもはるかに優れています。

Python 3では、メタクラスを指定するための構文が変更され、basestring抽象スーパークラスが削除されたため、代わりに次のようなものを使用する必要があることに注意してください。

class Atomic(metaclass=abc.ABCMeta):
    @classmethod
    def __subclasshook__(cls, other):
        return not issubclass(other, collections.Sequence) or NotImplemented

Atomic.register(str)

必要に応じて、Python 2.6+と3.x の両方と互換性のあるコードを作成することもできますが、そのためには、必要な抽象基本クラスを動的に作成するやや複雑な手法を使用する必要があり、それによってメタクラスの構文の違いによる構文エラーを回避できます。 . これは本質的に、Benjamin Peterson のsixモジュールのwith_metaclass()関数が行うことと同じです。

class _AtomicBase(object):
    @classmethod
    def __subclasshook__(cls, other):
        return not issubclass(other, collections.Sequence) or NotImplemented

class Atomic(abc.ABCMeta("NewMeta", (_AtomicBase,), {})):
    pass

try:
    unicode = unicode
except NameError:  # 'unicode' is undefined, assume Python >= 3
    Atomic.register(str)  # str includes unicode in Py3, make both Atomic
    Atomic.register(bytes)  # bytes will also be considered Atomic (optional)
else:
    # basestring is the abstract superclass of both str and unicode types
    Atomic.register(basestring)  # make both types of strings Atomic

operator2.6 より前のバージョンでは、モジュールに型チェッカーがあります。

>>> import operator
>>> operator.isSequenceType([])
True
>>> operator.isSequenceType(0)
False
于 2008-11-20T17:52:46.520 に答える
5

上記のすべての方法の問題は、strがシーケンスと見なされる(反復可能、getitemがあるなど)が、通常は単一のアイテムとして扱われることです。

たとえば、関数は、ファイル名またはファイル名のリストのいずれかである引数を受け入れる場合があります。関数が後者から最初のものを検出するための最もPython的な方法は何ですか?

改訂された質問に基づくと、あなたが望むものはもっと次のようなもののように聞こえます:

def to_sequence(arg):
    ''' 
    determine whether an arg should be treated as a "unit" or a "sequence"
    if it's a unit, return a 1-tuple with the arg
    '''
    def _multiple(x):  
        return hasattr(x,"__iter__")
    if _multiple(arg):  
        return arg
    else:
        return (arg,)

>>> to_sequence("a string")
('a string',)
>>> to_sequence( (1,2,3) )
(1, 2, 3)
>>> to_sequence( xrange(5) )
xrange(5)

これはすべてのタイプを処理することが保証されているわけではありませんが、あなたが言及したケースを非常にうまく処理し、ほとんどの組み込みタイプに対して正しいことを行うはずです。

それを使用するときは、これの出力を受け取るものがすべて反復可能を処理できることを確認してください。

于 2009-01-08T19:35:43.250 に答える
4

私見、Pythonの方法は、リストを*listとして渡すことです。のように:

myfunc(item)
myfunc(*items)
于 2008-12-01T18:59:11.283 に答える
4

シーケンスはここで説明されています: https://docs.python.org/2/library/stdtypes.html#sequence-types-str-unicode-list-tuple-bytearray-buffer-xrange

したがって、シーケンスは反復可能なオブジェクトと同じではありません。__getitem__反復可能なオブジェクトは実装する必要があるのに対し、シーケンスは実装する必要があると思います __iter__。たとえば、文字列はシーケンスであり、実装されていません__iter__。xrange オブジェクトはシーケンスであり、実装されていません__getslice__

しかし、あなたがやりたいと思ったことから、シーケンスではなく反復可能なオブジェクトが必要かどうかはわかりません。したがってhasattr("__getitem__", X)、シーケンスが必要な場合は行っhasattr("__iter__", X)てください。たとえば、文字列が必要ない場合は、代わりに行ってください。

于 2008-11-20T15:12:49.137 に答える
3

最も簡単な方法は、イテレータに変換できるかどうかを確認することです。すなわち

try:
    it = iter(X)
    # Iterable
except TypeError:
    # Not iterable

ただし、再起動可能またはランダム アクセス シーケンス (つまり、ジェネレーターなどではない) であることを確認する必要がある場合、このアプローチでは十分ではありません。

他の人が指摘したように、文字列も反復可能であるため、それらを除外する必要がある場合 (list(iter('a')) が再び ['a'] を返すため、アイテムを再帰する場合は特に重要です。具体的に除外する必要がある場合があります)それらと:

 if not isinstance(X, basestring)
于 2008-11-20T15:39:32.700 に答える
3

このような場合、私は常にシーケンス型を取得するか、常にスカラーを取得することを好みます。このセットアップで適切に動作しないのは文字列だけではありません。むしろ、集合体を使用し、その部分の反復を許可する型は、誤動作する可能性があります。

于 2008-11-20T14:52:34.247 に答える
2

私はここで初めてなので、正しい方法がわかりません。私の答えに答えたい:

上記のすべての方法の問題は、それstrがシーケンスと見なされる ( iterable 、 has__getitem__など) にもかかわらず、通常は単一のアイテムとして扱われることです。

たとえば、関数は、ファイル名またはファイル名のリストのいずれかの引数を受け入れる場合があります。関数が後者から最初のものを検出するための最も Pythonic な方法は何ですか?

これを新しい質問として投稿する必要がありますか? 元のものを編集しますか?

于 2008-11-23T10:32:51.570 に答える
1

文字列が問題の場合は、シーケンスを検出し、文字列の特殊なケースを除外します。

def is_iterable(x):
  if type(x) == str:
    return False
  try:
    iter(x)
    return True
  except TypeError:
    return False
于 2008-12-04T14:43:24.013 に答える
1

私がすることは、オブジェクトがシーケンスであることを示す特定のメソッドを持っているかどうかを確認することだと思います。シーケンスを構成するものの正式な定義があるかどうかはわかりません。私が考えることができる最善のことは、スライスをサポートする必要があるということです。したがって、次のように言えます。

is_sequence = '__getslice__' in dir(X)

使用する特定の機能を確認することもできます。

pi がコメントで指摘したように、1 つの問題は文字列がシーケンスであることですが、おそらくそれを 1 つとして扱いたくないでしょう。型が str ではないという明示的なテストを追加できます。

于 2008-11-20T14:05:04.280 に答える
0

あなたは間違った質問をしています。Python で型を検出しようとはしません。あなたは行動を検出します。

  1. 単一の値を処理する別の関数を作成します。(_use_single_val と呼びましょう)。
  2. シーケンス パラメータを処理する関数を 1 つ記述します。(_use_sequence と呼びましょう)。
  3. 上記の 2 つを呼び出す 3 番目の親関数を記述します。(これを use_seq_or_val と呼びます)。各呼び出しを例外ハンドラーで囲み、無効なパラメーター (つまり、単一の値またはシーケンスではない) をキャッチします。
  4. 単体テストを作成して、正しいパラメーターと正しくないパラメーターを親関数に渡して、例外が適切にキャッチされるようにします。

    def _use_single_val(v):
        print v + 1  # this will fail if v is not a value type

    def _use_sequence(s):
        print s[0]   # this will fail if s is not indexable

    def use_seq_or_val(item):    
        try:
            _use_single_val(item)
        except TypeError:
            pass

        try:
            _use_sequence(item)
        except TypeError:
            pass

        raise TypeError, "item not a single value or sequence"

編集:質問で尋ねられた「シーケンスまたは単一の値」を処理するように修正されました。

于 2008-11-20T16:46:48.213 に答える
0

修正された回答:

あなたの「シーケンス」の考え方が、Python のマニュアルで「シーケンス タイプ」と呼ばれるものと一致するかどうかはわかりませんが、一致する場合は、__Contains__ メソッドを探す必要があります。これは、Python が「if something in object:」チェックを実装するために使用する方法です。

if hasattr(X, '__contains__'):
    print "X is a sequence"

私の元の答え:

受け取ったオブジェクトがイテレータ インターフェイスを実装しているかどうかを確認します。

if hasattr(X, '__iter__'):
    print "X is a sequence"

私にとっては、次のようなことができるため、シーケンスの定義に最も近いものです。

for each in X:
    print each
于 2008-11-20T14:18:29.853 に答える
-1

組み込みの len() 関数にパラメータを渡して、これがエラーを引き起こすかどうかを確認できます。他の人が言ったように、文字列型には特別な処理が必要です。

ドキュメントによると、len 関数はシーケンス (文字列、リスト、タプル) または辞書を受け入れることができます。

次のコードを使用して、オブジェクトが文字列であることを確認できます。

x.__class__ == "".__class__
于 2008-11-20T14:08:25.053 に答える