正确的方法来检测序列参数?
我想写一个函数,接受一个参数,可以是序列或单个值。 值的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类可以与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的代码,但是这样做需要使用稍微复杂的技术来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; 你检测行为。
- 编写处理单个值的另一个函数。 (我们称之为_use_single_val)。
- 编写一个处理序列参数的函数。 (我们称之为_use_sequence)。
- 写第三个父函数,调用上面的两个。 (称之为use_seq_or_val)。 用exception处理程序围绕每个调用来捕获无效参数(即不是单个值或序列)。
- 编写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__