2009-09-13 2 views
11

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

В настоящее время у меня есть это:

def my_func(input): 
    if not isinstance(input, list): input = [input] 
    for e in input: 
     ... 

Я работаю с существующим API, так что я не могу изменить параметры ввода. Использование isinstance() чувствует себя взломанным, так ли есть способ сделать это?

+2

re: правильный. В Python обычно термин «Pythonic». Он признает, что есть много способов сделать что-то, но некоторые из них более соответствуют духу языка. –

ответ

6

Мне нравится предложение Андрея Вайны hasattr(var,'__iter__'). Обратите внимание, эти результаты некоторых типичных типов Python:

>>> hasattr("abc","__iter__") 
False 
>>> hasattr((0,),"__iter__") 
True 
>>> hasattr({},"__iter__") 
True 
>>> hasattr(set(),"__iter__") 
True 

Это имеет дополнительное преимущество обработки строки как не-итерация - строки серая зона, как иногда вы хотите, чтобы рассматривать их как элемент, в другое время как последовательность символов.

Обратите внимание, что в Python 3, str тип делает имеют атрибут __iter__ и это не работает:

>>> hasattr("abc", "__iter__") 
True 
+0

Разве вы не имеете в виду мое предложение? – Unknown

+0

Мой комментарий к ответу Питера был первым. Ха! :П –

-1

Это похоже на разумный способ сделать это. Вы хотите проверить, является ли элемент списком, и это выполняется непосредственно. Это становится все более сложным, если вы хотите, чтобы поддерживать другие «список-как» типы данных, также, например:

isinstance(input, (list, tuple)) 

или в более общем плане, абстрагироваться от вопроса:

def iterable(obj): 
    try: 
    len(obj) 
    return True 
    except TypeError: 
    return False 

, но опять же, резюме, ваш метод прост и правилен, что звучит хорошо для меня!

+2

Я думаю, вы можете использовать что-то вроде 'hasattr (a, '__iter __')', чтобы увидеть, является ли это типом данных типа «list-like». –

+1

-1. Итерации не обязательно имеют __len__, и они не обязательно конечны. – Triptych

0

Вы можете сделать прямые сравнения типов, используя type().

def my_func(input): 
    if not type(input) is list: 
     input = [input] 
    for e in input: 
     # do something 

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

+0

такое прямое сравнение не является отличной идеей - если пользователь подклассифицирует «список», например, ваше сравнение типа '() на основе' type() будет немедленно нарушено. 'isinstance()' ближе к тому, что хочет OP. – Peter

+0

Точно, я давал альтернативы, но мое замечание «однако» говорит ему, что его путь все еще лучше. – Soviut

0

Ваш aproach кажется мне прав.

Это похоже на то, как вы используете atom? в Lisp, когда вы перебираете списки и проверяете текущий элемент, чтобы увидеть, является ли он списком или нет, потому что если это список, который вы хотите обработать своими элементами тоже.

Итак, да, не вижу в этом ничего плохого.

2

Вы можете поместить * до вашего аргумента, таким образом, вы всегда будете получать кортеж:

def a(*p): 
    print type(p) 
    print p 

a(4) 
>>> <type 'tuple'> 
>>> (4,) 

a(4, 5) 
>>> <type 'tuple'> 
>>> (4,5,) 

Но это заставит вас назвать вашу функцию с переменными параметрами, я если что не знаю " приемлемый для вас.

+0

Это не работает для него, потому что он сказал, что не может изменить аргументы, и кто-то может войти в [1,2], и он будет дважды завернут как ([1,2],) – Unknown

+0

Хо, прости, увидел, где он сказал, что он работает над апи. моя вина. – attwad

11

Как правило, строки (простые и Unicode) являются единственным итерируемыми, что вы хотите все-таки рассматривать как «отдельные элементы» - basestring встроенных существует СПЕЦИАЛЬНО, чтобы позволить вам проверить для любого вида строк с isinstance, так что это очень ООН -grotty для этого частного случая ;-).

Таким образом, мой подход, предложенный в самом общем случае:

if isinstance(input, basestring): input = [input] 
    else: 
    try: iter(input) 
    except TypeError: input = [input] 
    else: input = list(input) 

Это способ лечения КАЖДОЙ итерации КРОМЕ строк в виде списка непосредственно, строк и чисел и другие не итерируемые как скаляры (быть нормированные в списки отдельных элементов).

Я явно делаю список из всех возможных итераций, поэтому вы ЗНАЕТЕ, что можете продолжить выполнение КАЖДОГО вида трюков списка - сортировка, повторение нескольких раз, добавление или удаление элементов для облегчения итерации и т. Д., Без изменение списка ACTUAL (если список действительно был ;-). Если все, что вам нужно, это одна простая for петли то, что последний шаг не является обязательным (и действительно бесполезно, если, например, ввод представляет собой огромный открытый файл), и я хотел бы предложить вспомогательный генератор вместо:

def justLoopOn(input): 
    if isinstance(input, basestring): 
    yield input 
    else: 
    try: 
     for item in input: 
     yield item 
    except TypeError: 
     yield input 

теперь каждом одна из функций, требующих такой аргумент нормализации, вы просто использовать:

for item in justLoopOn(input): 

вы можете использовать вспомогательную нормализующее-функцию даже в другом случае (если вам нужен реальный список для дальнейшего гнусных целей); на самом деле, в таких (более редких) случаях, вы можете просто сделать:

thelistforme = list(justLoopOn(input)) 

так, что (неизбежно) несколько опушенные логика нормализации только в одном месте, так же, как это должно быть -)

+0

Вызов 'iter (input)' и игнорирование его результата не пустая трата ресурсов? Было бы не лучше, если бы вы только проверяли существование атрибута '__iter__'? –

+0

@Cristian, я не думаю, что это дорого, может быть один вызов функции, который будет делать почти то же самое, что вы предлагаете, а также не полагается на проверку атрибута magic '__iter__' –

+3

' try'/'iter (x) '/' except' не особо дорого. И, 'iter (x)' преуспевает для некоторого объекта w/o '__iter__' (например, объект w/подходящий' __getitem__' принимающий числовые ключи, но не '__iter__'). –

0

! Это хороший способ сделать это (не забудьте включить кортежи).

Однако вы также можете рассмотреть, есть ли аргумент __iter__ method или __getitem__ method. (Обратите внимание, что строки имеют __getitem__ вместо __iter__.)

hasattr(arg, '__iter__') or hasattr(arg, '__getitem__') 

Это, вероятно, наиболее общее требование для списка подобных типов, чем только проверка типа.

3

Во-первых, не существует общего метода, который мог бы сказать «один элемент» из «списка элементов ", поскольку по определению список может быть элементом другого списка.

Я бы сказал, что вам нужно определить, какие данные вы могли бы иметь, так что вы можете иметь:

  • любой потомок list против чего-либо еще
    • испытания с isinstance(input, list) (так что ваш пример является правильным)
  • любой тип последовательности, за исключением строк (basestring в Python 2.x, str в Python 3.х)
    • Используйте метаклассом последовательность: isinstance(myvar, collections.Sequence) and not isinstance(myvar, str)
  • некоторый тип последовательности против известных случаев, как int, str, MyClass
    • Тест с isinstance(input, (int, str, MyClass))
  • любой итерацию, кроме строк:
    • Тест с

.

try: 
     input = iter(input) if not isinstance(input, str) else [input] 
    except TypeError: 
     input = [input] 
Смежные вопросы