检测序列参数的正确方法?

时间:2008-11-20 13:52:44

标签: python types sequences

我想编写一个接受参数的函数,该参数可以是序列也可以是单个值。值的类型是str,int等,但我希望将其限制为硬编码列表。 换句话说,我想知道参数X是否是一个序列或我必须转换为序列以避免以后的特殊套管。我能做到

type(X) in (list, tuple)

但可能还有其他我不知道的序列类型,也没有共同的基类。

N。

修改:请参阅下面的“答案”,了解为何大多数答案对我没有帮助。也许你有更好的建议。

12 个答案:

答案 0 :(得分:19)

从2.6开始,使用abstract base classes

>>> import collections
>>> isinstance([], collections.Sequence)
True
>>> isinstance(0, collections.Sequence)
False

此外,ABC可以自定义以解释异常,例如不将字符串视为序列。这是一个例子:

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)

注册后,原子类可用于 isinstance issubclass

assert isinstance("hello", Atomic) == True

这仍然比硬编码列表好得多,因为您只需要在规则中注册例外,并且代码的外部用户可以注册自己的。

请注意,在 Python 3 中,指定元类的语法已更改,并且basestring抽象超类已被删除,这需要使用以下内容:

class Atomic(metaclass=abc.ABCMeta):
    @classmethod
    def __subclasshook__(cls, other):
        return not issubclass(other, collections.Sequence) or NotImplemented

Atomic.register(str)

如果需要,可以编写兼容Python 2.6+ 3.x的代码,但这样做需要使用稍微复杂的技术来动态创建所需的抽象基类,从而避免由于元类语法差异导致的语法错误。这与Benjamin Peterson的six模块with_metaclass()函数的作用基本相同。

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

在2.6之前的版本中,operator模块中有类型检查器。

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

答案 1 :(得分:5)

  

以上所有问题   提到的方法就是str   考虑一个序列(它是可迭代的,   有 getitem 等等)但它是   通常被视为单个项目。

     

例如,函数可以接受   可以是文件名的参数   或文件名列表。什么是   大多数Pythonic方式的功能   检测后者的第一个?

根据修改后的问题,听起来你想要的更像是:

def to_sequence(arg):
    ''' 
    determine whether an arg should be treated as a "unit" or a "sequence"
    if it's a unit, return a 1-tuple with the arg
    '''
    def _multiple(x):  
        return hasattr(x,"__iter__")
    if _multiple(arg):  
        return arg
    else:
        return (arg,)

>>> to_sequence("a string")
('a string',)
>>> to_sequence( (1,2,3) )
(1, 2, 3)
>>> to_sequence( xrange(5) )
xrange(5)

这不能保证处理所有类型,但它会处理你提到的案例,并且应该为大多数内置类型做正确的事情。

使用它时,请确保收到此输出的内容可以处理迭代。

答案 2 :(得分:4)

序列描述如下: https://docs.python.org/2/library/stdtypes.html#sequence-types-str-unicode-list-tuple-bytearray-buffer-xrange

因此序列与可迭代对象不同。我认为序列必须实现 __getitem__,而可迭代对象必须实现__iter__。 因此,例如字符串是序列而不实现__iter__,xrange对象是序列,不实现__getslice__

但是从你看到的想要做的事情来看,我不确定你是否想要序列,而是可迭代的对象。 那么请选择hasattr("__getitem__", X)您想要的序列,但如果您不想要字符串,请选择hasattr("__iter__", X)

答案 3 :(得分:4)

恕我直言,python方式是将列表作为*列表传递。如:

myfunc(item)
myfunc(*items)

答案 4 :(得分:3)

在这种情况下,我更喜欢总是采用序列类型或总是采用标量。字符串不会是唯一在此设置中表现不佳的类型;相反,任何具有聚合使用并允许对其部分进行迭代的类型可能会出错。

答案 5 :(得分:3)

最简单的方法是检查是否可以将其转换为迭代器。即

try:
    it = iter(X)
    # Iterable
except TypeError:
    # Not iterable

如果你需要确保它是可重启的或随机的访问序列(即不是发生器等),那么这种方法是不够的。

正如其他人所说,字符串也是可迭代的,所以如果你需要这样排除它们(特别重要的是,如果通过项目递归,列表(iter('a'))再次给出['a'],那么你可能需要用以下内容明确排除它们:

 if not isinstance(X, basestring)

答案 6 :(得分:2)

我是新来的,所以我不知道做正确的方法是什么。我想回答我的答案:

上述所有方法的问题在于 str 被视为一个序列(它是可迭代的,有__getitem__等等)但它通常被视为一个序列单项。

例如,函数可以接受可以是文件名或文件名列表的参数。函数从后者中检测第一个函数的最Pythonic方法是什么?

我应该将此作为新问题发布吗?编辑原始文件?

答案 7 :(得分:1)

我认为我要做的是检查对象是否有某些方法表明它是一个序列。我不确定是否有关于序列的正式定义。我能想到的最好的是,它必须支持切片。所以你可以说:

is_sequence = '__getslice__' in dir(X)

您可能还会检查您将要使用的特定功能。

正如pi在评论中指出的那样,一个问题是字符串是一个序列,但您可能不希望将其视为一个序列。您可以添加类型不是str的显式测试。

答案 8 :(得分:1)

修订回答:

我不知道你对“序列”的想法是否符合Python手册所称的“Sequence Type”,但如果它符合,你应该寻找__Contains__方法。这是Python用来实现检查“如果在object中的东西:”

的方法
if hasattr(X, '__contains__'):
    print "X is a sequence"

我原来的回答:

我会检查你收到的对象是否实现了迭代器接口:

if hasattr(X, '__iter__'):
    print "X is a sequence"

对我而言,这是与你的序列定义最接近的匹配,因为这样可以让你做类似的事情:

for each in X:
    print each

答案 9 :(得分:1)

如果字符串是问题,请检测序列并过滤掉字符串的特殊情况:

def is_iterable(x):
  if type(x) == str:
    return False
  try:
    iter(x)
    return True
  except TypeError:
    return False

答案 10 :(得分:0)

你问的是错误的问题。您不会尝试在Python中检测类型;你发现了行为。

  1. 编写另一个处理单个值的函数。 (我们称之为_use_single_val)。
  2. 编写一个处理序列参数的函数。 (我们称之为_use_sequence)。
  3. 编写第三个调用上述两个函数的父函数。 (称之为use_seq_or_val)。使用异常处理程序围绕每个调用以捕获无效参数(即,不是单个值或序列)。
  4. 编写单元测试以通过正确的&父函数的参数不正确,以确保它正确捕获异常。
  5. 
        def _use_single_val(v):
            print v + 1  # this will fail if v is not a value type
    
        def _use_sequence(s):
            print s[0]   # this will fail if s is not indexable
    
        def use_seq_or_val(item):    
            try:
                _use_single_val(item)
            except TypeError:
                pass
    
            try:
                _use_sequence(item)
            except TypeError:
                pass
    
            raise TypeError, "item not a single value or sequence"
    

    编辑:修改以处理问题中询问的“序列或单个值”。

答案 11 :(得分:-1)

您可以在内置len()函数中传递参数,并检查是否会导致错误。正如其他人所说,字符串类型需要特殊处理。

根据文档,len函数可以接受序列(字符串,列表,元组)或字典。

您可以使用以下代码检查对象是否为字符串:

x.__class__ == "".__class__