22

How to check if an object is a list of strings? I could only check if an object is string as such:

def checktype(obj):
  if isinstance(obj,str):
    print "It's a string"

obj1 = ['foo','bar','bar','black','sheet']
obj2 = [1,2,3,4,5,'bar']
obj3 = 'bar'

for i in [obj1,obj2,obj3]:
  checktype(i)

Desired output:

It's a list of strings
It's not a list of strings or a single string
It's a single string
Asclepius
  • 57,944
  • 17
  • 167
  • 143
alvas
  • 115,346
  • 109
  • 446
  • 738
  • 3
    For clarity I would rename `checktype` to `is_list_of_string`. – Steven Rumbalski Aug 28 '13 at 18:11
  • 4
    One edge case to consider: Is `[]` a list of strings, or not? – abarnert Aug 28 '13 at 18:15
  • nope `[]` wouldn't be a list of string, it's an empty list. – alvas Aug 28 '13 at 18:36
  • 2
    @2er0: OK. Usually people want to consider `[]` a valid list of 0 anythings, so that's what functions like `all` do. So an answer that just relies on `all` (like Sukrit Kalra's current version) is going to return `True` rather than `False` in that case. The simplest fix is to do `if obj and [rest of check]`. – abarnert Aug 28 '13 at 18:47
  • `[]` is a valid empty list of strings. I have checked it by validating and it works fine. – whatsinthename Nov 29 '22 at 09:38

6 Answers6

25

Something like this, I presume? You could do some checks to see if it's a single string.

>>> def checktype(obj):
        return bool(obj) and all(isinstance(elem, basestring) for elem in obj)

>>> obj1 = ['foo','bar','bar','black','sheet']
>>> obj2 = [1,2,3,4,5,'bar']
>>> obj3 = 'bar'
>>> for i in [obj1, obj2, obj3] :
        print checktype(i)


True
False
True

Why check for basestring instead of str?

You should check for basestring instead of str since it's a common class from which both the str and unicode types inherit from. Checking only the str leaves out the unicode types.

As per Steven Rumbalski's suggestions, if you need to specifically check for a list of strings, you could do.

>>> def is_list_of_strings(lst):
        return bool(lst) and not isinstance(lst, basestring) and all(isinstance(elem, basestring) for elem in lst)
        # You could break it down into `if-else` constructs to make it clearer to read.

>>> for i in [obj1, obj2, obj3] :
        print is_list_of_strings(i)


True
False
False

EDIT - As per abarnert's suggestion, you could also check for a list instead of not isinstance(lst, basestring), the code would get rewritten as.

>>> def is_list_of_strings(lst):
        return bool(lst) and isinstance(lst, list) and all(isinstance(elem, basestring) for elem in lst)
        # You could break it down into `if-else` constructs to make it clearer to read.

>>> for i in [obj1, obj2, obj3] :
        print is_list_of_strings(i)


True
False
False

Moving away from one liners, we could use.

>>> def is_list_of_strings(lst):
        if lst and isinstance(lst, list):
            return all(isinstance(elem, basestring) for elem in lst)
        else:
            return False
Community
  • 1
  • 1
Sukrit Kalra
  • 33,167
  • 7
  • 69
  • 71
  • 3
    Specification says to test for a *list* of strings, so `checktype('bar')` should return `False`. Good job using `basestring`. – Steven Rumbalski Aug 28 '13 at 18:09
  • 3
    Actually, I think it would be better to check `isinstance(lst, list)` than `not isinstance(lst, basestring)`. After all, a dict mapping strings to strings isn't a list of strings any more than a string is… But otherwise, great answer. – abarnert Aug 28 '13 at 18:14
  • @abarnert : Point taken, and added to the answer. Thanks. :) – Sukrit Kalra Aug 28 '13 at 18:17
  • 2
    I'm surprised you didn't notice the simpler version of @abarnert's suggestion: `return isinstance(lst, list) and all(isinstance(elem, basestring) for elem in lst)` – Mark Ransom Aug 28 '13 at 18:22
  • I did. I just thought that became too long for a single line. I'll fix that in a second. :) – Sukrit Kalra Aug 28 '13 at 18:23
  • 2
    I liked that you had split it up. I thought it was more readable that way. To each their own. Either way, it's acceptable to revise your original answer. That might be clearer than tacking edits to the end. – Steven Rumbalski Aug 28 '13 at 18:25
  • Since the OP has now added that he wants `[]` to fail, you need to add a test for `if lst` somewhere. – abarnert Aug 28 '13 at 18:46
  • @abarnert : Added a `bool(lst)` as the first check. :) – Sukrit Kalra Aug 28 '13 at 18:50
  • @SukritKalra: Don't use `bool(lst)`; just use `lst`, as [PEP 8](http://www.python.org/dev/peps/pep-0008/#programming-recommendations) explains. – abarnert Aug 28 '13 at 19:57
  • @abarnert : That would return an empty list as output for an input of an empty list. Not sure if that's what the OP wants. – Sukrit Kalra Aug 28 '13 at 19:58
  • 1
    @SukritKalra: Ah, I see, you're using `if lst and…` in the multiline statement, but `bool(lst)` just as a shortcut in the one-liner. I guess that unexplained shortcut is the least of the problems with a complex expression that won't even fit into 80 columns, and you already suggested breaking it down for readability and then showed exactly how to do it, so… already great as-is. Never mind. – abarnert Aug 28 '13 at 20:18
  • 2
    From Python 3 on, you just check for isinstance(lst, str) – Praveen Apr 15 '18 at 00:23
19

To test if all the items in a list are strings, use the all built-in and a generator:

if all(isinstance(s, str) for s in lis):

Note though that, if your list is empty, this will still return True since that is technically a list of 0 strings. However, since you want to consider [] as being False, you will need to do this:

if lis and all(isinstance(s, str) for s in lis):

So, your function should be something like this:

def checktype(obj):
    # This if statement makes sure input is a list that is not empty
    if obj and isinstance(obj, list): 
        return all(isinstance(s, str) for s in obj)
    else:
        return False

This function will only return True if its input is a list that is not empty and that is composed entirely of strings. Anything else (such as [], ['a', 1], ('a', 'b'), etc) will make it return False.

Also, using all in this way has an added bonus in that it stops checking on the first item it finds that returns False (is not a string). This allows you to work with very large lists quite efficiently.

Community
  • 1
  • 1
  • 3
    Good answer, but change the instance check from `str` to `basestr`. – Steven Rumbalski Aug 28 '13 at 18:21
  • 2
    … unless, of course, the OP really meant "`list` of `str`" rather than "`list` of strings"… which is possible, but he didn't say that's what he wanted. (@StevenRumbalski explains this pretty well in his answer, so I don't think it's necessary to explain it again here.) – abarnert Aug 28 '13 at 18:23
  • Well, I went with `str` because, in the OP's post, he was checking if it was type `str`, not `basestring`. –  Aug 28 '13 at 18:28
  • Sorry, I meant Sukrit Kalra's answer, not Steven Rumbalski's. I should have known that an @ and an s would only autocomplete to people who'd commented on this answer, not to everyone on this page… – abarnert Aug 28 '13 at 18:42
  • @iCodez : Your code would return an empty list as output for an empty list. Your previous `if` check worked perfectly. :) – Sukrit Kalra Aug 28 '13 at 19:10
  • 1
    This doesn't quite "return `True` if its input is a list that is not empty and that is composed entirely of strings". If the input is an empty list, it returns the input. This may be especially surprising if the input has something added to it while the return value is still being used. – user2357112 Aug 28 '13 at 19:12
  • Oops! Don't know why I did that. I must've been trying to make it like the example if statement I gave above the function. Fixed it now. –  Aug 28 '13 at 19:52
3

This answer is for Python 3. If for example the variable name is pins:

if not (pins and isinstance(pins, list) and all(isinstance(pin, str) for pin in pins)):
    raise TypeError('pins must be a list of one or more strings.')

It checks for three things:

  1. Is is non-empty?
  2. Is it a list?
  3. Does it contain strings?

If you also need to check for uniqueness of the strings, include this fourth check:

and (len(pins) == len(set(pins)))
Asclepius
  • 57,944
  • 17
  • 167
  • 143
2

As a one liner:

assert all(map(lambda x: isinstance(x, str), my_list))
HeyMan
  • 1,529
  • 18
  • 32
1

The answers I've read so far raise exepctions when given a non-list that isn't a string...and isn't iterable either. That question is addressed in:

In Python, how do I determine if an object is iterable?

Taking the duck-typing approach:

def categorize(x):
    result = "not a string or list of strings"
    if isinstance(x, basestring):
        return "It's a single string"
    try:
        if all(isinstance(y, basestring) for y in x):
            return "It's a list of strings"
    except TypeError:
        pass
    return "It's not a list of strings or a single string"

data = [ 5, "xyzzy", list("xyzzy"), ['1', '23', 456]]
for x in data:
    print x, categorize(x)

Output:

5 It's not a list of strings or a single string
xyzzy It's a single string
['x', 'y', 'z', 'z', 'y'] It's a list of strings
['1', '23', 456] It's not a list of strings or a single string
Community
  • 1
  • 1
Mike Housky
  • 3,959
  • 1
  • 17
  • 31
0

You may also convert all elements to str: list(map(str, [1,2,3]))

Result: ['1', '2', '3']

HoangYell
  • 4,100
  • 37
  • 31