Как преобразовать строковое представление списка в список?

532

Мне было интересно, как проще всего преобразовать stringсписок вроде следующего в list:

x = u'[ "A","B","C" , " D"]'

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

x = ["A", "B", "C", "D"] 

в Python.

Я знаю , что может лишить пространства с strip()и с split()помощью разделения оператора и проверок для не алфавитов. Но код становился очень грязным. Есть ли быстрая функция, о которой я не знаю?

harijay
источник
4
Что вы на самом деле пытаетесь достичь? Вероятно, есть гораздо лучший способ, чем пытаться преобразовать синтаксис списка Python в реальный список ...
Николас Найт
1
Какую версию Python вы используете?
Марк Байерс
2
@Nicholas Knight: я пытаюсь обработать пользовательский ввод в унаследованном приложении, где все списки были введены в виде списков Unicode с квадратными скобками. @ Марк Байерс, я использую Python 2.6, поэтому аст.литеральный подход работает лучше всего
harijay

Ответы:

769
>>> import ast
>>> x = u'[ "A","B","C" , " D"]'
>>> x = ast.literal_eval(x)
>>> x
['A', 'B', 'C', ' D']
>>> x = [n.strip() for n in x]
>>> x
['A', 'B', 'C', 'D']

ast.literal_eval :

С ast.literal_eval вы можете безопасно оценить узел выражения или строку, содержащую выражение Python. Предоставленная строка или узел могут состоять только из следующих литеральных структур Python: строк, чисел, кортежей, списков, диктов, логических значений и None.

Сообщество
источник
6
Согласно комментарию ниже, это опасно, поскольку он просто запускает любой питон в строке. Так что, если кто-то позвонит, чтобы удалить все там, он с радостью сделает это.
Пол Кенджора
16
@PaulKenjora: Вы думаете eval, нет ast.literal_eval.
user2357112 поддерживает Monica
19
ast.literal_evalэто безопаснее , чем eval, но это на самом деле не безопасно . Как объясняется в последних версиях документации : «Предупреждение. Возможно, сбой интерпретатора Python с достаточно большой / сложной строкой из-за ограничений глубины стека в компиляторе Python AST». На самом деле, возможно, можно выполнить произвольный код с помощью осторожной атаки с разбивкой стека, хотя, насколько я знаю, никто не создает публичного доказательства концепции для этого.
18:00
Ну а что делать, если в списке нет кавычек? например, [4 из B, 1 из G]
sqp_125
84

jsonМодуль является лучшим решением , когда есть строковой список словарей. json.loads(your_data)Функция может быть использована для преобразования его в список.

>>> import json
>>> x = u'[ "A","B","C" , " D"]'
>>> json.loads(x)
[u'A', u'B', u'C', u' D']

по аналогии

>>> x = u'[ "A","B","C" , {"D":"E"}]'
>>> json.loads(x)
[u'A', u'B', u'C', {u'D': u'E'}]
Райан
источник
Однако я не хочу возвращаемый список в формате Unicode. но похоже, что даже если я удаляю u '' из строки, он все равно обрабатывает данные как Unicode.
Мансур Акрам
7
Это работает для целых, но не для строк в моем случае, потому что каждая строка одинарная, а не двойная, вздох.
Пол Кенджора
4
Согласно комментарию @ PaulKenjora, это работает для, '["a","b"]'но не для "['a','b']".
Скиппи ле Гран Гуру
83

Это evalопасно - вы не должны выполнять пользовательский ввод.

Если у вас 2.6 или новее, используйте ast вместо eval:

>>> import ast
>>> ast.literal_eval('["A","B" ,"C" ," D"]')
["A", "B", "C", " D"]

Как только у вас есть это, stripстроки.

Если вы используете более старую версию Python, вы можете получить очень близко к желаемому с помощью простого регулярного выражения:

>>> x='[  "A",  " B", "C","D "]'
>>> re.findall(r'"\s*([^"]*?)\s*"', x)
['A', 'B', 'C', 'D']

Это не так хорошо, как решение ast, например, оно неправильно обрабатывает экранированные кавычки в строках. Но это просто, не требует опасного eval, и может быть достаточно для вашей цели, если вы на старом Python без ast.

Марк Байерс
источник
Не могли бы вы сказать мне, что, почему вы сказали « evalОпасно - вы не должны выполнять ввод пользователя»? Я использую 3,6
Аарян Деван
1
@AaryanDewan, если вы используете evalнапрямую, он оценит любое допустимое выражение Python, которое потенциально опасно. literal_evalрешает эту проблему, оценивая только литеральные структуры Python: строки, числа, кортежи, списки, dicts, booleans и None.
Абхишек Менон
14
import ast
l = ast.literal_eval('[ "A","B","C" , " D"]')
l = [i.strip() for i in l]
вздор
источник
10

Есть быстрое решение:

x = eval('[ "A","B","C" , " D"]')

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

x = [x.strip() for x in eval('[ "A","B","C" , " D"]')]
Алексей Шолик
источник
это все равно сохранит пробелы внутри кавычек
tosh
17
Это открытое приглашение к выполнению произвольного кода, НИКОГДА не делайте этого или чего-либо подобного, если только вы не знаете с абсолютной уверенностью, что ввод всегда будет на 100% доверенным.
Николас Найт
1
Я мог использовать это предложение, потому что я знал, что мои данные всегда будут в этом формате, и это была работа по обработке данных.
Маниш Ранджан
9

Вдохновленный некоторыми ответами выше, которые работают с базовыми пакетами Python, я сравнил производительность нескольких (используя Python 3.7.3):

Способ 1: аст

import ast
list(map(str.strip, ast.literal_eval(u'[ "A","B","C" , " D"]')))
# ['A', 'B', 'C', 'D']

import timeit
timeit.timeit(stmt="list(map(str.strip, ast.literal_eval(u'[ \"A\",\"B\",\"C\" , \" D\"]')))", setup='import ast', number=100000)
# 1.292875313000195

Способ 2: JSON

import json
list(map(str.strip, json.loads(u'[ "A","B","C" , " D"]')))
# ['A', 'B', 'C', 'D']

import timeit
timeit.timeit(stmt="list(map(str.strip, json.loads(u'[ \"A\",\"B\",\"C\" , \" D\"]')))", setup='import json', number=100000)
# 0.27833264000014424

Способ 3: нет импорта

list(map(str.strip, u'[ "A","B","C" , " D"]'.strip('][').replace('"', '').split(',')))
# ['A', 'B', 'C', 'D']

import timeit
timeit.timeit(stmt="list(map(str.strip, u'[ \"A\",\"B\",\"C\" , \" D\"]'.strip('][').replace('\"', '').split(',')))", number=100000)
# 0.12935059100027502

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

kinzleb
источник
9

Если это только одномерный список, это можно сделать, ничего не импортируя:

>>> x = u'[ "A","B","C" , " D"]'
>>> ls = x.strip('[]').replace('"', '').replace(' ', '').split(',')
>>> ls
['A', 'B', 'C', 'D']
Руохола
источник
8
Предупреждение: это может быть опасно, если между какой-либо строкой внутри списка стоит запятая.
Хасан Камаль
Это не будет работать, если ваш список строк представляет собой список списков
crypdick
@crypdick Хороший вопрос, добавил примечание об этом :)
ruohola
6

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

import re
x = u'[ "A","B","C" , " D"]'
junkers = re.compile('[[" \]]')
result = junkers.sub('', x).split(',')
print result
--->  [u'A', u'B', u'C', u'D']

Переменная junkers содержит скомпилированное регулярное выражение (для скорости) всех символов, которые нам не нужны, используя] в качестве символа, требующего некоторой хитрости с обратной косой чертой. Re.sub заменяет все эти символы ничем, и мы разделяем полученную строку на запятые.

Обратите внимание, что при этом также удаляются пробелы внутри записей u '["oh no"]' ---> [u'ohno ']. Если это не то, что вы хотели, регулярное выражение нужно немного улучшить.

dirkjot
источник
4

Если вы знаете, что ваши списки содержат только строки в кавычках, этот пример разбора даст вам ваш список разделенных строк (даже с сохранением оригинальной Unicode-ness).

>>> from pyparsing import *
>>> x =u'[ "A","B","C" , " D"]'
>>> LBR,RBR = map(Suppress,"[]")
>>> qs = quotedString.setParseAction(removeQuotes, lambda t: t[0].strip())
>>> qsList = LBR + delimitedList(qs) + RBR
>>> print qsList.parseString(x).asList()
[u'A', u'B', u'C', u'D']

Если ваши списки могут иметь больше типов данных или даже содержать списки в списках, то вам понадобится более полная грамматика - такая, как эта в вики с pyparsing, которая будет обрабатывать кортежи, списки, целые числа, числа с плавающей запятой и строки в кавычках. Будет работать с версиями Python до 2.4.

PaulMcG
источник
дайте мне знать, как использовать "parseString (). asList ()", если у меня есть строка такого типа: '["A", "B", "C", ["D"]]', как вы заявил, что pyparsing может сделать это также. но я, кажется, не нашел правильный способ сделать это.
Мансур Акрам
«Если в ваших списках может быть больше типов данных или даже они содержат списки в списках, вам понадобится более полная грамматика» - просмотрите ссылку, которую я предоставил в своем ответе, для анализатора, который будет обрабатывать вложенные списки, и различных других типов данных.
PaulMcG
Pyparsing больше не размещается на вики-пространствах. parsePythonValue.pyПример теперь на GitHub в github.com/pyparsing/pyparsing/blob/master/examples/...
PaulMcG
1

Для дальнейшего завершения ответа @Ryan с использованием json, одна очень удобная функция для преобразования юникода - та, что размещена здесь: https://stackoverflow.com/a/13105359/7599285

например с двойными или одинарными кавычками:

>print byteify(json.loads(u'[ "A","B","C" , " D"]')
>print byteify(json.loads(u"[ 'A','B','C' , ' D']".replace('\'','"')))
['A', 'B', 'C', ' D']
['A', 'B', 'C', ' D']
CptHwK
источник
0

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

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

def parse_strlist(sl):
import re
clean = re.sub("[\[\],\s]","",sl)
splitted = re.split("[\'\"]",clean)
values_only = [s for s in splitted if s != '']
return values_only

тестовый образец : "['21'," foo "'6', '0'," A "]"

Джорди Ван Ландегем
источник
0

и с чистым питоном - не импортировать библиотеки

[x for x in  x.split('[')[1].split(']')[0].split('"')[1:-1] if x not in[',',' , ',', ']]
Иоаннис Насиос
источник
0

Вы можете столкнуться с такой проблемой, когда имеете дело с очищенными данными, хранящимися в Pandas DataFrame.

Это решение работает как шарм, если список значений представлен в виде текста .

def textToList(hashtags):
    return hashtags.strip('[]').replace('\'', '').replace(' ', '').split(',')

hashtags = "[ 'A','B','C' , ' D']"
hashtags = textToList(hashtags)

Output: ['A', 'B', 'C', 'D']

Внешняя библиотека не требуется.

dobydx
источник
-1

Итак, следуя всем ответам, я решил выбрать наиболее распространенные методы:

from time import time
import re
import json


my_str = str(list(range(19)))
print(my_str)

reps = 100000

start = time()
for i in range(0, reps):
    re.findall("\w+", my_str)
print("Regex method:\t", (time() - start) / reps)

start = time()
for i in range(0, reps):
    json.loads(my_str)
print("json method:\t", (time() - start) / reps)

start = time()
for i in range(0, reps):
    ast.literal_eval(my_str)
print("ast method:\t\t", (time() - start) / reps)

start = time()
for i in range(0, reps):
    [n.strip() for n in my_str]
print("strip method:\t", (time() - start) / reps)



    regex method:    6.391477584838867e-07
    json method:     2.535374164581299e-06
    ast method:      2.4425282478332518e-05
    strip method:    4.983267784118653e-06

Итак, в конце концов, регулярное выражение побеждает!

проходит мимо
источник
-1

Вы можете сохранить себя .strip () fcn, просто вырезав первый и последний символы из строкового представления списка (см. третью строку ниже)

>>> mylist=[1,2,3,4,5,'baloney','alfalfa']
>>> strlist=str(mylist)
['1', ' 2', ' 3', ' 4', ' 5', " 'baloney'", " 'alfalfa'"]
>>> mylistfromstring=(strlist[1:-1].split(', '))
>>> mylistfromstring[3]
'4'
>>> for entry in mylistfromstring:
...     print(entry)
...     type(entry)
... 
1
<class 'str'>
2
<class 'str'>
3
<class 'str'>
4
<class 'str'>
5
<class 'str'>
'baloney'
<class 'str'>
'alfalfa'
<class 'str'>
JCMontalbano
источник