-1

I have a list, and I would like to boolean test whether the list contains any of: lists or tuples or non-string sequences or list-like objects.

I would like the test return False if the list is of strs or any other unspecified iterable.

How can I evaluate this efficiently (lazily) in python?

Should I, for example use isinstance(x, collections.Sequence) together with a list comprehension condition?

Or could I collapse the elements of the list and test for residuals?

Here's how it goes:

{a, b, c, d, e, f} are {strs, ints, UUIDs, ...}

x1 = [a, b, c, d, e, f]
x2 = [(a, b),(c, d),(e, f)]
x2 = [[a, b],[c, d],[e, f]]

result = dict(x1=False, x2=True, x3=True)

def func(y):
    ...
    return z

for x in [x1, x2, x3]:
    assert func(x) == result[x], "Function needs an answer!"

What is a good way to populate func?

PS: Here is an example of a relevant list-like object (type OmegaConf.ListConfig):

https://github.com/omry/omegaconf/blob/a68b149b8e1a9e9a0cabc83e8691df8c6620909a/omegaconf/listconfig.py#L42C1-L43C1

jtlz2
  • 7,700
  • 9
  • 64
  • 114
  • 2
    Well, does `isinstance(x, collections.Sequence)` correctly match your desired values and _not_ match your undesired values…? – deceze Jun 27 '23 at 09:08
  • 2
    You don't need a list comprehension. Use the `any()` or `all()` function with a generator, depending on what you want to test. They're both lazy, they stop as soon as they've determined the answer. – Barmar Jun 27 '23 at 09:09
  • 1
    WHy don't you give some detail about your actual issue and what types exactly you want matched and which you don't. Like some sample input and output. – user2390182 Jun 27 '23 at 09:10
  • Thanks for all helpful feedback - I will address these shortly – jtlz2 Jun 27 '23 at 09:21
  • While you are at it, what constitutes a list-like object to you? – user2390182 Jun 27 '23 at 09:22
  • @deceze cc Barmar cc user2390182 Added significant detail as requested with many thanks in advance for any and all assistance! – jtlz2 Jun 27 '23 at 10:27

2 Answers2

0

So you want to exclude str (and likely bytes) from the sequence-like objects:

from typing import Sequence

# type test single item
def is_seq_but_not_str(item):
    return isinstance(item, Sequence) and not isinstance(item, (str, bytes))

# test list if any item passes test
def func(y):
    return any(map(is_seq_but_not_str, y))

The sequence protocol itself is not a 100% well defined, but most sources cite the presence of some of __len__, __getitem__, __reversed__, __contains__, __iter__ (by my subjective order of relevance; notice that the latter three can be implemented by means of the first two). Testing e.g. only for the first two will include dict which is explicitly not a sequence. If tested very strictly, __getitem__ should raise an IndexError for any item (not 0 <= item < len(seq)) and a TypeError for non-int items. In contrast, mappings (like dict) just raise a KeyError for any non-member item.

So, for custom classes that don't inherit from Sequence (or register with it), such a test could look like:

def custom_is_seq_but_not_str(item, 
                              required=("__len__", "__getitem__", "__reversed__")):
    if isinstance(item, (str, bytes)):
        return False
    return all(hasattr(item, attr) for attr in required)
    # or a little stronger
    # return all(callable(getattr(item, attr, None)) for attr in required)
user2390182
  • 72,016
  • 6
  • 67
  • 89
-1
>>> a = ['a', 1, [2,4]]
>>> any(map(lambda x: isinstance(x, str), a))
True
>>> any(map(lambda x: isinstance(x, int), a))
True
>>> any(map(lambda x: isinstance(x, list), a))
True
>>> any(map(lambda x: isinstance(x, float), a))
False
hacker315
  • 1,996
  • 2
  • 13
  • 23