Как определить, является ли переменная python строкой или списком?

У меня есть подпрограмма, которая берет список строк в качестве параметра, но я хочу поддерживать передачу в одной строке и преобразовывать ее в список одной строки. Например:

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

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

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

Как моя функция может определить, была ли передана строка или список? Я знаю, что есть функция type, но есть ли "более питонический" способ?

Ответ 1

Ну, нет ничего лишнего в том, чтобы проверять тип. Сказав это, если вы готовы наложить небольшую нагрузку на вызывающего абонента:

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

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

Я бы сказал, что это более pythonic в том, что "явный лучше, чем неявный". Здесь есть хотя бы признание со стороны вызывающего абонента, когда вход уже находится в форме списка.

Ответ 2

isinstance(your_var, basestring)

Ответ 3

Лично мне не нравится такое поведение - это мешает печатать на утке. Можно утверждать, что он не подчиняется "Явной лучше, чем неявной" мантре. Почему бы не использовать синтаксис varargs:

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

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

Ответ 4

Я бы сказал, что большинство способов Python'а состоит в том, чтобы заставить пользователя всегда передавать список, даже если в нем есть только один элемент. Это делает его действительно очевидным. func() может взять список файлов

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

func(['file1'])

Как предложил Дейв, вы можете использовать синтаксис func(*files), но мне никогда не нравилась эта функция, и она кажется более явной ( "явный лучше, чем неявный" ), чтобы просто потребовать список. Он также превращает ваш специальный случай (вызов func с одним файлом) в случай по умолчанию, потому что теперь вам нужно использовать дополнительный синтаксис для вызова func со списком.

Если вы хотите сделать особый случай для аргумента, являющегося строкой, используйте isinstance() builtin и сравните с basestring (из которых как str(), так и unicode()):

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

Действительно, я предлагаю просто потребовать список, даже с одним файлом (в конце концов, он требует только двух дополнительных символов!)

Ответ 5

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

Ответ 6

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

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

func('file1')

Ответ 7

Если у вас больше контроля над вызывающим, тогда один из других ответов лучше. У меня нет такой роскоши в моем случае, поэтому я остановился на следующем решении (с оговорками):

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

Этот подход будет работать для случаев, когда вы имеете дело с набором знаний типов, подобных спискам, которые соответствуют вышеуказанным критериям. Однако некоторые типы последовательностей будут пропущены.

Ответ 8

Varargs запутался для меня, поэтому я тестировал его на Python, чтобы очистить его для себя.

Прежде всего PEP для varargs здесь.

Вот пример программы, основанной на двух ответах от Дэйва и Дэвида Бергера, а затем на выходе, только для уточнения.

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

Надеюсь, это поможет кому-то другому.