Начиная с Python 2.6, с введением абстрактных базовых классов, isinstance
(используется на ABC, а не на конкретных классах) теперь считается вполне приемлемым. В частности:
from abc import ABCMeta, abstractmethod
class NonStringIterable:
__metaclass__ = ABCMeta
@abstractmethod
def __iter__(self):
while False:
yield None
@classmethod
def __subclasshook__(cls, C):
if cls is NonStringIterable:
if any("__iter__" in B.__dict__ for B in C.__mro__):
return True
return NotImplemented
Это точная копия (изменяющая только имя класса) Iterable
, как определено в _abcoll.py
(подробности реализации collections.py
) ... причина, по которой это работает, как вы хотите, в то время как collections.Iterable
не дело в том, что последний делает все возможное, чтобы строки считались итеративными, вызывая Iterable.register(str)
явно после этой инструкции class
.
Конечно, легко увеличить __subclasshook__
, возвращая False
до вызова any
для других классов, которые вы хотите специально исключить из своего определения.
В любом случае, после того, как вы импортируете этот новый модуль как myiter
, isinstance('ciao', myiter.NonStringIterable)
будет False
, а isinstance([1,2,3], myiter.NonStringIterable)
будет True
, как вы и просите - и в Python 2.6 и более поздних версиях это считается правильным способом воплощения таких проверок ... определите абстрактный базовый класс и проверьте isinstance
на нем.