As of 2.6, use abstract base classes.
>>> import collections
>>> isinstance([], collections.Sequence)
True
>>> isinstance(0, collections.Sequence)
False
Furthermore ABC's can be customized to account for exceptions, such as not considering strings to be sequences. Here an example:
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)
After registration the Atomic class can be used with isinstance and issubclass:
assert isinstance("hello", Atomic) == True
This is still much better than a hard-coded list, because you only need to register the exceptions to the rule, and external users of the code can register their own.
Note that in Python 3 the syntax for specifying metaclasses changed and the basestring
abstract superclass was removed, which requires something like the following to be used instead:
class Atomic(metaclass=abc.ABCMeta):
@classmethod
def __subclasshook__(cls, other):
return not issubclass(other, collections.Sequence) or NotImplemented
Atomic.register(str)
If desired, it's possible to write code which is compatible both both Python 2.6+ and 3.x, but doing so requires using a slightly more complicated technique which dynamically creates the needed abstract base class, thereby avoiding syntax errors due to the metaclass syntax difference. This is essentially the same as what Benjamin Peterson's six module'swith_metaclass()
function does.
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
In versions before 2.6, there are type checkers in theoperator
module.
>>> import operator
>>> operator.isSequenceType([])
True
>>> operator.isSequenceType(0)
False