我想编写一个接受参数的函数,该参数可以是序列也可以是单个值。值的类型是str,int等,但我不希望将其限制为硬编码列表。 换句话说,我想知道参数X是否是一个序列或我必须转换为序列以避免以后的特殊套管。我能做到
type(X) in (list, tuple)
但可能还有其他我不知道的序列类型,也没有共同的基类。
N。
修改:请参阅下面的“答案”,了解为何大多数答案对我没有帮助。也许你有更好的建议。
答案 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)
因此序列与可迭代对象不同。我认为序列必须实现
__getitem__
,而可迭代对象必须实现__iter__
。
因此,例如字符串是序列而不实现__iter__
,xrange对象是序列,不实现__getslice__
。
但是从你看到的想要做的事情来看,我不确定你是否想要序列,而是可迭代的对象。
那么请选择hasattr("__getitem__", X)
您想要的序列,但如果您不想要字符串,请选择hasattr("__iter__", X)
。
答案 3 :(得分:4)
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中检测类型;你发现了行为。
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__