Проверка типов: итеративный тип, который не является строкой

Чтобы лучше объяснить, рассмотрим эту простую функцию проверки типов:

from collections import Iterable
def typecheck(obj):
    return not isinstance(obj, str) and isinstance(obj, Iterable)

Если obj является итерируемым типом, отличным от str, он возвращает True. Однако, если obj является str или неистребимым типом, он возвращает False.

Есть ли способ более эффективно выполнять проверку типа? Я имею в виду, кажется, что избыточно проверять тип obj один раз, чтобы увидеть, не является ли это str, а затем снова проверить его, чтобы увидеть, является ли он итерируемым.

Я думал о перечислении любого другого итерируемого типа, кроме str, как это:

return isinstance(obj, (list, tuple, dict,...))

Но проблема в том, что этот подход пропустит любые другие повторяющиеся типы, которые явно не указаны.

Итак... есть ли что-нибудь лучше или это подход, который я дал в функции наиболее эффективным?

Ответ 1

В python 2.x проверка атрибута __iter__ была полезной (хотя и не всегда мудрой), потому что iterables должен иметь этот атрибут, но строки этого не сделали.

def typecheck(obj): return hasattr(myObj, '__iter__')

Нижняя сторона заключалась в том, что __iter__ не был действительно путинским способом: некоторые объекты могут реализовать __getitem__, но не __iter__ например.

В Python 3.x строки получили атрибут __iter__, нарушив этот метод.

Метод, который вы указали, является наиболее эффективным, действительно Pythonic способом, который я знаю в Python 3.x:

def typecheck(obj): return not isinstance(obj, str) and isinstance(obj, Iterable)

Существует гораздо более быстрый (более эффективный) способ проверки __iter__ как в Python 2.x, а затем проверки str.

def typecheck(obj): return hasattr(obj, '__iter__') and not isinstance(obj, str)

Это имеет ту же оговорку, что и в Python 2.x, но намного быстрее.

Ответ 2

Я проверяю его с помощью этого кода, и он отлично работает с Python 2 и 3

from __future__ import unicode_literals
import types
import collections

var = ["a", "b", "c"]
if isinstance(var, collections.Iterable) and \
        not isinstance(var, types.StringTypes):
    return var