Listenin dize olarak temsilini bir listeye nasıl dönüştürürüm?


531

stringAşağıdaki gibi bir liste dönüştürmek için en basit yolu ne olduğunu merak ediyordum list:

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

Kullanıcının virgül arasına boşluk koyması ve tırnak içine boşluk bırakması durumunda bile. Bunu da ele almam gerekiyor:

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

Python'da.

Split operatörü kullanarak strip()ve split()kullanarak boşlukları soyup alfabetik olmayanları kontrol edebileceğimi biliyorum . Ama kod çok yavaş oluyordu. Farkında olmadığım hızlı bir işlev var mı?


4
Aslında neyi başarmaya çalışıyorsunuz? Muhtemelen Python listesi sözdizimini gerçek bir listeye dönüştürmeye çalışmaktan çok daha iyi bir yol var ...
Nicholas Knight

1
Hangi Python sürümünü kullanıyorsunuz?
Mark Byers

2
@Nicholas Knight: Tüm listelerin kare parantezli unicode listeler olarak girildiği eski bir uygulamada kullanıcı girişini işlemeye çalışıyorum. @Mark Byers, python 2.6 kullanıyorum, bu yüzden ast.literal yaklaşım en iyi
sonucu veriyor

Yanıtlar:


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 ile bir ifade düğümünü veya Python ifadesi içeren bir dizeyi güvenle değerlendirebilirsiniz. Sağlanan dize veya düğüm yalnızca şu Python değişmez yapılarından oluşabilir: dizeler, sayılar, tuples, listeler, dikteler, booleans ve Yok.


6
Aşağıdaki yorum başına, bu tehlikelidir, çünkü dizedeki python'u çalıştırır. Birisi oradaki her şeyi silmek için bir çağrı yaparsa, mutlu olur.
Paul Kenjora

16
@PaulKenjora: Sen düşünüyorsun eval, değil ast.literal_eval.
user2357112 Monica

19
ast.literal_evalolduğunu daha güvenli daha eval, ama aslında değil güvenli . Gibi dokümanlar son sürümlerini açıklamaktadır: "nedeniyle Python'un AST derleyici yığın derinliği sınırlamaları yeterince büyük / karmaşık dize ile Python yorumlayıcısı çökmesine mümkündür Uyarı." Aslında, kimsenin bunun için kamuya açık bir kavram kanıtı oluşturmadığını bildiğim kadarıyla, dikkatli bir yığın smashing saldırısı yoluyla keyfi kod çalıştırmak mümkün olabilir.
abarnert

Peki ama Listede tırnak yoksa ne yapmalı? örneğin [B'nin 4'ü, G'nin
1'i

84

jsonBir olduğunda modül daha iyi bir çözümdür dizgelenmiş sözlükler listesi. json.loads(your_data)Fonksiyon listesi dönüştürmek için kullanılabilir.

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

benzer şekilde

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

Ancak unicode formatında iade listesi istemiyorum. ancak u '' dizeden kaldırsam bile veriyi unicode olarak ele alır.
Mansur Akram

7
Bu ints için çalışır ama benim durumumda dizeler için değil çünkü her dize tek tırnaklı çift tırnaklı değil, iç çeker.
Paul Kenjora

4
@ PaulKenjora'nın yorumuna göre, işe yarıyor '["a","b"]'ama işe yaramıyor "['a','b']".
Skippy le Grand Gourou

83

evalTehlikelidir - Eğer kullanıcı girişi yürütmek olmamalıdır.

2.6 veya daha yeni bir sürümünüz varsa, eval yerine ast kullanın:

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

Bunu yaptıktan sonra strip, dizeler.

Python'un daha eski bir sürümündeyseniz, basit bir normal ifade ile istediğiniz şeye çok yakın olabilirsiniz:

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

Bu ast çözümü kadar iyi değildir, örneğin dizelerde kaçan tırnakları doğru işlemez. Ancak basittir, tehlikeli bir değerlendirme içermez ve astsız eski bir Python'daysanız, amacınız için yeterince iyi olabilir.


Bana neden “ evalTehlikeli - kullanıcı girdisini yürütmemelisin ” dediğini söyleyebilir misin ? 3.6
Aaryan Dewan

1
@AaryanDewan evaldoğrudan kullanırsanız , potansiyel olarak tehlikeli olan geçerli herhangi bir python ifadesini değerlendirecektir. literal_evalBu sorunu yalnızca Python değişmez yapılarını değerlendirerek çözer: dizeler, sayılar, tuples, listeler, dikteler, booleans ve Yok.
Abhishek Menon

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

10

Hızlı bir çözüm var:

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

Liste öğelerindeki istenmeyen boşluklar bu şekilde kaldırılabilir:

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

bu hala tırnak içindeki boşlukları koruyacak
Tosh

17
Bu, rastgele kod yürütme için açık bir davettir, girişin her zaman% 100 güvenilir olacağından kesinlikle emin değilseniz ASLA bunu veya bunun gibi bir şey yapmayın.
Nicholas Knight

1
Bu öneriyi kullanabilirim çünkü verilerimin her zaman bu biçimde olacağını ve bir veri işleme çalışması olacağını biliyordum.
Manish Ranjan

9

Temel python paketleriyle çalışan yukarıdaki cevaplardan bazılarından esinlenerek birkaçının performansını karşılaştırdım (Python 3.7.3 kullanarak):

Yöntem 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

Yöntem 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

Yöntem 3: İçe Aktarma Yok

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

En kötü okunabilirliğe sahip yöntemi düşündüğümü görmek hayal kırıklığına uğradım ... biraz daha performanslı bir seçenek üzerinden okunabilirlik sağlar, ancak her zamanki gibi değişir.


9

Sadece tek boyutlu bir liste ise, bu hiçbir şey içe aktarılmadan yapılabilir:

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

8
Dikkat notu: Bu, listedeki dizelerden herhangi birinin arasında virgül bulunması durumunda tehlikeli olabilir.
Hassan Kamal

Dize listeniz bir liste listesiyse bu
çalışmaz

@crypdick İyi bir nokta, bu konuda bir not ekledi :)
ruohola

6

Tüm girişlerinizin liste olduğunu ve girişteki çift tırnakların gerçekten önemli olmadığını varsayarsak, bu basit bir normal ifade ile yapılabilir. Biraz perl-y ama bir cazibe gibi çalışır. Çıktının artık unicode dizelerinin bir listesi olduğunu unutmayın, buna ihtiyacınız olduğunu belirtmediniz, ancak unicode girişi verildiğinde mantıklı görünüyor.

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

Önemsiz değişken, istemediğimiz tüm karakterlerin derlenmiş bir düzenli ifadesini (hız için) içerir, bir karakter olarak] 'i kullanmak için bazı ters eğik hile gerektirir. Re.sub, tüm bu karakterleri hiçbir şeyle değiştirmez ve ortaya çıkan dizeyi virgülle böleriz.

Bunun, u '["oh no"]' ---> [u'ohno '] girişlerindeki boşlukları da kaldırdığını unutmayın. İstediğiniz bu değilse, normal ifadenin biraz çorbalanması gerekir.


4

Listelerinizin yalnızca tırnak içine alınmış dizeler içerdiğini biliyorsanız, bu kopyalama örneği size soyulmuş dizelerin listesini verir (hatta orijinal Unicode-ness'i koruyarak).

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

Listeleriniz daha fazla veri türüne sahip olabilirse veya listeler içinde listeler içeriyorsa, daha eksiksiz bir dilbilgisine ihtiyacınız olacaktır - bu , pyparsing wiki'de olduğu gibi, tuples, listeler, ints, float ve alıntılanan dizeleri işleyecektir. 2.4'e kadar Python sürümleriyle çalışacaktır.


Bu tür bir dizeye sahipsem "parseString (). asList ()", nasıl kullanılacağını bana bildirir misiniz: '["A", "B", "C", ["D"]]' pyparsing'in bunu da yapabileceğini belirtmişlerdir. ama bunu yapmak için doğru yolu bulamamış gibi görünüyor.
Mansur Akram

"Listeleriniz daha fazla veri türüne sahipse veya listelerin içinde listeler içeriyorsa, daha eksiksiz bir gramer gerekir" - iç içe listeler ve diğer çeşitli veri türlerini işleyecek bir ayrıştırıcı için cevabımda verdiğim bağlantıya bakın.
PaulMcG

Pyparsing artık wikispaces'ta barındırılmıyor. parsePythonValue.pyÖrnek olarak GitHub'dan şimdi ise github.com/pyparsing/pyparsing/blob/master/examples/...
PaulMcG

1

@Ryan'ın cevabını json kullanarak tamamlamak için, unicode'u dönüştürmek için çok kullanışlı bir işlev, burada yayınlanan yanıttır: https://stackoverflow.com/a/13105359/7599285

örneğin çift veya tek tırnaklı:

>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

Normal ifade ile daha sezgisel bir desenlendirme çözümü sunmak istiyorum. Aşağıdaki işlev girdi olarak rasgele dizeler içeren dizge haline getirilmiş bir listeyi alır.

Kademeli açıklama: Tüm boşluk, basamaklama ve değer_ayırıcılarını kaldırırsınız (ayıklamak istediğiniz değerlerin bir parçası olmadıkları takdirde, normal ifadeyi daha karmaşık hale getirir). Ardından, temizlenen dizeyi tek veya çift tırnaklara ayırır ve boş olmayan değerleri (veya tercih ne olursa olsun tek endeksli değerleri) alırsınız.

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

testample : "['21'," foo "'6', '0'," A "]"


0

ve saf python ile - herhangi bir kütüphaneyi içe aktarmamak

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

0

Pandas DataFrame olarak depolanmış kazınmış verilerle uğraşırken bu tür bir sorunla karşılaşabilirsiniz.

Değerler listesi metin olarak mevcutsa bu çözüm cazibe gibi çalışır .

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

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

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

Harici kütüphane gerekmez.


-1

Bu yüzden, tüm cevapları takip ederek en yaygın yöntemleri zamanlamaya karar verdim:

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

Sonuçta normal ifade kazanır!


-1

sadece listenin dize gösteriminin ilk ve son karakterlerini dilimleyerek .strip () fcn dosyasını kaydedebilirsiniz (aşağıdaki üçüncü satıra bakın)

>>> 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'>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.