检查列表的所有元素是不是属于同一类型

Posted

技术标签:

【中文标题】检查列表的所有元素是不是属于同一类型【英文标题】:Check if all elements of a list are of the same type检查列表的所有元素是否属于同一类型 【发布时间】:2012-10-26 11:35:06 【问题描述】:

如何检查列表中的元素是否属于同一类型,而不是尽可能单独检查每个元素?

例如,我想要一个函数来检查这个列表的每个元素是否都是一个整数(这显然是错误的):

x = [1, 2.5, 'a']

def checkIntegers(x):
    # return True if all elements are integers, False otherwise

【问题讨论】:

如果不检查每个元素,你怎么可能做到这一点?没有办法知道你没有看过的元素。 @DanielRoseman -- 一旦发现坏的可以短路。 看来使用all是一种方式... @mgilson,是的......我认为 OP 正在请求一个根本不涉及迭代的解决方案。 【参考方案1】:

尝试将allisinstance 结合使用:

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)。但这是一种非常罕见的情况。


你可以用 this 编写一个有趣的函数,如果所有其他元素都是相同的类型,它将返回序列中第一个元素的类型:

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

【讨论】:

请注意,homogeneous_type()(我猜这就是该函数应该被调用的)对于空迭代器会出错。此外,结果取决于可迭代项中项目的顺序(我想这就是您所说的“迭代器”),这不是我对这个名称的函数所期望的。 (例如 [0, False][False, 0]。) @SvenMarnach -- 我想这是一个合理的观点。我认为在空迭代器上出错是可以的(我实际上在编写函数时考虑到了这一点)......在空迭代器的情况下结果应该是什么?至于传递[0, False][False, 0] 的列表,我可以通过执行all( type(x) is first_type for x in iseq ) 来解决这个问题,但在我看来,如果该函数检查为 OK 会更有用子类也是如此。 @SvenMarnach -- 最后,是的,我的意思是iterable。对于语义,您会使用什么术语来描述可以使用的迭代器(iter(...) 或生成器)与不能使用的迭代器(listtuple、...)? (感谢您批判性地看待这个问题。希望它能提供更好的答案:) all() 的另一个优点是它会在遇到来自生成器表达式的False 值时立即短路并停止。 根据问题的代码示例,x 不应该是列表对象吗?【参考方案2】:

使用any(),无需遍历整个列表。只要找到不是intlong 的对象就中断:

>>> 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

【讨论】:

all 的逻辑更加清晰,但我想not any(not condition ...) 也可以。我想 +1 为您的努力(以及演示 all 的对应项,这同样有用。) @mgilson 当我看到你的comment 时我就想到了这个:),否则我对基于all() 的解决方案很满意。 all 也短路了,所以这和@mgilson 的解决方案之间没有任何区别。这更像是一种逻辑练习...... 一般来说,我认为您需要做的是检查是否有任何元素与第一个元素的类型不同。即any(not isinstance(e,type(seq[0])) for e in seq[1:]) %timeit all(isinstance(x, basestring) for x in list_w_int_or_str) 实际上比 %timeit not any(not isinstance(x, basestring) for x in list_w_int_or_str) 花费的时间更少【参考方案3】:

结合已经给出的一些答案,使用 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

【讨论】:

【参考方案4】:
>>> 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

【讨论】:

isinstance() 应优先于 type() 两者哪个更快,isinstance()type() isinstance(i,(int,long)) 很短。这与速度无关,type() 对于 int 的子类会失败 @mgilson 是的,我刚刚也注意到你在答案上击败了我。 :)【参考方案5】:

检查列表是否由同质元素组成的最简单方法是使用 itertools 模块的 groupby 函数:

from itertools import groupby
len(list(groupby(yourlist,lambda i:type(i)))) == 1

如果 th len 与 one 不同,则意味着它在列表中找到了不同类型的类型。 这具有贯穿整个序列的问题。 如果你想要一个惰性版本,你可以为此编写一个函数:

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 的建议,将 for 循环替换为对 all 的调用

在 void 迭代器的情况下,它返回 True 以与 bulitin all 函数的行为一致

【讨论】:

OP 的第一个请求不是检查所有元素是否属于特定类型,而是检查所有元素是否属于同一类型,与第一个元素无关。这是一个稍微不同的问题的解决方案。 @EnricoGiampieri -- 但您仍然可以在 3 行中执行短路版本。 a=iter(obj);t=type(next(a));all(isinstance(x,t) for x in a)。对于问题所在的列表来说更容易——t = type(lst[0]); return all(instance(x,t) for x in lst[1:])。不太破旧。 是的,您可以将其短路,但它不会处理 void 迭代器并会引发异常。这只是一个选择问题,就我而言,我更喜欢处理它而不是提高 您可以简单地使用type,而不是lambda i: type(i)【参考方案6】:

我希望您使用这种方法,您可以遍历列表中的每个项目并检查它们是否都是相同的数据类型,如果是,则返回 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

【讨论】:

【参考方案7】:

如果您想排除子类,也可以使用type()。见the difference between isinstance() and 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'>,所以如果它们是相同的类型,它们应该返回相同的标识。

【讨论】:

【参考方案8】:

我喜欢 EnricoGiampieri (above) 的函数,但有一个更简单的版本,使用 @987654325 的 "Itertools recipes" 部分中的 all_equal @文档:

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)

所有这些食谱都是packaged in 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 iterators”(例如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

【讨论】:

【参考方案9】:

这是一个关于此的简洁函数,目前它检查(在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']

【讨论】:

【参考方案10】:
    def c(x):
         for i in x:
             if isinstance(i,str):
                   return False
             if isinstance(i,float):
                   return False

          return True

【讨论】:

【参考方案11】:

我更喜欢在这样的情况下使用地图:

from types import IntType
In [21]: map((lambda x: isinstance(x, IntType)), x)
   Out[21]: [True, False, False]

【讨论】:

以上是关于检查列表的所有元素是不是属于同一类型的主要内容,如果未能解决你的问题,请参考以下文章

如果列表元素属于某种类型,则遍历列表并打印“真”[重复]

检查元素是不是属于集合的算法

检查领域列表属性是不是包含另一个列表的所有元素

EF CORE PostgreSQL - 检查列表的所有元素是不是包含在另一个列表中

检查元组列表中的所有第一个元素是不是满足条件

如何检查列表列表中的所有元素是不是都是字符串