2010-12-04 34 views
8

aşağıdaki gibi hatların binlerce oluşturulmuş bir dosya var: CODE,XXX,DATE,20101201,TIME,070400,CONDITION_CODES,LTXT,PRICE,999.0000,QUANTITY,100,TSN,1510000001Metin satırından sözlük nasıl oluşturulur?

Bazı hatlar

daha fazla alan ve diğerleri daha az olması, ancak tüm anahtar-değer çiftleri aynı kalıbı takip ve her satır bir sahiptir TSN alanı.

dosya üzerinde bazı analizler yaparken, ben yazdım gibi bir döngü bir sözlüğüne dosyasını okumak için aşağıdaki: için bunu istiyorum tam olarak ne

#!/usr/bin/env python 

from sys import argv 

records = {} 
for line in open(argv[1]): 
    fields = line.strip().split(',') 
    record = dict(zip(fields[::2], fields[1::2])) 
    records[record['TSN']] = record 

print 'Found %d records in the file.' % len(records) 

... gayet iyi ve yapar (print sadece önemsiz bir örnektir).

Ancak, benim için özellikle "pythonic" hissetmez ve çizgi ile: (? O alanlar üzerinde yineleme yok kaç kez) sadece "aksak" hissediyor

dict(zip(fields[::2], fields[1::2])) 

.

Python 2.6'da bunu yapmak için standart modüller ile daha iyi bir yol var mı?

+0

Bunun olabildiğince pythonic olduğunu düşünüyorum. –

+0

Sadece TSN kayıtlarında ilgileniyor musunuz? Yoksa bunu tüm kayıt türlerine genişletmeyi mi düşünüyorsunuz? – marcog

cevap

18

olarak dict kayıtlarının değer çiftlerinin oluşturulmasını kolaylaştırmak için. Python 2 itertools belgelerinde benzer bir (ancak işlevsel olarak farklı) recipe dan pairwise() için bir fikrim var.

o izip() itertools — gelen ikincinin çıkarılması adresleri bu aşağıdaki örneğe çıkan Python 2'de yaptı ve her iki versiyonda da çalışması gerekir yapar beri sadece düz zip() kullanabilirsiniz, Python 3'te yaklaşımı kullanın. Dosya for döngü okuma bu gibi kullanılabilir

try: 
    from itertools import izip 
except ImportError: # Python 3 
    izip = zip 

def pairwise(iterable): 
    "s -> (s0,s1), (s2,s3), (s4, s5), ..." 
    a = iter(iterable) 
    return izip(a, a) 

:

from sys import argv 

records = {} 
for line in open(argv[1]): 
    fields = (field.strip() for field in line.split(',')) # generator expr 
    record = dict(pairwise(fields)) 
    records[record['TSN']] = record 

print('Found %d records in the file.' % len(records)) 

Ama bekleyin, dahası var!

Tekrar (sağ pairwise() aşağıda listelenmiştir) benzer adlı, ancak işlevsel olarak farklı itertools tarifi tekabül ben grouper() arayacağım genelleştirilmiş versiyonunu oluşturmak mümkündür:

def grouper(n, iterable): 
    "s -> (s0,s1,...sn-1), (sn,sn+1,...s2n-1), (s2n,s2n+1,...s3n-1), ..." 
    return izip(*[iter(iterable)]*n) 

olabilir senin for döngüde bu gibi kullanılabilir: elbette

record = dict(grouper(2, fields)) 

, bu gibi özel durumlar için, bu functools.partial() kullanımı kolay ve (hem Python 2 & 3 çalışacak) onunla benzer pairwise() işlevi oluşturun: alanları gerçekten çok büyük sayıda olmadığı sürece

import functools 
pairwise = functools.partial(grouper, 2) 

Postscript

, bunun yerine bir gerçek dizisi oluşturabilir satır öğelerinin çiftleri dışında (daha doğrusu hiçbir len() olan bir jeneratör ifade kullanarak yerine):

fields = tuple(field.strip() for field in line.split(',')) 

bir ile göre alabilir daha basit grouper() fonksiyonu:

try: 
    xrange 
except NameError: # Python 3 
    xrange = range 

def grouper(n, sequence): 
    for i in xrange(0, len(sequence), n): 
     yield sequence[i:i+n] 

pairwise = functools.partial(grouper, 2) 
+2

Çok teşekkürler. Sağlanan tüm cevaplar mükemmeldi, ama kodunuz 2.2 Gb dosyası üzerinde çalışırken (itertools versiyonundan bile daha hızlı) ve okunması kolay ve ünite testi için en hızlı olanıydı. Itertools'a bakmayı düşünmediğim için kendimi tekmeliyorum, orada çok güzel şeyler var. – Johnsyweb

+2

@Johnsyweb: Performans hakkında harika haberler. Ben bununla gurur duyuyorum ve nihayet, günlük Python kodumda sıkça ihtiyaç duyduğum bir şey olduğu için bunu yapmak için oldukça zarif bir yol belirledim. – martineau

6

Değil çok daha iyi sen itertools modülde izip kullanabilir ve jeneratörün sihirli kendi işlevi yazmak nesneleri Python 2'de sadece more efficient...

Full explanation

+0

Vay, bu harika. – Kabie

+4

Buradaki hile, aynı nesnenin her iki parametrede "zip" için geçtiğinden emin olmak için liste çarpımını ve '* args' dereferencing 'işlevini kullanmakta, böylece yineleyici durumu her seferinde iki kez paylaşılmakta ve gelişmiş olmaktadır. çıkış tuple. Bunu birkaç başka yoldan yapabiliriz: x = iter (l); zip (x, x) 'belki daha okunabilir; '' (lambda x: zip (x, x)) (iter (l)) 'belki de işlevsel-programlama milletine daha fazla aşinadır, ancak bu şekilde neredeyse biz yan etkisiz programlama yaptığımızı sanki tamamen bir bağımlı;) –

+0

@Karl Knechtel: yerine (lambda x: zip (x, x)) (iter (x)) 'biri kullanılabilir (lambda x = iter (x): zip (x, x)))() [hala] farklı bir yan etkiye bağlı olmasına rağmen, biraz daha fazla okunabilir. – martineau

2
import itertools 

def grouper(n, iterable, fillvalue=None): 
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" 
    args = [iter(iterable)] * n 
    return itertools.izip_longest(fillvalue=fillvalue, *args) 

record = dict(grouper(2, line.strip().split(",")) 

source

+0

Maalesef bunun için bir oy kullanmam için çok geç oldu, çünkü bunun [yemek tarifleri] 'nin (http://docs.python.org/librar/itertools.html?highlight=grouper "itertools" belgelerindeki #recipes) veya referans veya alıntı verilmediğinden dolayı intihal olarak adlandırmam gerekecek. – martineau

+3

@martineau: Onun altında "kaynak" etiketli küçük bir küçük bağlantı var. –

+0

@Ignacio Vazquez-Abrams: Oh ... apaçık bir şekilde bunu özledim - üzgünüm @robert - yine de bir oyu hak ettiğini düşünmüyor. – martineau

1

biz yine bir fonksiyonu haline soyut gidiyoruz, o "sıfırdan" yazmak çok zor değil:

def pairs(iterable): 
    iterator = iter(iterable) 
    while True: 
     try: yield (iterator.next(), iterator.next()) 
     except: return 

Robert'ın tarifi versiyonu kesinlikle esneklik için puan kazanır olsa da.

+0

FWIW, "robert's tarifi" değil, onun [answer] (http://stackoverflow.com/questions/4356329/creating-a-python-dictionary-from-a-line-of-text/4356368 # 4356368). – martineau

İlgili konular