7

listtuplesetなどの反復可能なパラメーターのみを受け入れるメソッドを作成しようとしていますdict

これが私のコードです:

class MjmMenuControl(MjmBaseMenu):
    def __init__(self, items=None):
        iterables = (dict, list, set, tuple)
        for iterable in iterables:
            if isinstance(items, iterable):
                ...
                break

しかし、もっと簡単な方法はないかと思っていましif isinstance(items, <iterable_base_class>):たが、何も見つかりませんでした。

私はすでになどのベースクラスを見つけようとしましlistたが、それらはすべてから派生しているようですobject:

>>> inspect.getmro(list)
(<class 'list'>, <class 'object'>)
>>> inspect.getmro(tuple)
(<class 'tuple'>, <class 'object'>)

これは可能ですか、それともひどいforループに固執する必要がありますか?

4

2 に答える 2

18

はい、使用しますcollections.abc.Iterable:

>>> from collections import abc
>>> isinstance(set(), abc.Iterable)
True
>>> isinstance((), abc.Iterable)
True
>>> isinstance([], abc.Iterable)
True
>>> isinstance('', abc.Iterable)
True
>>> isinstance({}, abc.Iterable)
True
>>> isinstance(0, abc.Iterable)
False

ただし、イテラブルが渡されると仮定してメソッドを作成するだけです。正しい型が渡されることを確認するのは呼び出し元の責任です。

このcollections.abcモジュールは Python 3.3 で新しく追加されました。Python 3.3 以降でも名前は古い場所で利用できますが、その内容は以前はcollectionsモジュールに存在していました。

于 2013-04-13T12:19:46.810 に答える
5

このオプションの弱点の 1 つは、collections.abc.Iterableこのようなカスタムの iterable クラスに対応できないことです...

from collections.abc import Iterable

class MyIterable(object):
    def __getitem__(self, index):
        if index >= 10:
            raise IndexError
        return index

>>> myiter = MyIterable()
>>> isinstance(myiter, Iterable)
False
>>> [i for i in myiter]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

これを行う 'Pythonic' の方法は、ダック タイピングを使用すること、つまり、オブジェクトをイテラブルとして扱い、失敗した場合は例外を処理することであるとよく主張されます。例えば...

class MjmMenuControl(MjmBaseMenu):
    def __init__(self, items=None):
        try:
            for item in items:
                do_something_with(item)
        except TypeError:
            # If we get here, 'items' is not iterable
            do_something_else()

...しかし、どちらの方法にも、文字列をリストなどと同じものとして扱うという弱点があり、これは通常、必要なものではありません。通常、 iterable containerをテストする必要がありますが、これは少し異なります。そのため、このような特殊なケースとして文字列を扱うコードがよく見られます...

class MjmMenuControl(MjmBaseMenu):
    def __init__(self, items=None):
        if isinstance(items, (str, bytes)):
            do_something_else()
        else:
            try:
                for item in items:
                    do_something_with(item)
            except TypeError:
                # If we get here, 'items' is not iterable
                do_something_else()

...少し面倒ですが、仕事は完了です。

于 2013-04-13T13:11:33.030 に答える