简体   繁体   中英

Elegant way to check if a list contains int, str, tuple and sub-list

I wrote some script, but I have to add some condition; the user puts a list and it must contain at least one of these types (tuple, int,s tr and sub-list).

Is there an elegant way to do that without 4 if?

I thought about for loop and using isinstance(i, type) but its tricky, because it runs over and over and ask the different 'questions' about one kind of type

for i in List:
  if isinstance(i,int):
    if isinstance(i,str)

As you see this is not very elegant. I thought about putting new variable i,j,k for each kind of type and maybe do 4 for loops:

for
  for
   for
    for

Any ideas and clues are welcomed. Thanks!

You could add a helper function "has":

def has(items, t):
    for item in items:
        if isinstance(item, t):
            return True
    return False

def check(items):
    return has(items, int)   and has(items, str) \
       and has(items, tuple) and has(items, list)

If you want to be more compact (but less readable), use "any" and "all":

def has(item, t):
     return any([isinstance(items, t) for item in items])

def check(items):
     return all([has(items, t) for t in (str, int, tuple, list)])

You may use builtin any function. Quoting docs:

any(iterable)

Return True if any element of the iterable is true. If the iterable is empty, return False .

Sample usage:

expected_types = {int, str, tuple, list}  # set of expected types
for i in seq:
    if any(isinstance(i, t) for t in expected_types):
        pass  # do something

To check all elements of sequence you may use built-in all function. So, to check all elements of sequence you may simply use:

expected_types = {int, str, tuple, list}
if all(any(isinstance(i, t) for t in expected_types) for i in seq):
    pass  # condition is sufficient

Both all() and any() performs short-circuiting, so operation has reasonable performance. If you don't care about subclasses you may check type - it speeds up isinstance(i, t) for t in expected_types from O(n) to O(1) .

expected_types = {int, str, tuple, list}
if all(type(i) in expected_types for i in seq):
    pass  # condition is sufficient, but not for subclasses of int, str, tuple or list

All checks above tests if all objects are instances of one of expected_types , but not if all types occurs in sequence. If you want to ensure "at least one of each type" condition you may use collections.Counter .

c = collections.Counter(type(o) for o in seq)
if all(c[t] >= 1 for t in {int, list, tuple, str}):
    pass  # seq contains at least one of each type

You can use a tuple of different types with isinstance :

>>> isinstance('a', (str, list, tuple, int))
True

Combine with any

>>> data = [1, 'a', (2, 4, 6)]
>>> any(isinstance(x, (str, list, tuple, int)) for x in data)
True

or, if you want to do something with the objects of one these types:

for x in data:
    if isinstance(x, (str, list, tuple, int)):
        print('found')

To get the type of an object, you can use the built-in type() function. Passing an object as the only parameter will return the type object of that object:

 eg:
    a=5
    type(a) will return 'int'

If you want to check for all instances if pass then use all otherwise use any as below.

eg

types = set(['int','str','dict'])#Used set to ensure no duplicate
l = ['s',3,{'s':3}]
for j in types:
    if any(isinstance(i,eval(j)) for i in l):
        print 'passed'

Output-

passed
passed
passed

使用内置的allany

all([any(lambda x: isInstance(x,i), lst_to_check) for i in instances_list])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM