Làm thế nào để chuyển đổi đại diện chuỗi của danh sách thành một danh sách?


531

Tôi đã tự hỏi cách đơn giản nhất là chuyển đổi một stringdanh sách như sau thành list:

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

Ngay cả trong trường hợp người dùng đặt khoảng trắng ở giữa dấu phẩy và khoảng trắng bên trong dấu ngoặc kép. Tôi cũng cần phải xử lý việc đó để:

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

trong Python.

Tôi biết tôi có thể loại bỏ khoảng trắng bằng strip()split()sử dụng toán tử phân tách và kiểm tra các bảng chữ cái không. Nhưng mã đã trở nên rất ít. Có một chức năng nhanh chóng mà tôi không biết?


4
Bạn thực sự đang cố gắng để đạt được điều gì? Có lẽ có một cách tốt hơn nhiều so với việc cố gắng chuyển đổi cú pháp danh sách Python thành một danh sách thực sự ...
Nicholas Knight

1
Bạn đang sử dụng phiên bản Python nào?
Mark Byers

2
@Nicholas Knight: Tôi đang cố gắng xử lý dữ liệu nhập của người dùng trong một ứng dụng cũ, nơi tất cả các danh sách được nhập dưới dạng danh sách unicode với dấu ngoặc vuông. @Mark Byers, tôi đang sử dụng python 2.6 nên cách tiếp cận ast.literal hoạt động tốt nhất
harijay

Câu trả lời:


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 :

Với ast.literal_eval, bạn có thể đánh giá một cách an toàn một nút biểu thức hoặc một chuỗi chứa biểu thức Python. Chuỗi hoặc nút được cung cấp chỉ có thể bao gồm các cấu trúc chữ Python sau: chuỗi, số, bộ dữ liệu, danh sách, ký tự, booleans và Không có.


6
Mỗi bình luận bên dưới, điều này rất nguy hiểm vì nó chỉ đơn giản là chạy bất cứ con trăn nào trong chuỗi. Vì vậy, nếu ai đó thực hiện một cuộc gọi để xóa tất cả mọi thứ trong đó, nó sẽ hạnh phúc.
Paul Kenjora

16
@PaulKenjora: Bạn đang nghĩ đến eval, không phải ast.literal_eval.
user2357112 hỗ trợ Monica

19
ast.literal_evalan toàn hơn so với eval, nhưng nó không thực sự an toàn . Như các phiên bản gần đây của tài liệu giải thích: "Cảnh báo Có thể đánh sập trình thông dịch Python với chuỗi đủ lớn / phức tạp do giới hạn độ sâu ngăn xếp trong trình biên dịch AST của Python." Trên thực tế, có thể chạy mã tùy ý thông qua một cuộc tấn công ngăn xếp cẩn thận, mặc dù theo như tôi biết thì không ai xây dựng một bằng chứng công khai về khái niệm đó.
abarnert

Chà nhưng phải làm gì nếu Danh sách không có dấu ngoặc kép? ví dụ: [4 của B, 1 của G]
sqp_125

84

Các jsonmô-đun là một giải pháp tốt hơn bất cứ khi nào có một chuyển đổi thành chuỗi danh sách các từ điển. Các json.loads(your_data)chức năng có thể được sử dụng để chuyển đổi nó thành một danh sách.

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

Tương tự

>>> x = u'[ "A","B","C" , {"D":"E"}]'
>>> json.loads(x)
[u'A', u'B', u'C', {u'D': u'E'}]

tuy nhiên tôi không muốn danh sách trả về ở định dạng unicode. nhưng có vẻ như ngay cả khi tôi loại bỏ u '' khỏi chuỗi, nó vẫn coi dữ liệu là unicode.
Mansoor Akram

7
Điều này hoạt động cho ints nhưng không phải cho chuỗi trong trường hợp của tôi bởi vì mỗi chuỗi được trích dẫn không trích dẫn đôi, thở dài.
Paul Kenjora

4
Theo nhận xét của @ PaulKenjora, nó hoạt động '["a","b"]'nhưng không phải cho "['a','b']".
Skippy le Grand Gourou

83

Điều evalnày rất nguy hiểm - bạn không nên thực hiện nhập liệu của người dùng.

Nếu bạn có 2.6 hoặc mới hơn, hãy sử dụng ast thay vì eval:

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

Một khi bạn có điều đó, stripcác chuỗi.

Nếu bạn đang dùng phiên bản Python cũ hơn, bạn có thể tiến gần đến những gì bạn muốn với một biểu thức chính quy đơn giản:

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

Điều này không tốt như giải pháp ast, ví dụ, nó không xử lý chính xác các trích dẫn đã thoát trong chuỗi. Nhưng nó đơn giản, không liên quan đến một eval nguy hiểm và có thể đủ tốt cho mục đích của bạn nếu bạn sử dụng Python cũ mà không có ast.


Bạn có thể vui lòng cho tôi biết lý do tại sao bạn nói evallà nguy hiểm không - bạn không nên thực thi đầu vào của người dùng. Tôi đang sử dụng 3.6
Aaryan Dewan

1
@AaryanDewan nếu bạn sử dụng evaltrực tiếp, nó sẽ đánh giá bất kỳ biểu hiện trăn hợp lệ nào, có khả năng gây nguy hiểm. literal_evalgiải quyết vấn đề này bằng cách chỉ đánh giá các cấu trúc theo nghĩa đen của Python: chuỗi, số, bộ dữ liệu, danh sách, ký hiệu, booleans và Không có.
Abhishek Menon

14
import ast
l = ast.literal_eval('[ "A","B","C" , " D"]')
l = [i.strip() for i in l]

10

Có một giải pháp nhanh chóng:

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

Các khoảng trắng không mong muốn trong các thành phần danh sách có thể được loại bỏ theo cách này:

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

điều này vẫn sẽ giữ khoảng trống bên trong dấu ngoặc kép
tosh

17
Đây là một lời mời mở để thực thi mã tùy ý, KHÔNG BAO GIỜ làm điều này hoặc bất cứ điều gì tương tự trừ khi bạn biết chắc chắn rằng đầu vào sẽ luôn được tin cậy 100%.
Nicholas Knight

1
Tôi có thể sử dụng đề xuất này vì tôi biết dữ liệu của mình luôn ở định dạng đó và là một công việc xử lý dữ liệu.
Manish Ranjan

9

Lấy cảm hứng từ một số câu trả lời ở trên hoạt động với các gói python cơ sở tôi đã so sánh hiệu suất của một số (sử dụng Python 3.7.3):

Cách 1: ast

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

Cách 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

Phương pháp 3: không nhập

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

Tôi đã thất vọng khi thấy những gì tôi coi là phương pháp có khả năng đọc kém nhất là phương pháp có hiệu suất tốt nhất ... có những sự đánh đổi để xem xét khi đi với tùy chọn dễ đọc nhất ... đối với loại khối lượng công việc tôi thường sử dụng python giá trị dễ đọc hơn một tùy chọn hiệu suất cao hơn một chút, nhưng như thường lệ, nó phụ thuộc.


9

Nếu nó chỉ là một danh sách một chiều, điều này có thể được thực hiện mà không cần nhập bất cứ điều gì:

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

8
Lưu ý thận trọng: điều này có thể nguy hiểm nếu bất kỳ chuỗi nào trong danh sách có dấu phẩy ở giữa.
Hassan Kamal

Điều này sẽ không hoạt động nếu danh sách chuỗi của bạn là một danh sách các danh sách
crypdick

@crypdick Điểm tốt, đã thêm một lưu ý về điều đó :)
ruohola

6

Giả sử rằng tất cả các đầu vào của bạn là danh sách và các dấu ngoặc kép trong đầu vào thực sự không quan trọng, điều này có thể được thực hiện với một thay thế regrec đơn giản. Nó là một chút perl-y nhưng hoạt động như một nét duyên dáng. Cũng lưu ý rằng đầu ra bây giờ là một danh sách các chuỗi unicode, bạn đã không xác định rằng bạn cần điều đó, nhưng dường như nó có ý nghĩa khi đưa vào đầu vào 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']

Biến rác chứa một biểu thức chính quy được biên dịch (cho tốc độ) của tất cả các ký tự mà chúng ta không muốn, sử dụng] làm ký tự yêu cầu một số thủ thuật dấu gạch chéo ngược. Re.sub thay thế tất cả các ký tự này bằng không và chúng tôi chia chuỗi kết quả tại dấu phẩy.

Lưu ý rằng điều này cũng loại bỏ khoảng trắng từ các mục bên trong u '["oh no"]' ---> [u'ohno ']. Nếu đây không phải là những gì bạn muốn, regrec cần phải được cải thiện một chút.


4

Nếu bạn biết rằng danh sách của bạn chỉ chứa các chuỗi được trích dẫn, ví dụ về pyparsing này sẽ cung cấp cho bạn danh sách các chuỗi bị tước (thậm chí giữ nguyên trạng thái Unicode gốc).

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

Nếu danh sách của bạn có thể có nhiều kiểu dữ liệu hơn hoặc thậm chí chứa danh sách trong danh sách, thì bạn sẽ cần một ngữ pháp hoàn chỉnh hơn - như thế này trên wiki pyparsing, sẽ xử lý các bộ dữ liệu, danh sách, ints, float và chuỗi được trích dẫn. Sẽ hoạt động với các phiên bản Python trở lại 2.4.


bạn có thể cho tôi biết cách sử dụng "parseString (). asList ()" không, nếu tôi có loại chuỗi này: '["A", "B", "C", ["D"]]', như bạn đã tuyên bố rằng pyparsing cũng có thể làm điều đó. nhưng dường như không tìm thấy cách đúng đắn để làm điều đó.
Mansoor Akram

"Nếu danh sách của bạn có thể có nhiều kiểu dữ liệu hơn hoặc thậm chí chứa danh sách trong danh sách, thì bạn sẽ cần một ngữ pháp hoàn chỉnh hơn" - vui lòng xem liên kết tôi cung cấp trong câu trả lời của tôi để biết một trình phân tích cú pháp sẽ xử lý các danh sách lồng nhau và nhiều loại dữ liệu khác.
PaulMcG

Pyparsing không còn được lưu trữ tại wikispaces. Các parsePythonValue.pyví dụ tại là trên GitHub tại github.com/pyparsing/pyparsing/blob/master/examples/...
PaulMcG

1

Để hoàn thành thêm câu trả lời của @Ryan bằng json, một chức năng rất thuận tiện để chuyển đổi unicode là chức năng được đăng ở đây: https://stackoverflow.com/a/13105359/7599285

ex với dấu ngoặc kép hoặc đơn:

>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

Tôi muốn cung cấp một giải pháp tạo khuôn trực quan hơn với regex. Hàm dưới đây lấy đầu vào là một danh sách được xâu chuỗi chứa các chuỗi tùy ý.

Giải thích từng bước: Bạn xóa tất cả các khoảng trắng, ngoặc vuông và value_separators (miễn là chúng không phải là một phần của các giá trị bạn muốn trích xuất, nếu không thì làm cho biểu thức chính quy phức tạp hơn). Sau đó, bạn tách chuỗi đã làm sạch trên dấu ngoặc đơn hoặc dấu ngoặc kép và lấy các giá trị không trống (hoặc các giá trị được lập chỉ mục lẻ, bất kể tùy chọn nào).

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

mẫu thử : "['21'," foo "'6', '0'," A "]"


0

và với python thuần - không nhập bất kỳ thư viện nào

[x for x in  x.split('[')[1].split(']')[0].split('"')[1:-1] if x not in[',',' , ',', ']]

0

Bạn có thể gặp phải vấn đề như vậy trong khi xử lý dữ liệu bị loại bỏ được lưu trữ dưới dạng Pandas DataFrame.

Giải pháp này hoạt động như sự quyến rũ nếu danh sách các giá trị được trình bày dưới dạng văn bản .

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

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

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

Không cần thư viện bên ngoài.


-1

Vì vậy, theo tất cả các câu trả lời tôi quyết định thời gian các phương pháp phổ biến nhất:

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

Vì vậy, cuối cùng regex chiến thắng!


-1

bạn có thể tự lưu .strip () fcn bằng cách cắt bỏ các ký tự đầu tiên và cuối cùng từ biểu diễn chuỗi của danh sách (xem dòng thứ ba bên dưới)

>>> 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'>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.