How can I tell if a python variable is a string or a list?

这一生的挚爱 提交于 2019-11-27 12:16:29

Well, there's nothing unpythonic about checking type. Having said that, if you're willing to put a small burden on the caller:

def func( *files ):
    for f in files:
         doSomethingWithFile( f )

func( *['file1','file2','file3'] ) #Is treated like func('file1','file2','file3')
func( 'file1' )

I'd argue this is more pythonic in that "explicit is better than implicit". Here there is at least a recognition on the part of the caller when the input is already in list form.

isinstance(your_var, basestring)

Personally, I don't really like this sort of behavior -- it interferes with duck typing. One could argue that it doesn't obey the "Explicit is better than implicit" mantra. Why not use the varargs syntax:

def func( *files ):
    for f in files:
        doSomethingWithFile( f )

func( 'file1', 'file2', 'file3' )
func( 'file1' )
func( *listOfFiles )

I would say the most Python'y way is to make the user always pass a list, even if there is only one item in it. It makes it really obvious func() can take a list of files

def func(files):
    for cur_file in files:
        blah(cur_file)

func(['file1'])

As Dave suggested, you could use the func(*files) syntax, but I never liked this feature, and it seems more explicit ("explicit is better than implicit") to simply require a list. It's also turning your special-case (calling func with a single file) into the default case, because now you have to use extra syntax to call func with a list..

If you do want to make a special-case for an argument being a string, use the isinstance() builtin, and compare to basestring (which both str() and unicode() are derived from) for example:

def func(files):
    if isinstance(files, basestring):
        doSomethingWithASingleFile(files)
    else:
        for f in files:
            doSomethingWithFile(f)

Really, I suggest simply requiring a list, even with only one file (after all, it only requires two extra characters!)

def func(files):
    for f in files if not isinstance(files, basestring) else [files]:
        doSomethingWithFile(f)

func(['file1', 'file2', 'file3'])

func('file1')
limscoder
if hasattr(f, 'lower'): print "I'm string like"
Dana the Sane

If you have more control over the caller, then one of the other answers is better. I don't have that luxury in my case so I settled on the following solution (with caveats):

def islistlike(v):
   """Return True if v is a non-string sequence and is iterable. Note that
   not all objects with getitem() have the iterable attribute"""
   if hasattr(v, '__iter__') and not isinstance(v, basestring):
       return True
   else:
       #This will happen for most atomic types like numbers and strings
       return False

This approach will work for cases where you are dealing with a know set of list-like types that meet the above criteria. Some sequence types will be missed though.

Varargs was confusing for me, so I tested it out in Python to clear it up for myself.

First of all the PEP for varargs is here.

Here is sample program, based on the two answers from Dave and David Berger, followed by the output, just for clarification.

def func( *files ):
    print files
    for f in files:
        print( f )

if __name__ == '__main__':
    func( *['file1','file2','file3'] ) #Is treated like func('file1','file2','file3')
    func( 'onestring' )
    func( 'thing1','thing2','thing3' )
    func( ['stuff1','stuff2','stuff3'] )

And the resulting output;

('file1', 'file2', 'file3')
file1
file2
file3
('onestring',)
onestring
('thing1', 'thing2', 'thing3')
thing1
thing2
thing3
(['stuff1', 'stuff2', 'stuff3'],)
['stuff1', 'stuff2', 'stuff3']

Hope this is helpful to somebody else.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!