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

我想写一个函数,接受一个参数,可以是序列或单个值。 值的types是str,int等,但我希望它被限制在硬编码列表中。 换句话说,我想知道参数X是一个序列还是我必须转换为一个序列,以避免后来的特殊情况。 我可以

type(X) in (list, tuple)

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

-N。

编辑 :请参阅下面的“答案”,为什么大多数这些答案不帮助我。 也许你有更好的build议。

上述所有方法的问题是,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) 

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

当使用它时,确保接收到的输出可以处理迭代。

从2.6开始,使用抽象基类 。

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

此外,可以对ABC进行自定义以解释exception情况,例如不将string视为序列。 这里是一个例子:

 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) 

注册后, Atomic类可以与isinstanceissubclass一起使用:

 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的代码,但是这样做需要使用稍微复杂的技术来dynamic地创build所需的抽象基类,从而避免由于元类语法差异而导致的语法错误。 这与Benjamin Peterson的六个模块的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模块中有types检查器。

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

恕我直言,python的方式是通过列表作为*列表。 如:

 myfunc(item) myfunc(*items) 

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

所以序列和可迭代对象不一样。 我认为序列必须实现__getitem__ ,而可迭代的对象必须实现__iter__ 。 因此,例如string是序列,不实现__iter__ ,xrange对象是序列,并不实现__getslice__

但是从你看到想要做的,我不确定你想要的序列,而是可迭代的对象。 所以去hasattr("__getitem__", X)你想要的序列,但宁可hasattr("__iter__", X)如果你不想string例如。

在这样的情况下,我宁愿总是采取序列types或总是采取标量。 string不会是唯一在这种设置中performance不佳的types; 相反,任何具有总体使用并允许对其各部分进行迭代的types可能是不合适的。

最简单的方法是检查你是否可以把它变成一个迭代器。 即

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

如果你需要确保它是一个可重新启动或随机访问序列(即不是一个生成器等),这种方法将是不够的。

正如其他人所指出的那样,string也是可迭代的,所以如果你需要这样排除它们(特别重要的是如果recursion遍历项目,那么list(iter('a'))又给出'''',那么你可能需要特别排除他们与:

  if not isinstance(X, basestring) 

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

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

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

我应该把这个作为一个新的问题吗? 编辑原始的?

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

 is_sequence = '__getslice__' in dir(X) 

您也可以检查您要使用的特定function。

正如pi在评论中指出的那样,一个问题是一个string是一个序列,但是你可能不希望把它当作一个。 你可以添加一个明确的testing,types不是str。

如果string是问题,则检测一个序列并过滤掉string的特殊情况:

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

修改答案:

我不知道你的“序列”的想法是否与Python手册所说的“ 序列types ”匹配,但如果是这样,你应该寻找__Contains__方法。 这是Python用来实现检查“if something in 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 

你问的是错误的问题。 你不要试图在Python中检测types; 你检测行为。

  1. 编写处理单个值的另一个函数。 (我们称之为_use_single_val)。
  2. 编写一个处理序列参数的函数。 (我们称之为_use_sequence)。
  3. 写第三个父函数,调用上面的两个。 (称之为use_seq_or_val)。 用exception处理程序围绕每个调用来捕获无效参数(即不是单个值或序列)。
  4. 编写unit testing以将正确和不正确的parameter passing给父函数,以确保正确捕获exception。
 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" 

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

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

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

您可以使用以下代码检查对象是否是string:

 x.__class__ == "".__class__