0

I am creating a Python object from its string representation:

obj = ast.literal_eval(obj_text)

Now, I want to make sure that the object has an appropriate type. In this case, I want it to be a list of strings. This can be done, as explained here. Still, I thought that maybe newer (3.10) Python versions will have an easier way to do that, so I tried this instead:

type_ok = (type(obj) == list[str])

For some reason, this is always false. So I tried is instead:

type_ok = (type(obj) is list[str])

But this also always evaluates to False. Why is this the case? Isn't list[str] the type of, say, ["a", "b", "c"]? Anyway it seems, like I'll have to use a for loop, if this doesn't work.

janekb04
  • 4,304
  • 2
  • 20
  • 51
  • 4
    `list[str]` is a type hint, not a type. At runtime, there are just heterogeneous lists. – chepner Mar 12 '23 at 20:10
  • 4
    Note that `isinstance(["a", "b", "c"], list[str])` raises a `TypeError`, rather than returning `True`. – chepner Mar 12 '23 at 20:12
  • You need to do something like `isinstance(obj, list) and all(isinstance(i, str) for i in obj)`. – Samwise Mar 12 '23 at 20:19
  • `assert isinstance(obj, list) and all(isinstance(i, str) for i in obj), f"obj ({obj}) must be a list of strings" It will create an AssertionError if it is not a list of strings – Jason Grace Mar 12 '23 at 20:22
  • 1
    _In this case, I want it to be a list of strings_ "list of strings" is not a type. "list" is a type. The only way to get what you want is to 1) check that the type of the outer object is `list`, and 2) check that the type of each object inside the list is `str`. – John Gordon Mar 12 '23 at 21:23
  • because `list[str]` is not an actual type. The type is just `list` – juanpa.arrivillaga Mar 12 '23 at 21:23

2 Answers2

3

You can simply print the type of the types to see why they are not equal.

print(type(type(['a', 'b']))) #<class 'type'>
print(type(list[str]))        #<class 'types.GenericAlias'>

Anyway it seems, like I'll have to use a for loop, if this doesn't work.

There is a way you could do this without an apparent loop, but it is more like a hack. join expects an Iterable of str. If it gets anything other than that, it raises an except clause. This means you could just test the list against join.

from typing import Any

def is_liststr(obj:Any) -> bool:
    try   : ''.join(obj)
    except: return False
        
    #make sure it is specifically a list
    return isinstance(obj, list)
    
print(is_liststr(['a', 1]))   #False
print(is_liststr(('a', 'b'))) #False
print(is_liststr(['a', 'b'])) #True
OneMadGypsy
  • 4,640
  • 3
  • 10
  • 26
  • why even suggest such a weird, obfuscated way of doing it? – juanpa.arrivillaga Mar 12 '23 at 21:24
  • 1
    @juanpa.arrivillaga ~ because thinking outside of convention is not a sin, it can actually make you much better at thinking, at all. I do not deny your claim. I also do not deny that this will 100% work, without manually looping over the contents. It may even be faster, but I have not tested it so, I am not sure. – OneMadGypsy Mar 12 '23 at 21:28
  • @juanpa.arrivillaga ~ may I add: anything we would have done manually in python to check the type, is circumstantially built-in to the `join` function. Using it in this way is allowing `c` to handle the looping and conditions. Albeit strange, in some rights, it's actually brilliant. I'll admit that using `join` in this way has the same spirit as a `side effect`, and is bad practice. That's unfortunate. – OneMadGypsy Mar 12 '23 at 22:12
  • 1
    I regret the tone of my original comment. But my primary concern isn't convention, except maybe as an ancillary concern. My primary concern is clarity. with regards to performance, I might also argue that it is unexpected that such a function would create one large string out of the input. – juanpa.arrivillaga Mar 12 '23 at 23:14
  • @juanpa.arrivillaga ~ your tone was fine. I haven't received any down votes, which means you didn't give me any. There was arguably respect for my "bonus" possibility, regardless of your agreement with it. I am 100% aware that my solution is strange and hope that automatically claiming it is a hack, puts enough of a warning label on it, for it to be "taken with a grain of salt". – OneMadGypsy Mar 12 '23 at 23:31
-1

Python is not a strongly typed language. Unlike in C# and other languages, letters = ["a", "b", "c"] is not stored as a list of strings, but simply as a generic list. list[str] is a type hint in python, not a base type. In order to check whether your variable is a list, instead write type_ok = (type(obj) == list). The only issue with this approach is that it doesn't check whether the list is made of purely strings, but that could be resolved with another check.

Edit: As chepner noted, isinstance() is usually always better. My initial example was showcasing how your current approach was incorrect, but yes, use isinstance().

David Latimer
  • 65
  • 2
  • 9