如何检查列表的元素是否属于同一类型,而不需要单独检查每个元素(如果可能)?
例如,我想要一个函数来检查此列表中的每个元素是否都是整数(这显然是错误的):
x = [1, 2.5, 'a']
def checkIntegers(x):
# return True if all elements are integers, False otherwise
all
与 isinstance
结合使用:
all(isinstance(x, int) for x in lst)
如果需要,您甚至可以使用
isinstance
检查多种类型:
all(isinstance(x, (int, long)) for x in lst)
请注意,这也会获取继承的类。例如:
class MyInt(int):
pass
print(isinstance(MyInt('3'),int)) #True
如果您需要将自己限制为整数,则可以使用
all(type(x) is int for x in lst)
。但这是一种非常罕见的情况。
您可以用它编写一个有趣的函数,如果所有其他元素都是相同的类型,它将返回序列中第一个元素的类型:
def homogeneous_type(seq):
iseq = iter(seq)
first_type = type(next(iseq))
return first_type if all( (type(x) is first_type) for x in iseq ) else False
这适用于任何任意可迭代对象,但它会在此过程中消耗“迭代器”。
另一个有趣的函数同样返回一组公共基数:
import inspect
def common_bases(seq):
iseq = iter(seq)
bases = set(inspect.getmro(type(next(iseq))))
for item in iseq:
bases = bases.intersection(inspect.getmro(type(item)))
if not bases:
break
return bases
any()
,无需遍历整个列表。只要找到不是 int
或 long
的对象就立即中断:
>>> not any(not isinstance(y,(int,long)) for y in [1,2,3])
True
>>> not any(not isinstance(y,(int,long)) for y in [1,'a',2,3])
False
结合已经给出的一些答案,使用 map()、type() 和 set() 的组合提供了一个恕我直言相当可读的答案。假设不检查类型多态性的限制是可以的。也不是计算效率最高的答案,但它可以轻松检查所有元素是否属于同一类型。
# To check whether all elements in a list are integers
set(map(type, [1,2,3])) == {int}
# To check whether all elements are of the same type
len(set(map(type, [1,2,3]))) == 1
检查列表是否由同质元素组成的最简单方法可以使用 itertools 模块的 groupby 函数:
from itertools import groupby
len(list(groupby(yourlist,lambda i:type(i)))) == 1
如果 len 与 len 不同,则意味着它在列表中找到了不同类型的类型。 这存在贯穿整个序列的问题。 如果您想要一个惰性版本,您可以为此编写一个函数:
def same(iterable):
iterable = iter(iterable)
try:
first = type(next(iterable))
return all(isinstance(i,first) for i in iterable)
except StopIteration:
return True
此函数存储第一个元素的类型,并在列表中的一个元素中发现不同类型时立即停止。
这两种方法都对类型非常敏感,因此它会看到不同的 int 和 float,但这应该尽可能接近您的请求
编辑:
按照 mgilson 的建议,用对 all 的调用替换了 for 循环
在 void 迭代器的情况下,它返回 True 以与 bulitin all 函数的行为一致
>>> def checkInt(l):
return all(isinstance(i, (int, long)) for i in l)
>>> checkInt([1,2,3])
True
>>> checkInt(['a',1,2,3])
False
>>> checkInt([1,2,3,238762384762364892364])
True
我喜欢 EnricoGiampieri 的函数(上面),但是有一个更简单的版本,使用
all_equal
中的 “Itertools 食谱” 文档的 itertools
部分:
from itertools import groupby
def all_equal(iterable):
"Returns True if all the elements are equal to each other"
g = groupby(iterable)
return next(g, True) and not next(g, False)
所有这些食谱都包装在
more_itertools
:
基本上所有这些方法以及许多其他方法都可以从 Python 包索引上的 more-itertools 项目中安装:
pip install more-itertools
扩展工具提供与底层工具集相同的高性能。卓越的内存性能是通过一次处理一个元素而不是一次性将整个可迭代对象放入内存来保持的。通过以函数式方式将工具链接在一起,可以保持较小的代码量,这有助于消除临时变量。通过使用“向量化”构建块而不是使用会产生解释器开销的 for 循环和生成器来保持高速。
from more_itertools import all_equal
all_equal(map(type, iterable))
或使用
isinstance
和已知类型 int
(根据原始问题)
all_equal(map(lambda x: isinstance(x, int), iterable))
这两种方法比 Enrico 的建议更简洁,并且可以像 Enrico 的函数一样处理“void 迭代器”(例如
range(0)
)。
all_equal(map(type, range(0))) # True
all_equal(map(type, range(1))) # True
all_equal(map(type, range(2))) # True
all_equal(map(lambda x: isinstance(x, int), range(0))) # True
all_equal(map(lambda x: isinstance(x, int), range(1))) # True
all_equal(map(lambda x: isinstance(x, int), range(2))) # True
如果您想排除子类,也可以使用
type()
。请参阅isinstance()
和type()
之间的区别:
>>> not any(not type(y) is int for y in [1, 2, 3])
True
>>> not any(not type(y) == int for y in [1, 'a', 2.3])
False
虽然你可能不愿意,因为这样会更脆弱。如果 y 将其类型更改为 int 的子类,此代码将中断,而
isinstance()
仍然可以工作。
使用
is
是可以的,因为内存中只有一个<type 'int'>
,所以如果它们是相同的类型,它们应该返回相同的标识。
我更希望您使用此方法,您可以迭代列表中的每个项目并检查它们是否都是相同的数据类型,如果是,则返回 True,否则返回 False。
def checkIntegers(x):
# return True if all elements are integers, False otherwise
return all(isinstance(i, type(x[0])) for i in x[1:])
x = [1, 2.5, 'a']
checkIntegers(x)
False
这里有一个关于此的简洁函数,当前它检查(在
list
中)所有项目是否为整数或所有项目是否为字符串,或者是否为混合数据类型。
def check_item_dtype_in_list(item_range):
if all(map(lambda x: str(x).isdigit(), item_range)):
item_range = list(map(int, item_range))
print('all are integer')
print(item_range)
return
elif all(isinstance(item, str) for item in item_range):
print('all are string')
print(item_range)
return
elif any(map(lambda x: str(x), item_range)):
print('mixed dtype')
print(item_range)
return
check_item_dtype_in_list(['2', 2, 3])
check_item_dtype_in_list(["2", 2, 'Two'])
check_item_dtype_in_list(['Two', 'Two', 'Two'])
check_item_dtype_in_list([2, 2., 'Two'])
all are integer
[2, 2, 3]
mixed dtype
['2', 2, 'Two']
all are string
['Two', 'Two', 'Two']
mixed dtype
[2, 2.0, 'Two']
def c(x):
for i in x:
if isinstance(i,str):
return False
if isinstance(i,float):
return False
return True
对于这样的情况,我更喜欢使用地图:
from types import IntType
In [21]: map((lambda x: isinstance(x, IntType)), x)
Out[21]: [True, False, False]