Як перетворити рядкове представлення списку в список?


531

Мені було цікаво, який найпростіший спосіб - перетворити такий stringсписок, як наступний, у list:

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

Навіть у випадку, якщо користувач розміщує пробіли між комами та пробілами всередині лапок. Мені потрібно це впоратися також:

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

в Python.

Я знаю , що може позбавити простору з strip()і з split()допомогою поділу оператора і перевірок для НЕ алфавітів. Але код став дуже химерним. Чи є швидка функція, про яку я не знаю?


4
Що ви насправді намагаєтесь досягти? Напевно, є набагато кращий спосіб, ніж намагатися перетворити синтаксис списку Python у фактичний список ...
Nicholas Knight

1
Яку версію Python ви використовуєте?
Марк Байєрс

2
@Nicholas Knight: Я намагаюся обробляти введення користувача у застарілому додатку, де всі списки були внесені як списки unicode із квадратними дужками. @Mark Byers, я використовую python 2.6, тому ast.literal підхід працює найкраще
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 підтримує Моніку

19
ast.literal_evalце безпечніше , ніж eval, але це на самому ділі не безпечно . Як пояснюють останні версії документів : "Попередження Можливо збити інтерпретатора Python досить великою / складною рядком через обмеження глибини стека в компіляторі AST Python." Насправді, можливо, запустити довільний код за допомогою ретельної атаки, що розбиває стеки, хоча, наскільки я знаю, ніхто не створює для цього публічного доказу концепції.
abarnert

Ну але що робити, якщо в Списку немає цитат? напр. [4 of B, 1 of 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.
Mansoor Akram

7
Це працює для ints, але не для рядків у моєму випадку, оскільки кожен рядок є одинарним, а не подвійним цитуванням, зітхання.
Пол Кенджора

4
Відповідно до коментаря @ PaulKenjora, він працює, '["a","b"]'але не для "['a','b']".
Skippy le Grand Gourou

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
Aaryan Dewan

1
@AaryanDewan, якщо ви користуєтесь evalбезпосередньо, він оцінить будь-яке дійсне вираження python, що є потенційно небезпечним. literal_evalвирішує цю проблему, лише оцінюючи літеральні структури Python: рядки, числа, кортежі, списки, дикти, булеві та None.
Абхішек Менон


10

Є швидке рішення:

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

Небажані пробіли в елементах списку можуть бути видалені таким чином:

x = [x.strip() for x in eval('[ "A","B","C" , " D"]')]

це все-таки збереже пробіли всередині цитат
09/09

17
Це відкрите запрошення до довільного виконання коду, НІКОЛИ не робіть цього чи будь-чого подібного, якщо тільки ви не знаєте з абсолютною впевненістю, що вхід завжди буде на 100% довіряти.
Миколай Лицар

1
Я міг би скористатися цією пропозицією, тому що я знав, що мої дані завжди будуть у такому форматі та є обробкою даних.
Manish Ranjan

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, як правило цінність читабельності за трохи ефективнішого варіанту, але, як завжди, це залежить.


9

Якщо це лише одновимірний список, це можна зробити, не імпортуючи нічого:

>>> x = u'[ "A","B","C" , " D"]'
>>> ls = x.strip('[]').replace('"', '').replace(' ', '').split(',')
>>> ls
['A', 'B', 'C', 'D']

8
Попереджувальна примітка: це може бути небезпечним, якщо будь-який з рядків всередині списку має кому між ними.
Хасан Камал

Це не спрацює, якщо ваш список рядків - це список списків
crypdick

@crypdick Добре, додав примітку про це :)
ruohola

6

Якщо припустити, що всі ваші входи є списками і що подвійні лапки у введенні насправді не мають значення, це можна зробити за допомогою простої заміни на регулярний вираз. Це трохи перл-у, але працює як шарм. Зауважте також, що тепер вихід є переліком рядків Unicode, ви не вказали, що вам це потрібно, але, мабуть, має сенс дане введення unicode.

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 '["о ні"]' ---> [u'ohno ']. Якщо це не те, що ви хотіли, регулярне вирівнювання потрібно трохи підготувати.


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

Якщо у ваших списках може бути більше типів даних або навіть містити списки у списках, то вам знадобиться більш повна граматика - як ця на вікі-програмі, що обробляє кортежі, списки, вставки, плавки та рядки з цитатами. Працюватиме з версіями Python до версії 2.4.


Ви дасте мені знати, як використовувати "parseString (). asList ()", якщо у мене є такий рядок: '["A", "B", "C", ["D"]] ", як ви заявили, що піпарінг може це зробити і. але, схоже, ви не знайшли правильного способу зробити це.
Mansoor Akram

"Якщо у ваших списках може бути більше типів даних або навіть містити списки в списках, вам знадобиться більш повна граматика" - будь ласка, дивіться посилання, яке я надав у своїй відповіді, для аналізу, який буде обробляти вкладені списки, та різних інших типів даних.
PaulMcG

Pyparsing більше не влаштовується на вікі-просторах. parsePythonValue.pyПриклад тепер на GitHub в github.com/pyparsing/pyparsing/blob/master/examples / ...
PaulMcG

1

Для подальшого завершення відповіді @Ryan за допомогою json, одна дуже зручна функція для перетворення unicode - це та розміщена тут: 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']

0

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

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

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

а з чистим python - не імпортує жодної бібліотеки

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

Не потрібна зовнішня бібліотека.


-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'>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.