Correct way to detect sequence parameter?

后端 未结 12 1554
情书的邮戳
情书的邮戳 2020-11-28 13:48

I want to write a function that accepts a parameter which can be either a sequence or a single value. The type of value is str, int, etc., but I don\'t want

12条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2020-11-28 14:08

    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 theoperatormodule.

    >>> import operator
    >>> operator.isSequenceType([])
    True
    >>> operator.isSequenceType(0)
    False
    

提交回复
热议问题