我想编写一个接受参数的函数,该参数可以是序列也可以是单个值.值的类型是str,int等,但我不希望它被限制为硬编码列表.换句话说,我想知道参数X是否是一个序列或我必须转换为序列以避免以后的特殊套管.我可以
type(X) in (list, tuple)
但是可能还有其他我不知道的序列类型,也没有共同的基类.
-N.
编辑:请参阅下面的"答案",了解为何大多数答案对我没有帮助.也许你有更好的建议.
从2.6开始,使用抽象基类.
>>> 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)
注册后,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的代码,但这样做需要使用稍微复杂的技术来动态创建所需的抽象基类,从而避免由于元类语法差异导致的语法错误.这与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
模块中有类型检查器.
>>> import operator
>>> operator.isSequenceType([])
True
>>> operator.isSequenceType(0)
False
所有上述方法的问题在于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)
这并不能保证处理所有类型,但它会处理你提到的案例,并且应该为大多数内置类型做正确的事情.
使用它时,请确保接收到的输出可以处理迭代.