如何判断Python变量是字符串还是列表?

63

我的一个例程需要将字符串列表作为参数,但我希望支持传入一个单一的字符串并将其转换为只包含一个字符串的列表。例如:

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

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

func( 'file1' ) # should be treated like ['file1']

如何判断函数的参数是字符串还是列表?我知道有type函数,但是否有更符合Python风格的方式?

8个回答

40
isinstance(your_var, basestring)

7
这个解决方案需要一些解释和上下文。你的变量和basestring都没有出现在原帖中。 - ExcessOperatorHeadspace
9
这个在 Python 3 中不起作用。请使用 isinstance(your_var, str) - Attila Tanyi
其他评论也同样不起作用。导入six并检查它是否为字符串,这样可以实现Python2和3的兼容性以及Python2中的UTF字符串支持。 - MrMesees

37

嗯,检查类型并没有不符合Python的特性。话虽如此,如果你愿意给调用者增加一点负担:

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

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

我认为这更符合“显式优于隐式”的Python风格。调用者至少能够意识到输入已经是列表形式。


11
肯定更“明确”的方法是要求用户在列表中传递单个文件,比如func(['file1'])。 - dbr
2
我同意那是明确的,但我不明白它为什么更明确。两种技术使用相同的逻辑;一种是另一种的反向。我认为上面的方法稍微更直观,因为它强调列表中的文件而不是列表本身与func相关。 - David Berger

32

就我个人而言,我并不太喜欢这种行为——它会干扰鸭子类型。有人可能会认为它不遵守“显式优于隐式”的口号。为什么不使用varargs语法呢:

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

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

1
个人而言,我并不是很喜欢这种行为,你具体指的是什么? - James McMahon
1
@nemo 我认为他指的是原始问题,这可能被解释为要求一个函数,其参数是字符串或字符串列表,其中函数调用没有提及传递的参数类型。我同意这一点。 - David Berger
1
就此而言,只有在没有其他参数的情况下,可变参数才能正常工作:func(file, someflag),否则将无法将其转换为func(*files, someflag),除非将someflag转换为关键字参数。 - pjz

16
我认为最Pythonic的方法是让用户始终传递一个列表,即使其中只有一项。这样可以清楚地表明func()可以接受文件列表。
def func(files):
    for cur_file in files:
        blah(cur_file)

func(['file1'])

正如Dave建议的那样,你可以使用func(*files)语法,但我从来不喜欢这个特性,并且似乎更明确(“明确胜于隐晦”)只需要求传递一个列表即可。这还将调用单个文件的特殊情况转换为默认情况,因为现在您必须使用额外的语法来使用列表调用func

如果确实想要针对参数为字符串的特殊情况进行处理,可以使用内置的isinstance()函数,并与basestring进行比较(其中包括str()unicode()),例如:

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

实际上,我建议只需求一个列表,即使只有一个文件(毕竟,它只需要额外的两个字符!)


15
问题在于,如果您仅依赖鸭子类型,Python 会将字符串视为具有列表特性的对象进行处理,这样就无法得到正确的结果。Python 会将字符串视为由字符组成的列表,而不是单独的一个对象。 - Robert P
请注意,Python 3 中没有 basestring。除非您正在引用字节对象(对于此用例绝对不应该这样做),否则请使用 str - Soren Bjornstad

13
if hasattr(f, 'lower'): print "I'm string like"

一个非常简单的解决方案,对我来说完美地解决了问题。谢谢! - Jamie Brown
3
直到有人向你传递一个带有“lower”方法的集合,这种方法才有效 :-/ - AlexChaffee
1
例如数学对象,或者与现实世界函数进行接口的对象,比如升降机械控制的窗户。 :) - Joshua Burns
casefold 方法可能更像字符串。 - Eponymous
这个程序可能不是最健壮的,正如其他人所指出的那样,但它确实非常适合鸭子类型。 - MrMesees

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

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

func('file1')

POP 8似乎更喜欢使用isinstance(u'abc', basestring)而不是使用types模块。 - max
@mdorseif:我已经编辑了答案,使用basestring代替types模块。 - jfs

6
如果您对调用者有更多的控制权,那么其他答案可能更好。在我的情况下,我没有这种奢侈,因此我采用了以下解决方案(带有警告):
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

这种方法仅适用于处理已知符合上述条件的列表类型。但是,一些序列类型将被忽略。


3

对于我来说,可变参数很困惑,因此我在Python中进行了测试,以便自己澄清。

首先,varargs的PEP在这里

以下是一个示例程序,基于Dave和David Berger的两个答案,后跟输出,仅供澄清。

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'] )

并得到如下的输出结果:
('file1', 'file2', 'file3')
file1
file2
file3
('onestring',)
onestring
('thing1', 'thing2', 'thing3')
thing1
thing2
thing3
(['stuff1', 'stuff2', 'stuff3'],)
['stuff1', 'stuff2', 'stuff3']

希望这对其他人有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接