Как я могу определить, является ли переменная python строкой или списком? - PullRequest
61 голосов
/ 07 мая 2009

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

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

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

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

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

Ответы [ 8 ]

43 голосов
/ 07 мая 2009
isinstance(your_var, basestring)
37 голосов
/ 07 мая 2009

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

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

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

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

32 голосов
/ 07 мая 2009

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

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

func( 'file1', 'file2', 'file3' )
func( 'file1' )
func( *listOfFiles )
16 голосов
/ 07 мая 2009

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

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

func(['file1'])

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

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

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

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

11 голосов
/ 08 мая 2009
def func(files):
    for f in files if not isinstance(files, basestring) else [files]:
        doSomethingWithFile(f)

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

func('file1')
11 голосов
/ 08 мая 2009
if hasattr(f, 'lower'): print "I'm string like"
6 голосов
/ 19 июня 2012

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

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 голосов
/ 08 мая 2009

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

Надеюсь, это полезно кому-то еще.

...